amazon-chime-sdk-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +87 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +47 -0
  5. data/.yardopts +6 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +25 -0
  8. data/LICENSE +21 -0
  9. data/README.md +1109 -0
  10. data/Rakefile +20 -0
  11. data/amazon-chime-sdk-rails.gemspec +29 -0
  12. data/gemfiles/Gemfile.rails-5.0 +25 -0
  13. data/gemfiles/Gemfile.rails-5.1 +25 -0
  14. data/gemfiles/Gemfile.rails-5.2 +25 -0
  15. data/gemfiles/Gemfile.rails-6.0 +25 -0
  16. data/lib/amazon-chime-sdk-rails.rb +37 -0
  17. data/lib/chime_sdk/config.rb +90 -0
  18. data/lib/chime_sdk/controller/attendees.rb +128 -0
  19. data/lib/chime_sdk/controller/common.rb +202 -0
  20. data/lib/chime_sdk/controller/meetings.rb +192 -0
  21. data/lib/chime_sdk/meeting_coordinator.rb +184 -0
  22. data/lib/chime_sdk/version.rb +4 -0
  23. data/lib/generators/chime_sdk/controllers_generator.rb +104 -0
  24. data/lib/generators/chime_sdk/install_generator.rb +27 -0
  25. data/lib/generators/chime_sdk/js_generator.rb +67 -0
  26. data/lib/generators/chime_sdk/views_generator.rb +43 -0
  27. data/lib/generators/templates/chime_sdk.rb +28 -0
  28. data/lib/generators/templates/controllers/README +35 -0
  29. data/lib/generators/templates/controllers/meeting_attendees_controller.rb +106 -0
  30. data/lib/generators/templates/controllers/meetings_controller.rb +146 -0
  31. data/lib/generators/templates/views/meetings/index.html.erb +27 -0
  32. data/lib/generators/templates/views/meetings/show.html.erb +136 -0
  33. data/spec/factories/rooms.rb +5 -0
  34. data/spec/factories/users.rb +8 -0
  35. data/spec/generators/controllers_generator_spec.rb +113 -0
  36. data/spec/generators/install_generator_spec.rb +24 -0
  37. data/spec/generators/js_generator_spec.rb +26 -0
  38. data/spec/generators/views_generator_spec.rb +46 -0
  39. data/spec/rails_app/Rakefile +15 -0
  40. data/spec/rails_app/app/assets/config/manifest.js +2 -0
  41. data/spec/rails_app/app/assets/images/.keep +0 -0
  42. data/spec/rails_app/app/assets/javascripts/.keep +0 -0
  43. data/spec/rails_app/app/assets/stylesheets/application.css +15 -0
  44. data/spec/rails_app/app/assets/stylesheets/scaffolds.scss +65 -0
  45. data/spec/rails_app/app/controllers/api/meeting_attendees_controller.rb +3 -0
  46. data/spec/rails_app/app/controllers/api/meetings_controller.rb +3 -0
  47. data/spec/rails_app/app/controllers/api/rooms_controller.rb +3 -0
  48. data/spec/rails_app/app/controllers/application_controller.rb +11 -0
  49. data/spec/rails_app/app/controllers/entries_controller.rb +47 -0
  50. data/spec/rails_app/app/controllers/meeting_attendees_controller.rb +121 -0
  51. data/spec/rails_app/app/controllers/meetings_controller.rb +162 -0
  52. data/spec/rails_app/app/controllers/rooms_controller.rb +76 -0
  53. data/spec/rails_app/app/controllers/spa_controller.rb +6 -0
  54. data/spec/rails_app/app/javascript/App.vue +50 -0
  55. data/spec/rails_app/app/javascript/channels/consumer.js +6 -0
  56. data/spec/rails_app/app/javascript/channels/index.js +5 -0
  57. data/spec/rails_app/app/javascript/components/DeviseTokenAuth.vue +84 -0
  58. data/spec/rails_app/app/javascript/components/meetings/Index.vue +100 -0
  59. data/spec/rails_app/app/javascript/components/meetings/Meeting.vue +178 -0
  60. data/spec/rails_app/app/javascript/components/rooms/Index.vue +53 -0
  61. data/spec/rails_app/app/javascript/components/rooms/Show.vue +91 -0
  62. data/spec/rails_app/app/javascript/packs/application.js +17 -0
  63. data/spec/rails_app/app/javascript/packs/spa.js +14 -0
  64. data/spec/rails_app/app/javascript/router/index.js +74 -0
  65. data/spec/rails_app/app/javascript/store/index.js +37 -0
  66. data/spec/rails_app/app/models/application_record.rb +3 -0
  67. data/spec/rails_app/app/models/entry.rb +5 -0
  68. data/spec/rails_app/app/models/room.rb +12 -0
  69. data/spec/rails_app/app/models/user.rb +6 -0
  70. data/spec/rails_app/app/views/devise/registrations/new.html.erb +34 -0
  71. data/spec/rails_app/app/views/layouts/_header.html.erb +20 -0
  72. data/spec/rails_app/app/views/layouts/application.html.erb +18 -0
  73. data/spec/rails_app/app/views/meetings/index.html.erb +28 -0
  74. data/spec/rails_app/app/views/meetings/show.html.erb +136 -0
  75. data/spec/rails_app/app/views/rooms/_form.html.erb +22 -0
  76. data/spec/rails_app/app/views/rooms/_room.json.jbuilder +7 -0
  77. data/spec/rails_app/app/views/rooms/edit.html.erb +6 -0
  78. data/spec/rails_app/app/views/rooms/index.html.erb +27 -0
  79. data/spec/rails_app/app/views/rooms/index.json.jbuilder +1 -0
  80. data/spec/rails_app/app/views/rooms/new.html.erb +5 -0
  81. data/spec/rails_app/app/views/rooms/show.html.erb +42 -0
  82. data/spec/rails_app/app/views/rooms/show.json.jbuilder +1 -0
  83. data/spec/rails_app/app/views/spa/index.html.erb +1 -0
  84. data/spec/rails_app/babel.config.js +72 -0
  85. data/spec/rails_app/bin/bundle +114 -0
  86. data/spec/rails_app/bin/rails +9 -0
  87. data/spec/rails_app/bin/rake +9 -0
  88. data/spec/rails_app/bin/setup +36 -0
  89. data/spec/rails_app/bin/spring +17 -0
  90. data/spec/rails_app/bin/webpack +18 -0
  91. data/spec/rails_app/bin/webpack-dev-server +18 -0
  92. data/spec/rails_app/bin/yarn +11 -0
  93. data/spec/rails_app/config.ru +5 -0
  94. data/spec/rails_app/config/application.rb +21 -0
  95. data/spec/rails_app/config/boot.rb +4 -0
  96. data/spec/rails_app/config/cable.yml +10 -0
  97. data/spec/rails_app/config/credentials.yml.enc +1 -0
  98. data/spec/rails_app/config/database.yml +25 -0
  99. data/spec/rails_app/config/environment.rb +14 -0
  100. data/spec/rails_app/config/environments/development.rb +62 -0
  101. data/spec/rails_app/config/environments/production.rb +112 -0
  102. data/spec/rails_app/config/environments/test.rb +49 -0
  103. data/spec/rails_app/config/initializers/application_controller_renderer.rb +8 -0
  104. data/spec/rails_app/config/initializers/assets.rb +15 -0
  105. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  106. data/spec/rails_app/config/initializers/chime_sdk.rb +28 -0
  107. data/spec/rails_app/config/initializers/content_security_policy.rb +30 -0
  108. data/spec/rails_app/config/initializers/cookies_serializer.rb +5 -0
  109. data/spec/rails_app/config/initializers/devise.rb +311 -0
  110. data/spec/rails_app/config/initializers/devise_token_auth.rb +60 -0
  111. data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  112. data/spec/rails_app/config/initializers/inflections.rb +16 -0
  113. data/spec/rails_app/config/initializers/mime_types.rb +4 -0
  114. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  115. data/spec/rails_app/config/locales/devise.en.yml +65 -0
  116. data/spec/rails_app/config/locales/en.yml +33 -0
  117. data/spec/rails_app/config/puma.rb +38 -0
  118. data/spec/rails_app/config/routes.rb +24 -0
  119. data/spec/rails_app/config/secrets.yml +8 -0
  120. data/spec/rails_app/config/spring.rb +6 -0
  121. data/spec/rails_app/config/storage.yml +34 -0
  122. data/spec/rails_app/config/webpack/development.js +5 -0
  123. data/spec/rails_app/config/webpack/environment.js +7 -0
  124. data/spec/rails_app/config/webpack/loaders/vue.js +6 -0
  125. data/spec/rails_app/config/webpack/production.js +5 -0
  126. data/spec/rails_app/config/webpack/test.js +5 -0
  127. data/spec/rails_app/config/webpacker.yml +97 -0
  128. data/spec/rails_app/db/migrate/20200912140231_devise_create_users.rb +45 -0
  129. data/spec/rails_app/db/migrate/20200912140352_add_tokens_to_users.rb +12 -0
  130. data/spec/rails_app/db/migrate/20200912140657_create_rooms.rb +9 -0
  131. data/spec/rails_app/db/migrate/20200912140749_create_entries.rb +10 -0
  132. data/spec/rails_app/db/schema.rb +49 -0
  133. data/spec/rails_app/db/seeds.rb +41 -0
  134. data/spec/rails_app/log/.keep +0 -0
  135. data/spec/rails_app/package.json +23 -0
  136. data/spec/rails_app/postcss.config.js +12 -0
  137. data/spec/rails_app/public/404.html +67 -0
  138. data/spec/rails_app/public/422.html +67 -0
  139. data/spec/rails_app/public/500.html +66 -0
  140. data/spec/rails_app/public/favicon.ico +0 -0
  141. data/spec/rails_app/public/robots.txt +1 -0
  142. data/spec/rails_app/tmp/.keep +0 -0
  143. data/spec/requests/atendees_spec.rb +182 -0
  144. data/spec/requests/meetings_spec.rb +433 -0
  145. data/spec/spec_helper.rb +35 -0
  146. metadata +400 -0
@@ -0,0 +1,17 @@
1
+ // This file is automatically compiled by Webpack, along with any other files
2
+ // present in this directory. You're encouraged to place your actual application logic in
3
+ // a relevant structure within app/javascript and only use these pack files to reference
4
+ // that code so it'll be compiled.
5
+
6
+ require("@rails/ujs").start()
7
+ require("turbolinks").start()
8
+ require("@rails/activestorage").start()
9
+ require("channels")
10
+
11
+
12
+ // Uncomment to copy all static images under ../images to the output folder and reference
13
+ // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
14
+ // or the `imagePath` JavaScript helper below.
15
+ //
16
+ // const images = require.context('../images', true)
17
+ // const imagePath = (name) => images(name, true)
@@ -0,0 +1,14 @@
1
+ import Vue from 'vue'
2
+ import App from '../App.vue'
3
+ import router from '../router'
4
+ import store from '../store'
5
+
6
+ Vue.config.productionTip = false
7
+
8
+ document.addEventListener('DOMContentLoaded', () => {
9
+ new Vue({
10
+ router,
11
+ store,
12
+ render: h => h(App)
13
+ }).$mount('#app')
14
+ })
@@ -0,0 +1,74 @@
1
+ import Vue from 'vue'
2
+ import VueRouter from 'vue-router'
3
+ import store from '../store'
4
+ import DeviseTokenAuth from '../components/DeviseTokenAuth.vue'
5
+ import Rooms from '../components/rooms/Index.vue'
6
+ import Room from '../components/rooms/Show.vue'
7
+ import Meetings from '../components/meetings/Index.vue'
8
+ import Meeting from '../components/meetings/Meeting.vue'
9
+
10
+ Vue.use(VueRouter)
11
+
12
+ const routes = [
13
+ // Routes for common components
14
+ {
15
+ path: '/',
16
+ name: 'Root',
17
+ component: Rooms
18
+ },
19
+ {
20
+ path: '/users/sign_in',
21
+ name: 'SignIn',
22
+ component: DeviseTokenAuth
23
+ },
24
+ {
25
+ path: '/users/sign_out',
26
+ name: 'SignOut',
27
+ component: DeviseTokenAuth,
28
+ props: { isLogout: true }
29
+ },
30
+
31
+ // Routes for Rooms
32
+ {
33
+ path: '/rooms',
34
+ name: 'Rooms',
35
+ component: Rooms
36
+ },
37
+ {
38
+ path: '/rooms/:room_id',
39
+ name: 'Room',
40
+ component: Room,
41
+ props: true,
42
+ meta: { requiresAuth: true }
43
+ },
44
+
45
+ // Routes for Meetings
46
+ {
47
+ path: '/rooms/:room_id/meetings',
48
+ name: 'Meetings',
49
+ component: Meetings,
50
+ props: true,
51
+ meta: { requiresAuth: true }
52
+ },
53
+ {
54
+ path: '/rooms/:room_id/meetings/:meeting_id',
55
+ name: 'Meeting',
56
+ component: Meeting,
57
+ props: true,
58
+ meta: { requiresAuth: true }
59
+ }
60
+ ]
61
+
62
+ const router = new VueRouter({
63
+ routes
64
+ })
65
+
66
+ router.beforeEach((to, from, next) => {
67
+ if (to.matched.some(record => record.meta.requiresAuth) && !store.getters.userSignedIn) {
68
+ next({ name: 'SignIn', query: { redirect: to.fullPath }});
69
+ } else {
70
+ next();
71
+ }
72
+ })
73
+
74
+ export default router
@@ -0,0 +1,37 @@
1
+ import Vue from 'vue'
2
+ import Vuex from 'vuex'
3
+ import createPersistedState from "vuex-persistedstate"
4
+
5
+ Vue.use(Vuex)
6
+
7
+ export default new Vuex.Store({
8
+ state: {
9
+ signedInStatus: false,
10
+ currentUser: null,
11
+ authHeaders: {}
12
+ },
13
+ mutations: {
14
+ signIn(state, { user, authHeaders }) {
15
+ state.currentUser = user;
16
+ state.authHeaders = authHeaders;
17
+ state.signedInStatus = true;
18
+ },
19
+ signOut(state) {
20
+ state.signedInStatus = false;
21
+ state.currentUser = null;
22
+ state.authHeaders = {};
23
+ }
24
+ },
25
+ getters: {
26
+ userSignedIn(state) {
27
+ return state.signedInStatus;
28
+ },
29
+ currentUser(state) {
30
+ return state.currentUser;
31
+ },
32
+ authHeaders(state) {
33
+ return state.authHeaders;
34
+ }
35
+ },
36
+ plugins: [createPersistedState({storage: window.sessionStorage})]
37
+ });
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,5 @@
1
+ class Entry < ApplicationRecord
2
+ belongs_to :room
3
+ belongs_to :user
4
+ validates :user, uniqueness: { scope: :room }
5
+ end
@@ -0,0 +1,12 @@
1
+ class Room < ApplicationRecord
2
+ has_many :entries, dependent: :destroy
3
+ has_many :members, through: :entries, source: :user
4
+
5
+ def add_member(user)
6
+ entries.create(user: user)
7
+ end
8
+
9
+ def member?(user)
10
+ members.include?(user)
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class User < ApplicationRecord
2
+ devise :database_authenticatable, :registerable
3
+ include DeviseTokenAuth::Concerns::User
4
+ has_many :entries, dependent: :destroy
5
+ validates_uniqueness_of :name
6
+ end
@@ -0,0 +1,34 @@
1
+ <h2>Sign up</h2>
2
+
3
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
4
+ <%= render "devise/shared/error_messages", resource: resource %>
5
+
6
+ <div class="field">
7
+ <%= f.label :name %><br />
8
+ <%= f.text_field :name, autocomplete: "name" %>
9
+ </div>
10
+
11
+ <div class="field">
12
+ <%= f.label :email %><br />
13
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
14
+ </div>
15
+
16
+ <div class="field">
17
+ <%= f.label :password %>
18
+ <% if @minimum_password_length %>
19
+ <em>(<%= @minimum_password_length %> characters minimum)</em>
20
+ <% end %><br />
21
+ <%= f.password_field :password, autocomplete: "new-password" %>
22
+ </div>
23
+
24
+ <div class="field">
25
+ <%= f.label :password_confirmation %><br />
26
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
27
+ </div>
28
+
29
+ <div class="actions">
30
+ <%= f.submit "Sign up" %>
31
+ </div>
32
+ <% end %>
33
+
34
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,20 @@
1
+ <header>
2
+ <div>
3
+ <div>
4
+ <%= link_to 'SPA with Rails API', "/spa/" %>
5
+ <strong><%= link_to 'Rails App with Action View', root_path %></strong>
6
+ </div>
7
+ <div>
8
+ <strong>Rails Application for Amazon Chime SDK Meeting (Rails App with Action View)</strong>
9
+ </div>
10
+ <div>
11
+ <% if user_signed_in? %>
12
+ <%= current_user.name %>
13
+ <%= link_to 'Logout', destroy_user_session_path, method: :delete %>
14
+ <% else %>
15
+ <%= link_to "Sign up", new_user_registration_path %>
16
+ <%= link_to 'Login', new_user_session_path %>
17
+ <% end %>
18
+ </div>
19
+ </div>
20
+ </header>
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Rails Application for Amazon Chime SDK Meeting</title>
5
+ <%= csrf_meta_tags %>
6
+
7
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
8
+ <%= yield(:javascript_pack_tag) %>
9
+ <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
10
+ </head>
11
+
12
+ <body>
13
+ <div id="app">
14
+ <%= render 'layouts/header' %>
15
+ <%= yield %>
16
+ <div>
17
+ </body>
18
+ </html>
@@ -0,0 +1,28 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Meetings by Amazon Chime SDK</h1>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th>Meeting ID</th>
9
+ <th>External Meeting ID</th>
10
+ <th>Media Region</th>
11
+ <th colspan="1"></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <% @meetings.each do |meeting| %>
17
+ <tr>
18
+ <td><%= link_to(meeting[:Meeting][:MeetingId], meeting_resource_path(meeting[:Meeting][:MeetingId])) %></td>
19
+ <td><%= meeting[:Meeting][:ExternalMeetingId] %></td>
20
+ <td><%= meeting[:Meeting][:MediaRegion] %></td>
21
+ <td><%= link_to 'Destroy', meeting_resource_path(meeting[:Meeting][:MeetingId]), method: :delete, data: { confirm: 'Are you sure?' } %></td>
22
+ </tr>
23
+ <% end %>
24
+ </tbody>
25
+ </table>
26
+
27
+ <p><%= link_to 'Create Meeting', meeting_resources_path, method: :post %></p>
28
+ <p><%= link_to("Back to Room", room_path(@room)) %></p>
@@ -0,0 +1,136 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Meeting by Amazon Chime SDK</h1>
4
+
5
+ <p><%= link_to("Show Meetings", meeting_resources_path) %></p>
6
+
7
+ <% if @meeting %>
8
+ <strong>Meeting</strong>
9
+ <p>
10
+ Meeting ID : <%= link_to(@meeting[:Meeting][:MeetingId], meeting_resource_path(@meeting[:Meeting][:MeetingId])) %><br>
11
+ External Meeting ID : <%= @meeting[:Meeting][:ExternalMeetingId] %><br>
12
+ Media Region : <%= @meeting[:Meeting][:MediaRegion] %>
13
+ </p>
14
+
15
+ <div id="meeting" meeting-data="<%= @meeting.to_json %>"></div>
16
+ <% end %>
17
+
18
+ <% if @attendee %>
19
+ <strong>Attendee</strong>
20
+ <p>
21
+ Attendee ID : <%= link_to(@attendee[:Attendee][:AttendeeId], attendee_resource_path(@meeting[:Meeting][:MeetingId], @attendee[:Attendee][:AttendeeId])) %><br>
22
+ External User ID : <%= @attendee[:Attendee][:ExternalUserId] %><br>
23
+ Application Attendee Name : <%= application_attendee_name(@attendee) %>
24
+ </p>
25
+
26
+ <div id="status">
27
+ <strong>Status</strong>
28
+ <div id="meeting-status">
29
+ <p>Meeting : Offline <input type="button" value="Join a meeting" onclick="join()"></p>
30
+ </div>
31
+ <div id="attendee-status"></div>
32
+ </div>
33
+
34
+ <audio id="audio"></audio>
35
+
36
+ <div id="attendee" attendee-data="<%= @attendee.to_json %>"></div>
37
+ <div id="attendee-resources-path" attendee-resources-path-data="<%= attendee_resources_path(@meeting[:Meeting][:MeetingId]) %>"></div>
38
+ <% end %>
39
+
40
+ <%= javascript_include_tag "amazon-chime-sdk.min.js" %>
41
+ <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
42
+
43
+ <script defer>
44
+ // This is a very simple script as a template view using Amazon Chime SDK.
45
+ // This script only provides simple audio meeting function.
46
+ // See the following guide to develop your own video meeting application.
47
+ // https://github.com/aws/amazon-chime-sdk-js
48
+ // https://aws.github.io/amazon-chime-sdk-js/modules/gettingstarted.html
49
+
50
+ // Customize this function to extract attendee name to show
51
+ function showApplicationUserName(attendee) {
52
+ // return attendee.Attendee.AttendeeId;
53
+ return `${attendee.Attendee.ApplicationMetadata.User.name} (${attendee.Attendee.AttendeeId})`;
54
+ }
55
+
56
+ const meeting = JSON.parse(document.getElementById('meeting').getAttribute('meeting-data'));
57
+ const attendee = JSON.parse(document.getElementById('attendee').getAttribute('attendee-data'));
58
+ const attendeeResourcesPath = document.getElementById('attendee-resources-path').getAttribute('attendee-resources-path-data');
59
+ const audioOutputElement = document.getElementById('audio');
60
+
61
+ const logger = new ChimeSDK.ConsoleLogger('ChimeMeetingLogs', ChimeSDK.LogLevel.INFO);
62
+ const deviceController = new ChimeSDK.DefaultDeviceController(logger);
63
+ const configuration = new ChimeSDK.MeetingSessionConfiguration(meeting, attendee);
64
+ const meetingSession = new ChimeSDK.DefaultMeetingSession(configuration, logger, deviceController);
65
+
66
+ const attendeeApi = axios.create({
67
+ baseURL: `${attendeeResourcesPath}/`
68
+ });
69
+
70
+ async function join() {
71
+ try {
72
+ const audioInputDevices = await meetingSession.audioVideo.listAudioInputDevices();
73
+ const audioOutputDevices = await meetingSession.audioVideo.listAudioOutputDevices();
74
+ await meetingSession.audioVideo.chooseAudioInputDevice(audioInputDevices[0].deviceId);
75
+ await meetingSession.audioVideo.chooseAudioOutputDevice(audioOutputDevices[0].deviceId);
76
+ } catch (error) {
77
+ // handle error - unable to acquire audio device perhaps due to permissions blocking
78
+ console.log(error);
79
+ }
80
+ meetingSession.audioVideo.bindAudioElement(audioOutputElement);
81
+ meetingSession.audioVideo.start();
82
+ document.getElementById("meeting-status").innerHTML = '<p>Meeting : Online <input type="button" value="Leave a meeting" onclick="leave()"></p><div id="audio-status"></div>';
83
+ document.getElementById("audio-status").innerHTML = '<p>Audio : Active <input type="button" value="Mute" onclick="mute()"></p>';
84
+ document.getElementById("attendee-status").innerHTML = '<strong>Present Attendees</strong>';
85
+ parepareAttendeeStatus();
86
+ }
87
+
88
+ function leave() {
89
+ meetingSession.audioVideo.stop();
90
+ document.getElementById("meeting-status").innerHTML = '<p>Meeting : Offline <input type="button" value="Join a meeting" onclick="join()">';
91
+ document.getElementById("attendee-status").innerHTML = '';
92
+ }
93
+
94
+ function mute() {
95
+ meetingSession.audioVideo.realtimeMuteLocalAudio();
96
+ document.getElementById("audio-status").innerHTML = '<p>Audio : Muted <input type="button" value="Unmute" onclick="unmute()"></p>';
97
+ }
98
+
99
+ function unmute() {
100
+ const unmuted = meetingSession.audioVideo.realtimeUnmuteLocalAudio();
101
+ if (unmuted) {
102
+ document.getElementById("audio-status").innerHTML = '<p>Audio : Active <input type="button" value="Mute" onclick="mute()"></p>';
103
+ } else {
104
+ console.log('You cannot unmute yourself');
105
+ }
106
+ }
107
+
108
+ function parepareAttendeeStatus() {
109
+ const presentAttendeeMap = {};
110
+ const callback = (presentAttendeeId, present) => {
111
+ if (present) {
112
+ attendeeApi.get(presentAttendeeId)
113
+ .then(response => {
114
+ presentAttendeeMap[presentAttendeeId] = response.data;
115
+ updateAttendeeStatus(presentAttendeeMap);
116
+ })
117
+ .catch(error => {
118
+ console.log(error);
119
+ });
120
+ } else {
121
+ delete presentAttendeeMap[presentAttendeeId]
122
+ updateAttendeeStatus(presentAttendeeMap);
123
+ }
124
+ };
125
+ meetingSession.audioVideo.realtimeSubscribeToAttendeeIdPresence(callback);
126
+ }
127
+
128
+ function updateAttendeeStatus(presentAttendeeMap) {
129
+ presentAttendees = "<strong>Present Attendees</strong><ul>";
130
+ Object.values(presentAttendeeMap).forEach(attendee => {
131
+ presentAttendees = presentAttendees + `<li>${showApplicationUserName(attendee)}</li>`
132
+ });
133
+ presentAttendees = presentAttendees + '</ul>'
134
+ document.getElementById("attendee-status").innerHTML = presentAttendees;
135
+ }
136
+ </script>
@@ -0,0 +1,22 @@
1
+ <%= form_with(model: room, local: true) do |form| %>
2
+ <% if room.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(room.errors.count, "error") %> prohibited this room from being saved:</h2>
5
+
6
+ <ul>
7
+ <% room.errors.full_messages.each do |message| %>
8
+ <li><%= message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="field">
15
+ <%= form.label :name %>
16
+ <%= form.text_field :name %>
17
+ </div>
18
+
19
+ <div class="actions">
20
+ <%= form.submit %>
21
+ </div>
22
+ <% end %>