Skip to content

Commit c5515b8

Browse files
author
Dave Newman
committed
Sync chat with recorded videos
1 parent fc15262 commit c5515b8

File tree

10 files changed

+77
-27
lines changed

10 files changed

+77
-27
lines changed

app/assets/javascripts/components/Chat.es6.jsx

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
let messageId = 1
22

3+
function pollUntil(condition, action, interval=100) {
4+
if (!condition()) {
5+
return setTimeout(() => pollUntil(condition, action, interval), interval)
6+
}
7+
8+
action()
9+
}
10+
11+
312
class Chat extends React.Component {
413
constructor(props) {
514
super(props)
@@ -26,7 +35,13 @@ class Chat extends React.Component {
2635
}
2736

2837
renderComments() {
29-
return this.state.comments.map(c =>
38+
let visibleComments = this.state.comments
39+
const start = this.props.stream.recording_started_at
40+
if (start) {
41+
const current = start + this.state.timeOffset
42+
visibleComments = this.state.comments.filter(c => c.created_at < current)
43+
}
44+
return visibleComments.map(c =>
3045
<ChatComment
3146
key={c.id}
3247
id={c.id}
@@ -37,7 +52,11 @@ class Chat extends React.Component {
3752
}
3853

3954
renderChatInput() {
40-
if (this.props.active && this.pusher) {
55+
const allowChat = this.props.signedIn &&
56+
this.state.channel &&
57+
this.props.stream.archived_at === null
58+
59+
if (allowChat) {
4160
return (
4261
<form onSubmit={this.handleSubmit.bind(this)}>
4362
<input type="text" ref="body" defaultValue="" placeholder="Ask question" className="col-9 focus-no-border font-sm resize-chat-on-change m0" style={{"border": "none", "outline": "none"}} />
@@ -71,7 +90,7 @@ class Chat extends React.Component {
7190
method: 'POST',
7291
dataType: 'json',
7392
data: {
74-
socket_id: this.pusher.connection.socket_id,
93+
socket_id: this.state.pusher.connection.socket_id,
7594
comment: {
7695
article_id: this.props.stream.id,
7796
body: this.refs.body.value,
@@ -123,15 +142,21 @@ class Chat extends React.Component {
123142
}
124143

125144
componentWillMount() {
126-
this.pusher = new Pusher(this.props.pusherKey)
127-
this.channel = this.pusher.subscribe(this.props.chatChannel)
145+
pollUntil(
146+
() => typeof Pusher !== 'undefined',
147+
() => {
148+
const pusher = new Pusher(this.props.pusherKey)
149+
const channel = pusher.subscribe(this.props.chatChannel)
150+
channel.bind('new-comment', comment => {
151+
this.setState({comments: [...this.state.comments, comment]})
152+
})
153+
154+
this.setState({pusher, channel})
155+
}
156+
)
128157
}
129158

130159
componentDidMount() {
131-
this.channel.bind('new-comment', comment => {
132-
this.setState({comments: [...this.state.comments, comment]})
133-
})
134-
135160
const self = this
136161
$(this.refs.scrollable).bind('mousewheel DOMMouseScroll', function(e) {
137162
if (this.scrollTop < 100) {
@@ -146,6 +171,7 @@ class Chat extends React.Component {
146171
this.scrollToBottom()
147172
this.fetchOlderChatMessages()
148173
$(window).on('video-resize', this.constrainChatToStream)
174+
$(window).on('video-time', (e, data) => this.setState({ timeOffset: data.position }))
149175
}
150176

151177
componentWillUnmount() {
@@ -184,8 +210,7 @@ class Chat extends React.Component {
184210
Chat.propTypes = {
185211
chatChannel: React.PropTypes.string.isRequired,
186212
comments: React.PropTypes.array.isRequired,
187-
isLive: React.PropTypes.bool,
188213
pusherKey: React.PropTypes.string.isRequired,
189-
active: React.PropTypes.bool,
214+
signedIn: React.PropTypes.bool,
190215
stream: React.PropTypes.object.isRequired,
191216
}

app/assets/javascripts/components/Video.es6.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Video extends React.Component {
2727
}).on('play', () => this.setState({online: true}))
2828
.on('bufferFull', () => this.setState({online: true}))
2929
.on('resize', data => $(window).trigger('video-resize', data))
30+
.on('time', data => $(window).trigger('video-time', data))
3031
.onError(this.onError.bind(this))
3132

3233
// debug
@@ -80,9 +81,9 @@ class Video extends React.Component {
8081
}
8182

8283
onAll(e, data) {
83-
if (e !== 'time' && e !== 'meta') {
84+
// if (e !== 'time' && e !== 'meta') {
8485
console.log(e, data)
85-
}
86+
// }
8687
}
8788
}
8889

app/controllers/quickstream_controller.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ class QuickstreamController < ApplicationController
22
skip_before_action :verify_authenticity_token
33

44
def webhook
5-
@user = User.find_by!(stream_key: params[:token])
5+
case params[:type].to_sym
6+
when :auth
7+
@user = User.find_by!(stream_key: params[:token])
8+
when :youtube_live
9+
puts params[:broadcast]
10+
broadcast_id = params[:broadcast]['id']
11+
@stream = Stream.joins(:user).find_by!('users.username' => params[:streamer], :recording_id => broadcast_id)
12+
@stream.update!(recording_started_at: Time.parse(params[:broadcast]['snippet']['actual_start_time']))
13+
end
614
render nothing: true, status: :ok
715
end
816
end

app/controllers/streams_controller.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,13 @@ def save_and_redirect
9898
if @stream.save
9999
case
100100
when @stream.archived?
101+
@stream.touch(:archived_at)
101102
end_youtube_stream
102103
flash[:notice] = "You are offline and your broadcast was archived"
103104
redirect_to new_stream_path
104105
when @stream.published?
105106
Rails.logger.info("pushing to youtube")
106-
stream_to_youtube if @stream.save_recording
107+
stream_to_youtube
107108
redirect_to profile_stream_path(current_user.username)
108109
else
109110
redirect_to new_stream_path

app/models/comment.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ class Comment < ActiveRecord::Base
33
paginates_per 10
44
html_schema_type :Comment
55

6+
VIDEO_LAG = 25.seconds # TODO: measure the real lag value
7+
68
after_create :auto_like_article_for_author
79

810
belongs_to :user, touch: true, required: true
@@ -20,4 +22,8 @@ def dom_id
2022
def auto_like_article_for_author
2123
article.likes.create(user: user) unless user.likes?(article)
2224
end
25+
26+
def video_timestamp
27+
(created_at - VIDEO_LAG).to_i
28+
end
2329
end

app/models/stream.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def preview_image_url
5151
end
5252

5353
def source
54-
if recording_id
54+
if archived?
5555
"//www.youtube.com/watch?v=#{recording_id}"
5656
else
5757
user.stream_source
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
json.extract! comment, :id, :created_at
1+
json.extract! comment, :id
2+
json.created_at comment.video_timestamp
23
json.authorUrl user_path(comment.user)
34
json.authorUsername comment.user.username
45
json.markup sanitize(CoderwallFlavoredMarkdown.render_to_html(comment.body))

app/views/streams/show.json.jbuilder

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ if current_user
22
json.authorUrl user_path(current_user)
33
json.authorUsername current_user.username
44
end
5-
json.pusherKey ENV['PUSHER_KEY']
65
json.chatChannel @stream.dom_id
6+
json.pusherKey ENV['PUSHER_KEY']
7+
json.signedIn !!current_user
78

89
json.stream do
9-
json.extract! @stream, :id, :active
10+
json.extract! @stream, :id, :archived_at
11+
json.recording_started_at @stream.recording_started_at.try(:to_i)
1012
end
1113

1214
json.comments @comments, partial: 'comments/comment', as: :comment
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddRecordingStartedAtToProtips < ActiveRecord::Migration
2+
def change
3+
add_column :protips, :recording_started_at, :datetime
4+
end
5+
end

db/schema.rb

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#
1212
# It's strongly recommended that you check this file into your version control system.
1313

14-
ActiveRecord::Schema.define(version: 20160607202132) do
14+
ActiveRecord::Schema.define(version: 20160608034824) do
1515

1616
# These are extensions that must be enabled in order to support this database
1717
enable_extension "plpgsql"
@@ -97,17 +97,18 @@
9797
t.integer "user_id"
9898
t.float "score"
9999
t.datetime "featured_at"
100-
t.datetime "created_at", null: false
101-
t.datetime "updated_at", null: false
102-
t.string "tags", default: [], array: true
103-
t.integer "likes_count", default: 0
104-
t.integer "views_count", default: 0
105-
t.boolean "flagged", default: false
106-
t.text "type", null: false
100+
t.datetime "created_at", null: false
101+
t.datetime "updated_at", null: false
102+
t.string "tags", default: [], array: true
103+
t.integer "likes_count", default: 0
104+
t.integer "views_count", default: 0
105+
t.boolean "flagged", default: false
106+
t.text "type", null: false
107107
t.datetime "published_at"
108108
t.datetime "archived_at"
109109
t.boolean "save_recording"
110110
t.text "recording_id"
111+
t.datetime "recording_started_at"
111112
end
112113

113114
add_index "protips", ["created_at"], name: "index_protips_on_created_at", using: :btree

0 commit comments

Comments
 (0)