talking_stick 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 || {}));