Bläddra i källkod

Pro: call accept workflow [WIP]

Vijayakrishnan 5 år sedan
förälder
incheckning
4ed662c974

+ 7 - 0
app/Http/Controllers/ProController.php

@@ -25,4 +25,11 @@ class ProController extends Controller
         $pro = Pro::where('uid', $uid)->first();
         return view('pro.show', compact('pro'));
     }
+
+    public function meeting(Request $request, $meetingID) {
+        return view('pro.meeting', [
+            'sessionKey' => $request->cookie('sessionKey'),
+            'meetingID' => $meetingID
+        ]);
+    }
 }

+ 6 - 3
resources/views/layouts/pro.blade.php

@@ -9,7 +9,7 @@
     <link rel="stylesheet" href="{{asset('css/lumen.min.css')}}">
     <link rel="stylesheet" href="{{asset('css/toastr.min.css')}}">
 
-    <script src="{{asset('js/jquery-3.2.1.min.js')}}"></script>
+    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
     <script src="{{asset('js/jquery-ui.min.js')}}"></script>
     <script src="{{asset('js/jquery.form.js')}}"></script>
     <script src="{{asset('js/toastr.min.js')}}"></script>
@@ -18,6 +18,9 @@
 
     <script src="{{asset('js/stomp.min.js')}}"></script>
 
+    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
+    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" integrity="sha384-1CmrxMRARb6aLqgBO7yyAxTOQE2AKb9GfXnEo760AUcUmFx3ibVJJAzGytlQcNXd" crossorigin="anonymous"></script>
+
     <title>Document</title>
 </head>
 
@@ -56,10 +59,10 @@
         </div>
     </div>
 
-  
+
 
     <script src="{{asset('js/moe.js')}}"></script>
 
 </body>
 
-</html>
+</html>

+ 6 - 4
resources/views/meeting.blade.php

@@ -125,13 +125,13 @@
                     });
                     return this.pros[this.pros.length - 1];
                 },
-                onProJoined: function(_pID) {
+                onProJoined: function(_data) {
 
                     var found = false;
 
                     // find pro with participant id
                     var pro = this.pros.filter(function(_pro) {
-                        return _pro.id === _pID;
+                        return _pro.id === _data.meetingParticipantUid;
                     });
 
                     // no pro with id, find first one with id = 0
@@ -149,7 +149,9 @@
                         pro = pro[0];
                     }
 
-                    pro.id = _pID;
+                    pro.id = _data.meetingParticipantUid;
+                    pro.name = _data.proDisplayName;
+                    pro.type = _data.hcpCategory;
                     pro.status = 'active';
 
                     // TODO: init pro stream
@@ -242,7 +244,7 @@
                         message = JSON.parse(message.body);
                         console.log(message.meetingParticipantUid);
 
-                        self.onProJoined(message.meetingParticipantUid);
+                        self.onProJoined(message);
 
                     });
 

+ 27 - 6
resources/views/pro/dashboard.blade.php

@@ -21,6 +21,27 @@
         <button cancel>Cancel</button>
     </form>
 </div>
+
+<div class="modal" id="incomingCallModal" tabindex="-1" role="dialog">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">Incoming Call</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <p>Click <b>Accept</b> to join the call.</p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-success px-4" onclick="joinCall()">Answer</button>
+                <button type="button" class="btn btn-secondary px-4" data-dismiss="modal">Reject</button>
+            </div>
+        </div>
+    </div>
+</div>
+
 <script>
 var stompClient = null;
 
@@ -58,12 +79,7 @@ function connect() {
             message = JSON.parse(message.body);
             console.log(message.meetingUid);
 
-            // auto accept for now
-            stompClient.send("/app/pro-join-meeting", {},
-                JSON.stringify({
-                    sessionKey: "<?= $sessionKey ?>",
-                    meetingUid: message.meetingUid
-                }));
+            $('#incomingCallModal').data('meetingUid', message.meetingUid).modal('show');
 
         });
 
@@ -77,6 +93,11 @@ function connect() {
 
 connect();
 
+function joinCall() {
+    var meetingUid = $('#incomingCallModal').data('meetingUid');
+    window.location.href = '/pro/meeting/' + meetingUid;
+}
+
 function disconnect() {
     if (stompClient !== null) {
         stompClient.disconnect();

+ 209 - 0
resources/views/pro/meeting.blade.php

@@ -0,0 +1,209 @@
+@extends('layouts.guest-meeting')
+@section('content')
+
+    <div id="meetingComponent">
+
+        <h5 class="bg-dark font-weight-bold text-white m-0 py-3 px-4 d-flex">
+            <span>Meeting</span>
+            <span class="ml-auto" v-if="!started">
+                Connecting...
+            </span>
+            <span class="ml-auto" v-if="started">
+                <i class="fa fa-clock mr-1 text-light"></i>
+                @{{ timeDisplay() }}
+            </span>
+        </h5>
+
+        <div class="d-flex align-items-stretch mt-4 justify-content-center flex-nowrap">
+            <div class="tp-bar">
+                <div v-for="(pro, index) of pros"
+                     class="tp-item mb-4"
+                     :class="activeParticipant && activeType === 'pro' && activeParticipant.id === pro.id ? 'active' : ''"
+                     v-on:click="setActiveView('pro', pro)">
+                    <div v-if="pro.status === 'active'">
+                        <img :src="pro.image" alt="" class="w-100">
+                        <p class="font-weight-bold text-center mt-1 mb-0">@{{ pro.name }}</p>
+                        <p class="font-weight-normal text-center">@{{ pro.type }}</p>
+                    </div>
+                    <div v-if="pro.status === 'connecting'">
+                        <img src="/images/person/connecting.jpg" alt="" class="w-100">
+                        <p class="font-weight-normal text-center mt-1 d-flex align-items-center justify-content-center">
+                            <img src="/images/loading.gif" class="mr-1">
+                            Connecting
+                        </p>
+                    </div>
+                </div>
+                <div class="tp-item mb-4">
+                    <img src="/images/person/guest.png" alt="" class="w-100">
+                    <p class="font-weight-bold text-center mt-1">Invite Pro</p>
+                </div>
+            </div>
+            <div class="main-view" style="width: 800px; min-height: 600px;">
+                <div class="p-4 w-100 h-100 d-flex align-items-stretch justify-content-stretch flex-column">
+                    <img :src="activeParticipant.image" class="d-block mw-100 mh-100 mx-auto">
+                    <p class="font-weight-bold text-center text-white mt-2">Feed from @{{ activeParticipant.name }}</p>
+                </div>
+            </div>
+            <div class="tp-bar">
+                <div v-for="(guest, index) of guests"
+                     class="tp-item mb-4"
+                     :class="activeParticipant && activeType === 'guest' && activeParticipant.id === guest.id ? 'active' : ''"
+                     v-on:click="setActiveView('guest', guest)">
+                    <img :src="guest.image" alt="" class="w-100">
+                    <p class="font-weight-bold text-center mt-1 mb-0">@{{ guest.name }}</p>
+                </div>
+            </div>
+        </div>
+
+    </div>
+
+    <script>
+        new Vue({
+            el: '#meetingComponent',
+            delimiters: ['@{{', '}}'],
+            data: {
+                meetingID: '<?= $meetingID ?>',
+                socket: null,
+                stompClient: null,
+                time: 0,
+                startTime: 0,
+                started: false,
+                guest: false,
+                activeType: false,
+                activeParticipant: false,
+                guests: [
+                    {
+                        id: 1,
+                        name: 'You',
+                        image: '/images/person/you.jpg',
+                        status: 'connecting',
+                    },
+                ],
+                pros: [
+                    {
+                        id: 1,
+                        name: 'Nancy Drew',
+                        type: 'Receptionist',
+                        image: '/images/person/nancy.jpg',
+                        status: 'connecting',
+                    }
+                ]
+            },
+            methods: {
+                setActiveView: function(_type, _participant) {
+                    if(_participant.status === 'active') {
+                        this.activeType = _type;
+                        this.activeParticipant = _participant;
+                    }
+                },
+                timeDisplay: function() {
+                    // this.time = new Date().getTime() - this.startTime;
+                    var seconds = this.time / 1000,
+                        minutes = parseInt(seconds / 60, 10);
+                    seconds = parseInt(seconds % 60, 10);
+                    return minutes + " min, " + seconds + " sec";
+                },
+                connectToFirstPro: function() {
+                    console.log('Connecting to first pro ...');
+                    this.pros = [];
+                    this.addPro();
+                    $.post('/api/meeting/request-dial-pro', {
+                        meetingUid: this.meetingID,
+                    }, function(_data) {
+                        console.log('Response to /api/meeting/request-dial-pro');
+                        console.log(_data);
+                    }, 'json');
+                },
+                addPro: function() {
+                    this.pros.push({
+                        id: '0',
+                        name: 'Pro',
+                        type: 'Receptionist',
+                        image: '/images/person/nancy.jpg',
+                        status: 'connecting',
+                    });
+                    return this.pros[this.pros.length - 1];
+                },
+                onProJoined: function(_pID) {
+
+                    var found = false;
+
+                    // find pro with participant id
+                    var pro = this.pros.filter(function(_pro) {
+                        return _pro.id === _pID;
+                    });
+
+                    // no pro with id, find first one with id = 0
+                    if(!pro.length) {
+                        pro = this.pros.filter(function(_pro) {
+                            return _pro.id === "0";
+                        });
+                    }
+
+                    // no pro with id = 0, create one
+                    if(!pro.length) {
+                        pro = this.addPro();
+                    }
+                    else {
+                        pro = pro[0];
+                    }
+
+                    pro.id = _pID;
+                    pro.status = 'active';
+
+                    // TODO: init pro stream
+                },
+            },
+            mounted: function() {
+
+                this.pros = [];
+
+                // this.playDemo();
+
+                var self = this;
+
+                // connect to WS
+                self.socket = new SockJS('http://localhost:8080/ws');
+                self.stompClient = Stomp.over(self.socket);
+                self.stompClient.connect({}, function (frame) {
+                    console.log('Connected: ' + frame);
+
+                    // join self
+                    // this is called everytime the pro call page is refreshed
+                    self.stompClient.send("/app/pro-join-meeting", {},
+                        JSON.stringify({
+                            sessionKey: "<?= $sessionKey ?>",
+                            meetingUid: "<?= $meetingID ?>",
+                        }));
+
+                    /*
+
+                    // set self as active
+                    self.guests[0].id = '';
+                    self.guests[0].status = 'active';
+
+                    // TODO: init own stream
+
+                    // attempt to connect to first pro if "start"
+                    @if(request('start'))
+                        self.connectToFirstPro();
+                    @endif
+
+                    // subscribe to on pro joined WS event
+                    self.stompClient.subscribe("/topic/on-pro-join-meeting", function(message){
+                        console.log("on-pro-join-meeting:", message);
+
+                        message = JSON.parse(message.body);
+                        console.log(message.meetingParticipantUid);
+
+                        self.onProJoined(message.meetingParticipantUid);
+
+                    });
+                    */
+
+                });
+            }
+        });
+    </script>
+
+@endsection

+ 2 - 0
routes/web.php

@@ -36,5 +36,7 @@ Route::middleware('ensureValidSession')->group(function(){
     Route::get("/pros/create", 'ProController@create')->name('pro-create');
     Route::get("/pros/show/{uid}", 'ProController@show')->name('pro-show');
 
+    Route::get('/pro/meeting/{meetingID}', 'ProController@meeting');
+
     Route::get('/pro/logout', 'AppSessionController@processProLogOut')->name('pro-logout');
 });