meeting.blade.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. @extends('layouts.guest-meeting')
  2. @section('content')
  3. <div id="meetingComponent">
  4. <h5 class="bg-dark font-weight-bold text-white m-0 py-3 px-4 d-flex">
  5. <span>Meeting</span>
  6. <span class="ml-auto" v-if="!started">
  7. Connecting...
  8. </span>
  9. <span class="ml-auto" v-if="started">
  10. <i class="fa fa-clock mr-1 text-light"></i>
  11. @{{ timeDisplay() }}
  12. </span>
  13. </h5>
  14. <div class="d-flex align-items-stretch mt-4 justify-content-center flex-nowrap">
  15. <div class="tp-bar">
  16. <div v-for="(guest, index) of guests"
  17. class="tp-item mb-4"
  18. :class="activeParticipant && activeType === 'guest' && activeParticipant.id === guest.id ? 'active' : ''"
  19. v-on:click="setActiveView('guest', guest)">
  20. <img :src="guest.image" alt="" class="w-100">
  21. <p class="font-weight-bold text-center mt-1 mb-0">@{{ guest.name }}</p>
  22. </div>
  23. <div v-if="!guest" class="tp-item mb-4">
  24. <img src="/images/person/guest.png" alt="" class="w-100">
  25. <p class="font-weight-bold text-center mt-1">Invite Guest</p>
  26. </div>
  27. </div>
  28. <div class="main-view" style="width: 800px; min-height: 600px;">
  29. <div class="p-4 w-100 h-100 d-flex align-items-stretch justify-content-stretch flex-column">
  30. <img :src="activeParticipant.image" class="d-block mw-100 mh-100 mx-auto">
  31. <p class="font-weight-bold text-center text-white mt-2">Feed from @{{ activeParticipant.name }}</p>
  32. </div>
  33. </div>
  34. <div class="tp-bar">
  35. <div v-for="(pro, index) of pros"
  36. class="tp-item mb-4"
  37. :class="activeParticipant && activeType === 'pro' && activeParticipant.id === pro.id ? 'active' : ''"
  38. v-on:click="setActiveView('pro', pro)">
  39. <div v-if="pro.status === 'active'">
  40. <img :src="pro.image" alt="" class="w-100">
  41. <p class="font-weight-bold text-center mt-1 mb-0">@{{ pro.name }}</p>
  42. <p class="font-weight-normal text-center">@{{ pro.type }}</p>
  43. </div>
  44. <div v-if="pro.status === 'connecting'">
  45. <img src="/images/person/connecting.jpg" alt="" class="w-100">
  46. <p class="font-weight-normal text-center mt-1 d-flex align-items-center justify-content-center">
  47. <img src="/images/loading.gif" class="mr-1">
  48. Connecting
  49. </p>
  50. </div>
  51. </div>
  52. </div>
  53. </div>
  54. </div>
  55. <script>
  56. new Vue({
  57. el: '#meetingComponent',
  58. delimiters: ['@{{', '}}'],
  59. data: {
  60. meetingID: '<?= $meetingID ?>',
  61. participantID: '<?= $participantID ?>',
  62. socket: null,
  63. stompClient: null,
  64. time: 0,
  65. startTime: 0,
  66. started: false,
  67. guest: false,
  68. activeType: false,
  69. activeParticipant: false,
  70. guests: [
  71. {
  72. id: 1,
  73. name: 'You',
  74. image: '/images/person/you.jpg',
  75. status: 'connecting',
  76. },
  77. ],
  78. pros: [
  79. {
  80. id: 1,
  81. name: 'Nancy Drew',
  82. type: 'Receptionist',
  83. image: '/images/person/nancy.jpg',
  84. status: 'connecting',
  85. }
  86. ]
  87. },
  88. methods: {
  89. setActiveView: function(_type, _participant) {
  90. if(_participant.status === 'active') {
  91. this.activeType = _type;
  92. this.activeParticipant = _participant;
  93. }
  94. },
  95. timeDisplay: function() {
  96. // this.time = new Date().getTime() - this.startTime;
  97. var seconds = this.time / 1000,
  98. minutes = parseInt(seconds / 60, 10);
  99. seconds = parseInt(seconds % 60, 10);
  100. return minutes + " min, " + seconds + " sec";
  101. },
  102. connectToFirstPro: function() {
  103. console.log('Connecting to first pro ...');
  104. this.pros = [];
  105. this.addPro();
  106. $.post('/api/meeting/request-dial-pro', {
  107. meetingUid: this.meetingID,
  108. }, function(_data) {
  109. console.log('Response to /api/meeting/request-dial-pro');
  110. console.log(_data);
  111. }, 'json');
  112. },
  113. addPro: function() {
  114. this.pros.push({
  115. id: '0',
  116. name: 'Pro',
  117. type: 'Receptionist',
  118. image: '/images/person/nancy.jpg',
  119. status: 'connecting',
  120. });
  121. return this.pros[this.pros.length - 1];
  122. },
  123. onProJoined: function(_data) {
  124. var found = false;
  125. // find pro with participant id
  126. var pro = this.pros.filter(function(_pro) {
  127. return _pro.id === _data.meetingParticipantUid;
  128. });
  129. // no pro with id, find first one with id = 0
  130. if(!pro.length) {
  131. pro = this.pros.filter(function(_pro) {
  132. return _pro.id === "0";
  133. });
  134. }
  135. // no pro with id = 0, create one
  136. if(!pro.length) {
  137. pro = this.addPro();
  138. }
  139. else {
  140. pro = pro[0];
  141. }
  142. pro.id = _data.meetingParticipantUid;
  143. pro.name = _data.proDisplayName;
  144. pro.type = _data.hcpCategory;
  145. pro.status = 'active';
  146. // TODO: init pro stream
  147. },
  148. playDemo: function() {
  149. this.activeType = 'guest';
  150. this.activeParticipant = this.guests[0];
  151. var self = this;
  152. // after 2 seconds, connect recep
  153. window.setTimeout(function() {
  154. self.pros[0].status = 'active';
  155. self.started = true;
  156. self.startTime = new Date().getTime();
  157. self.activeType = 'pro';
  158. self.activeParticipant = self.pros[0];
  159. window.setInterval(function() {
  160. self.time = new Date().getTime() - self.startTime;
  161. }, 1000);
  162. // after 1 second, add 2 docs in connecting state
  163. window.setTimeout(function() {
  164. self.pros.push({
  165. id: 2,
  166. name: 'Dr. Strange',
  167. type: 'Dietitian',
  168. image: '/images/person/strange.0.jpg',
  169. status: 'connecting',
  170. });
  171. self.pros.push({
  172. id: 3,
  173. name: 'Dr. Do Little',
  174. type: 'Cardiologist',
  175. image: '/images/person/dl.jpg',
  176. status: 'connecting',
  177. });
  178. // after a second, connect strange
  179. window.setTimeout(function() {
  180. self.pros[1].status = 'active';
  181. window.setTimeout(function() {
  182. self.pros[2].status = 'active';
  183. }, 1000);
  184. }, 1000);
  185. }, 1000);
  186. }, 2000);
  187. }
  188. },
  189. mounted: function() {
  190. this.pros = [];
  191. // this.playDemo();
  192. var self = this;
  193. // connect to WS
  194. self.socket = new SockJS('http://localhost:8080/ws');
  195. self.stompClient = Stomp.over(self.socket);
  196. self.stompClient.connect({}, function (frame) {
  197. console.log('Connected: ' + frame);
  198. // join self
  199. self.stompClient.send(
  200. "/app/meeting-participant-join-meeting",
  201. {},
  202. JSON.stringify({meetingParticipantUid: self.participantID})
  203. );
  204. // set self as active
  205. self.guests[0].id = '<?= $participantID ?>';
  206. self.guests[0].status = 'active';
  207. // TODO: init own stream
  208. // attempt to connect to first pro if "start"
  209. @if(request('start'))
  210. self.connectToFirstPro();
  211. @endif
  212. // subscribe to on pro joined WS event
  213. self.stompClient.subscribe("/topic/on-pro-join-meeting", function(message){
  214. console.log("on-pro-join-meeting:", message);
  215. message = JSON.parse(message.body);
  216. console.log(message.meetingParticipantUid);
  217. self.onProJoined(message);
  218. });
  219. });
  220. }
  221. });
  222. </script>
  223. @endsection