talking_stick 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Changes.md +5 -0
  4. data/Guardfile +44 -0
  5. data/README.md +59 -24
  6. data/app/assets/images/talking_stick/line-spinner.svg +1 -0
  7. data/app/assets/images/talking_stick/loading.gif +0 -0
  8. data/app/assets/javascripts/talking_stick/talking_stick.js.erb +272 -0
  9. data/app/assets/javascripts/talking_stick/talking_stick/partner.js +89 -28
  10. data/app/assets/javascripts/talking_stick/talking_stick/rails_signaling.js +24 -6
  11. data/app/assets/stylesheets/talking_stick/application.css +55 -0
  12. data/app/controllers/talking_stick/application_controller.rb +6 -0
  13. data/app/controllers/talking_stick/participants_controller.rb +1 -1
  14. data/app/controllers/talking_stick/rooms_controller.rb +4 -1
  15. data/app/models/talking_stick/participant.rb +2 -1
  16. data/app/models/talking_stick/room.rb +17 -0
  17. data/app/models/talking_stick/signal.rb +2 -0
  18. data/app/views/talking_stick/rooms/_form.html.erb +12 -9
  19. data/app/views/talking_stick/rooms/edit.html.erb +5 -4
  20. data/app/views/talking_stick/rooms/index.html.erb +29 -22
  21. data/app/views/talking_stick/rooms/new.html.erb +5 -3
  22. data/app/views/talking_stick/rooms/show.html.erb +92 -19
  23. data/config/locales/en.yml +15 -0
  24. data/config/locales/it.yml +14 -0
  25. data/db/migrate/20150722200822_add_slug_to_talking_stick_rooms.rb +11 -0
  26. data/lib/generators/talking_stick/views/USAGE +18 -0
  27. data/lib/generators/talking_stick/views/views_generator.rb +18 -0
  28. data/lib/talking_stick/version.rb +1 -1
  29. data/spec/dummy/app/assets/javascripts/application.js +1 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css +1 -0
  31. data/spec/models/talking_stick/participant_spec.rb +1 -1
  32. data/spec/models/talking_stick/room_spec.rb +32 -2
  33. data/talking_stick.gemspec +2 -0
  34. metadata +42 -5
  35. data/app/assets/javascripts/talking_stick/talking_stick.js +0 -141
  36. data/app/views/layouts/talking_stick/application.html.erb +0 -14
@@ -1,5 +1,7 @@
1
- <h1>New Room</h1>
1
+ <div class="container">
2
2
 
3
- <%= render 'form' %>
3
+ <h1>New Room</h1>
4
4
 
5
- <%= link_to 'Back', rooms_path %>
5
+ <%= render 'form' %>
6
+
7
+ </div>
@@ -1,22 +1,48 @@
1
- <p id="notice"><%= notice %></p>
1
+ <% content_for(:title) { "#{@room.name} Group Conversation" } %>
2
+ <div class="container-fluid" role="main" id="room" data-room-name="<%= @room.name %>">
3
+ <div class="row">
4
+ <div class="col-xs-3">
5
+ <div class="row">
6
+ <div class="col-xs-12">
7
+ <div id="localvideo-container"></div>
8
+ </div>
9
+ <div class="col-xs-12">
10
+ <span class="navbar-brand"><%= @room.name %></span>
11
+ </div>
12
+ </div>
13
+ </div>
14
+ <div class="col-xs-9">
15
+ <div id="partnervideos"></div>
16
+ </div>
17
+ </div>
18
+ </div>
2
19
 
3
- <p>
4
- <strong>Name:</strong>
5
- <%= @room.name %>
6
- </p>
7
-
8
- <p>
9
- <button id="connect">Connect to room</button>
10
- </p>
11
-
12
- <video id="localvideo"></video>
13
- <div id="partnerVideos"></div>
14
-
15
- <%= link_to 'Edit', edit_room_path(@room) %> |
16
- <%= link_to 'Back', rooms_path %>
20
+ <div class="modal fade" role="dialog" id="video-preview">
21
+ <div class="modal-dialog">
22
+ <div class="modal-content">
23
+ <div class="modal-header">
24
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
25
+ <h3 class="modal-title"><%= t(:joining_room) %> "<%= @room.name %>"</h3>
26
+ </div>
27
+ <div class="modal-body">
28
+ <video id="localvideo" poster="<%= image_path 'talking_stick/line-spinner.svg' %>" autoplay=true></video>
29
+ <p id="preview_instructions"><%= t(:video_grant_access) %></p>
30
+ </div>
31
+ <div class="modal-footer">
32
+ <button id="connect" type="button" class="btn btn-default btn-primary" data-dismiss="modal" disabled>Join</button>
33
+ <%= link_to "Exit", :back, class: 'btn btn-default' %>
34
+ </div>
35
+ </div><!-- /.modal-content -->
36
+ </div><!-- /.modal-dialog -->
37
+ </div><!-- /.modal -->
17
38
 
18
39
  <script type="text/javascript">
19
40
  $(document).ready(function() {
41
+ $('#sim').click(sim);
42
+ // Attempt to keep the modal from scrolling vertically
43
+ // 300px is the approximate height of the modal, minus the video
44
+ $('#video-preview').find('video').height($(window).height() - 300);
45
+ $('#video-preview').modal({backdrop: 'static', keyboard: false, show: true});
20
46
  var myGuid = TalkingStick.generateGUID();
21
47
  // Use the least-common-denominator, Rails API, for WebRTC setup
22
48
  var options = {
@@ -27,14 +53,61 @@ $(document).ready(function() {
27
53
 
28
54
  var options = {
29
55
  guid: myGuid,
30
- localVideo: $('#localvideo'),
31
56
  roomUrl: '<%= room_path %>',
32
57
  signalingEngine: signalingEngine,
33
- logLevel: 'debug',
58
+ logLevel: 'trace',
34
59
  };
35
60
  TalkingStick.init(options);
36
61
 
37
- $('#connect').click(TalkingStick.connect);
38
-
62
+ $('#connect').click(startSession);
63
+ $(window).on('talking_stick.local_media_setup', function() {
64
+ $('#preview_instructions').html('<%= t(:video_intro) %>');
65
+ $('form#connect input[name="join"]').attr('disabled', false);
66
+ });
39
67
  });
68
+
69
+ function startSession() {
70
+ $('#localvideo').detach().appendTo('#localvideo-container');
71
+ $('#localvideo').height('auto');
72
+ // Gotta restart and re-mute the video after moving it
73
+ $('#localvideo')[0].play();
74
+ TalkingStick.connect();
75
+ };
76
+
77
+ function resizeVideos() {
78
+ var videoCollection = $('#partnervideos video');
79
+ var numVideos = videoCollection.length;
80
+ var square = Math.sqrt(numVideos);
81
+ if (!isInt(square)) {
82
+ // Round up to the next square
83
+ square = parseInt(square) + 1;
84
+ }
85
+ console.log(square);
86
+ videoCollection.css('width', (100/square) + "%");
87
+ };
88
+
89
+ function cleanupPartner() {
90
+ resizeVideos();
91
+ }
92
+
93
+ function sim() {
94
+ var el = $(document.createElement('video'));
95
+ el.attr('poster', "<%= image_path 'talking_stick/line-spinner.svg' %>");
96
+ $('#partnervideos').append(el);
97
+ TalkingStick.localVideo = $('#localvideo');
98
+ TalkingStick.trigger('partner.media', el);
99
+ }
100
+
101
+ function isInt(value){
102
+ if((parseFloat(value) == parseInt(value)) && !isNaN(value)){
103
+ return true;
104
+ } else {
105
+ return false;
106
+ }
107
+ }
108
+
109
+ $(window).on('talking_stick.partner.media', resizeVideos);
110
+ $(window).on('talking_stick.partner.cleanup', cleanupPartner);
111
+ $(window).resize(resizeVideos);
112
+
40
113
  </script>
@@ -0,0 +1,15 @@
1
+ en:
2
+ camera: camera
3
+ destroy: Destroy
4
+ edit: Edit
5
+ join: Join
6
+ last_used: Last Used
7
+ last_used_never: Never
8
+ listing_rooms: Listing Rooms
9
+ microphone: microphone
10
+ name: Name
11
+ new_room: New Room
12
+ show: Show
13
+ your_name: Your name
14
+ video_grant_access: To join this conversation you'll need to grant access to the camera and microphone.
15
+ video_intro: Put on pants (optional), check your hair (required), and press "Join" when you're ready to start!
@@ -0,0 +1,14 @@
1
+ it:
2
+ camera: fotocamera
3
+ destroy: Distruggere
4
+ edit: Modifica
5
+ join: Unirsi
6
+ last_used: Ultimo Uso
7
+ listing_rooms: Lista Camere
8
+ microphone: microfono
9
+ name: Nome
10
+ new_room: Creo Camere
11
+ show: Mostrare
12
+ your_name: Il tuo nome
13
+ video_grant_access: Per partecipare a questa conversazione è necessario concedere l'accesso alla videocamera e al microfono.
14
+ video_intro: Indossare pantaloni (opzionale), controlla i tuoi capelli (richiesto), e premere "Unirsi" quando si è pronti per iniziare!
@@ -0,0 +1,11 @@
1
+ class AddSlugToTalkingStickRooms < ActiveRecord::Migration
2
+ def up
3
+ add_column :talking_stick_rooms, :slug, :string
4
+ # Add slugs to the existing rooms
5
+ TalkingStick::Room.all.map &:save
6
+ end
7
+
8
+ def down
9
+ remove_column :talking_stick_rooms, :slug
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ Description:
2
+ Creates default participant and room views available for customization
3
+
4
+ Run:
5
+ rails generate talking_stick:views
6
+
7
+ This will create the following default views:
8
+ app/views/talking_stick/participants/_form.html.erb
9
+ app/views/talking_stick/participants/edit.html.erb
10
+ app/views/talking_stick/participants/index.html.erb
11
+ app/views/talking_stick/participants/new.html.erb
12
+ app/views/talking_stick/participants/show.html.erb
13
+
14
+ app/views/talking_stick/rooms/_form.html.erb
15
+ app/views/talking_stick/rooms/edit.html.erb
16
+ app/views/talking_stick/rooms/index.html.erb
17
+ app/views/talking_stick/rooms/new.html.erb
18
+ app/views/talking_stick/rooms/show.html.erb
@@ -0,0 +1,18 @@
1
+ class TalkingStick::ViewsGenerator < Rails::Generators::Base
2
+ source_root File.expand_path('../../../../../app/views/talking_stick', __FILE__)
3
+
4
+ def generate_participant_views
5
+ directory "participants", "#{paste_path}/participants"
6
+ end
7
+
8
+ def generate_room_views
9
+ directory "rooms", "#{paste_path}/rooms"
10
+ end
11
+
12
+ private
13
+
14
+ def paste_path
15
+ 'app/views/talking_stick'
16
+ end
17
+
18
+ end
@@ -1,3 +1,3 @@
1
1
  module TalkingStick
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -11,3 +11,4 @@
11
11
  // about supported directives.
12
12
  //
13
13
  //= require_tree .
14
+ //= require talking_stick/application.js
@@ -12,4 +12,5 @@
12
12
  *
13
13
  *= require_tree .
14
14
  *= require_self
15
+ *= require talking_stick/application
15
16
  */
@@ -1,4 +1,4 @@
1
- require 'rails_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  module TalkingStick
4
4
  RSpec.describe Participant, type: :model do
@@ -1,7 +1,37 @@
1
- require 'rails_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  module TalkingStick
4
4
  RSpec.describe Room, type: :model do
5
- pending "add some examples to (or delete) #{__FILE__}"
5
+ describe "#validation" do
6
+ subject { described_class.new(name: "Test") }
7
+
8
+ it "automatically sluggifys the room name" do
9
+ expect {
10
+ subject.valid?
11
+ }.to change{ subject.slug }.from(nil).to("test")
12
+ end
13
+
14
+ it "allows the slug to be set by manually" do
15
+ subject.slug = "dont_touch"
16
+ expect {
17
+ subject.valid?
18
+ }.to_not change{ subject.slug }.from("dont_touch")
19
+ end
20
+ end
21
+
22
+ describe ".find_or_create" do
23
+ it "uses a room if it already exists" do
24
+ existing = described_class.create(name: "Test")
25
+
26
+ expect(described_class.find_or_create(slug: "test")).to eq existing
27
+ expect(described_class.count).to eq 1
28
+ end
29
+
30
+ it "creates a room if the slug doesn't exist" do
31
+ expect {
32
+ described_class.find_or_create(slug: "Test")
33
+ }.to change(described_class, :count).by(1)
34
+ end
35
+ end
6
36
  end
7
37
  end
@@ -21,6 +21,8 @@ Gem::Specification.new do |s|
21
21
  s.add_dependency 'jquery-rails', '~> 4.0'
22
22
 
23
23
  s.add_development_dependency 'sqlite3'
24
+ s.add_development_dependency 'guard'
25
+ s.add_development_dependency 'guard-rspec'
24
26
  s.add_development_dependency 'rspec-rails'
25
27
  s.add_development_dependency 'capybara'
26
28
  s.add_development_dependency 'factory_girl_rails'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: talking_stick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Klang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-17 00:00:00.000000000 Z
11
+ date: 2016-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rspec-rails
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -104,13 +132,17 @@ extra_rdoc_files: []
104
132
  files:
105
133
  - ".gitignore"
106
134
  - ".rspec"
135
+ - Changes.md
107
136
  - Gemfile
137
+ - Guardfile
108
138
  - LICENSE.md
109
139
  - README.md
110
140
  - Rakefile
111
141
  - app/assets/images/talking_stick/.keep
142
+ - app/assets/images/talking_stick/line-spinner.svg
143
+ - app/assets/images/talking_stick/loading.gif
112
144
  - app/assets/javascripts/talking_stick/application.js
113
- - app/assets/javascripts/talking_stick/talking_stick.js
145
+ - app/assets/javascripts/talking_stick/talking_stick.js.erb
114
146
  - app/assets/javascripts/talking_stick/talking_stick/partner.js
115
147
  - app/assets/javascripts/talking_stick/talking_stick/rails_signaling.js
116
148
  - app/assets/stylesheets/talking_stick/application.css
@@ -121,7 +153,6 @@ files:
121
153
  - app/models/talking_stick/participant.rb
122
154
  - app/models/talking_stick/room.rb
123
155
  - app/models/talking_stick/signal.rb
124
- - app/views/layouts/talking_stick/application.html.erb
125
156
  - app/views/talking_stick/participants/_form.html.erb
126
157
  - app/views/talking_stick/participants/edit.html.erb
127
158
  - app/views/talking_stick/participants/index.html.erb
@@ -133,10 +164,15 @@ files:
133
164
  - app/views/talking_stick/rooms/new.html.erb
134
165
  - app/views/talking_stick/rooms/show.html.erb
135
166
  - bin/rails
167
+ - config/locales/en.yml
168
+ - config/locales/it.yml
136
169
  - config/routes.rb
137
170
  - db/migrate/20150510181337_create_talking_stick_rooms.rb
138
171
  - db/migrate/20150510182258_create_talking_stick_participants.rb
139
172
  - db/migrate/20150511005922_create_talking_stick_signals.rb
173
+ - db/migrate/20150722200822_add_slug_to_talking_stick_rooms.rb
174
+ - lib/generators/talking_stick/views/USAGE
175
+ - lib/generators/talking_stick/views/views_generator.rb
140
176
  - lib/talking_stick.rb
141
177
  - lib/talking_stick/engine.rb
142
178
  - lib/talking_stick/version.rb
@@ -207,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
243
  version: '0'
208
244
  requirements: []
209
245
  rubyforge_project:
210
- rubygems_version: 2.4.6
246
+ rubygems_version: 2.2.2
211
247
  signing_key:
212
248
  specification_version: 4
213
249
  summary: Easy group-based video conversations for Rails with WebRTC
@@ -256,3 +292,4 @@ test_files:
256
292
  - spec/models/talking_stick/participant_spec.rb
257
293
  - spec/models/talking_stick/room_spec.rb
258
294
  - spec/spec_helper.rb
295
+ has_rdoc:
@@ -1,141 +0,0 @@
1
- var TalkingStick = (function(self) {
2
- self.guid = undefined;
3
- self.myStream = undefined;
4
- self.joinedAt = undefined;
5
- self._options = {
6
- media: { audio: true, video: true },
7
- localVideo: undefined, // Set this to the DOM element where video should be rendered
8
- logLevel: 'error',
9
- };
10
-
11
- self.logLevels = [
12
- 'trace',
13
- 'debug',
14
- 'notice',
15
- 'warning',
16
- 'error',
17
- ];
18
-
19
- self.init = function(options) {
20
- $.extend(self._options, options);
21
-
22
- self.logLevelIdx = self.logLevels.indexOf(self._options.logLevel);
23
- self.partners = {};
24
- self.guid = self._options.guid || self.generateGUID();
25
- self.signalingEngine = self._options.signalingEngine;
26
- self.setupLocalVideo();
27
- self.log('notice', 'TalkingStick initialized.');
28
- };
29
-
30
- self.log = function() {
31
- var level = arguments[0];
32
- var levelIdx = self.logLevels.indexOf(level);
33
- if (levelIdx >= self.logLevelIdx) {
34
- var args = Array.prototype.slice.call(arguments, 1);
35
- args.unshift('[' + level + ']');
36
- switch(levelIdx) {
37
- case 4:
38
- console.error.apply(console, args);
39
- break;
40
- case 3:
41
- console.warn.apply(console, args);
42
- break;
43
- default:
44
- console.log.apply(console, args);
45
- break;
46
- }
47
- }
48
- }
49
-
50
- self.setupLocalVideo = function() {
51
- var localVideo = $(self._options.localVideo);
52
-
53
- self.prepareVideoElement(localVideo);
54
-
55
- self.log('debug', 'Requesting user consent to access to input devices');
56
- navigator.getUserMedia(self._options.media, function(stream) {
57
- self.log('debug', 'User has granted access to input devices');
58
- self.myStream = stream;
59
-
60
- // The JS API requires the raw DOM element, not a jQuery wrapper
61
- attachMediaStream(localVideo[0], stream);
62
- }, self.errorCallback);
63
- };
64
-
65
- self.connect = function() {
66
- // Tell the server we're ready to start contacting the other participants
67
- var data = {
68
- participant: {
69
- guid: self.guid,
70
- }
71
- }
72
- $.post(self._options.roomUrl + '/participants.json', data)
73
- .success(function(data) {
74
- self.log('notice', 'TalkingStick connected to the room.');
75
- self.joinedAt = new Date(data.joined_at);
76
- self.signalingEngine.connected();
77
- })
78
- .fail(function() { self.ajaxErrorLog('Error joining the room', arguments) });
79
- };
80
-
81
-
82
- self.errorCallback = function(error) {
83
- self.log('error', error);
84
- };
85
-
86
- /*
87
- * participant.guid
88
- * participant.joined_at
89
- */
90
- self.addPartner = function(participant) {
91
- self.log('debug', 'Adding new partner to this conversation', participant);
92
- var partnerVideo = document.createElement('video');
93
- self.prepareVideoElement(partnerVideo);
94
- $('#partnerVideos').append(partnerVideo);
95
- var options = {
96
- videoElement: partnerVideo,
97
- signalingEngine: self.signalingEngine,
98
- }
99
-
100
- partner = new TalkingStick.Partner(participant, options);
101
- self.partners[participant.guid] = partner;
102
-
103
- partner.connect(self.myStream);
104
-
105
- if (partner.joinedAt < TalkingStick.joinedAt) {
106
- self.log('trace', 'Sending Offer to', partner.guid);
107
- // Send Offers to partners who joined before us.
108
- // Expect partners who join after us to send us Offers.
109
- partner.sendOffer();
110
- }
111
-
112
- return partner;
113
- };
114
-
115
- self.prepareVideoElement = function(el) {
116
- el = $(el);
117
-
118
- // Ensure video streams play as soon as they are attached
119
- el.attr('autoplay', true);
120
-
121
- // Prevent local audio feedback
122
- el.attr('muted', true);
123
- };
124
-
125
- self.generateGUID = function () {
126
- function s4() {
127
- return Math.floor((1 + Math.random()) * 0x10000)
128
- .toString(16)
129
- .substring(1);
130
- }
131
- return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
132
- s4() + '-' + s4() + s4() + s4();
133
- };
134
-
135
- self.ajaxErrorLog = function(message, ajaxFailArgs) {
136
- // ajaxFailArgs: 0: jqXHR; 1: textStatus; 2: errorThrown
137
- self.log('error', message + ':', ajaxFailArgs[2]);
138
- }
139
-
140
- return self;
141
- }(TalkingStick || {}));