Forráskód Böngészése

resolved conflict

Josh Kamau 5 éve
szülő
commit
aa92379fb2

+ 1 - 1
.idea/jsLibraryMappings.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="JavaScriptLibraryMappings">
-    <file url="PROJECT" libraries="{jquery-3.5.1, jquery-3.5.1.slim}" />
+    <file url="PROJECT" libraries="{bootstrap.bundle, jquery-3.5.1, jquery-3.5.1.slim}" />
   </component>
 </project>

+ 5 - 0
.idea/php.xml

@@ -98,4 +98,9 @@
     </include_path>
   </component>
   <component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
+  <component name="PhpUnit">
+    <phpunit_settings>
+      <PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" />
+    </phpunit_settings>
+  </component>
 </project>

+ 10 - 0
.idea/phpunit.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="PHPUnit">
+    <option name="directories">
+      <list>
+        <option value="$PROJECT_DIR$/tests" />
+      </list>
+    </option>
+  </component>
+</project>

+ 2 - 0
.idea/stagfe.iml

@@ -5,6 +5,7 @@
       <sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" />
       <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tests\" />
+      <excludeFolder url="file://$MODULE_DIR$/storage" />
       <excludeFolder url="file://$MODULE_DIR$/vendor/asm89/stack-cors" />
       <excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" />
       <excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
@@ -103,5 +104,6 @@
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="jquery-3.5.1.slim" level="application" />
     <orderEntry type="library" name="jquery-3.5.1" level="application" />
+    <orderEntry type="library" name="bootstrap.bundle" level="application" />
   </component>
 </module>

+ 1 - 1
.idea/vcs.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="VcsDirectoryMappings">
-    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="" vcs="Git" />
   </component>
 </project>

+ 16 - 1
app/Http/Controllers/GuestController.php

@@ -2,6 +2,8 @@
 
 namespace App\Http\Controllers;
 
+use App\Models\Meeting;
+use App\Models\MeetingParticipant;
 use Illuminate\Http\Request;
 use App\Models\Pro;
 
@@ -9,7 +11,20 @@ class GuestController extends Controller
 {
 
     public function meeting(Request $request, $meetingID, $participantID) {
-        return view('meeting', compact('meetingID', 'participantID'));
+        $meeting = Meeting::where('uid', $meetingID)->first();
+        if(!$meeting) {
+            return abort(404, "Meeting no longer active");
+        }
+        $participants = MeetingParticipant::where('meeting_id', $meeting->id)->get();
+        foreach ($participants as $participant) {
+            $participant->proName = $participant->proName(); // eager-fill proName
+        }
+        return view('meeting', [
+            'meetingID' => $meetingID,
+            'participantID'=> $participantID,
+            'participants' => $participants,
+            'guest' => true
+        ]);
     }
 
 }

+ 21 - 3
app/Http/Controllers/ProController.php

@@ -2,14 +2,16 @@
 
 namespace App\Http\Controllers;
 
+use App\Models\Meeting;
+use App\Models\MeetingParticipant;
 use Illuminate\Http\Request;
 use App\Models\Pro;
 
 class ProController extends Controller
 {
-    
-    public function dashboard(){
-        return view('pro.dashboard');
+
+    public function dashboard(Request $request){
+        return view('pro.dashboard', ['sessionKey' => $request->cookie('sessionKey')]);
     }
 
     public function index(){
@@ -25,4 +27,20 @@ class ProController extends Controller
         $pro = Pro::where('uid', $uid)->first();
         return view('pro.show', compact('pro'));
     }
+
+    public function meeting(Request $request, $meetingID) {
+        $meeting = Meeting::where('uid', $meetingID)->first();
+        if(!$meeting) {
+            return abort(404, "Meeting no longer active");
+        }
+        $participants = MeetingParticipant::where('meeting_id', $meeting->id)->get();
+        foreach ($participants as $participant) {
+            $participant->proName = $participant->proName(); // eager-fill proName
+        }
+        return view('meeting', [
+            'meetingID' => $meetingID,
+            'participants' => $participants,
+            'guest' => false
+        ]);
+    }
 }

+ 13 - 0
app/Models/Meeting.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Meeting extends Model
+{
+
+    protected $table = "meeting";
+
+    //
+}

+ 21 - 0
app/Models/MeetingParticipant.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class MeetingParticipant extends Model
+{
+
+    protected $table = "meeting_participant";
+
+    public function proName() {
+        if($this->guest_or_pro === 'PRO' && $this->pro_id) {
+            $pro = Pro::find($this->pro_id);
+            if($pro) {
+                return $pro->name_display;
+            }
+        }
+        return '';
+    }
+}

+ 27 - 9
public/css/meeting.css

@@ -93,19 +93,37 @@ h1 {
 }
 
 .tp-bar {
-    width: 140px;
+    width: 120px;
+}
+.tp-bar .tp-item {
+    padding-top: 1rem;
+    padding-bottom: 0.5rem;
+    border: 0.3rem solid transparent;
 }
 
-.tp-bar .tp-item img {
-    border-radius: 3px;
-    opacity: 0.8;
-    cursor: pointer;
+.tp-bar .tp-item.active {
+    border: 0.3rem solid #44bdad85;
 }
 
-.tp-bar .tp-item img:hover, .tp-bar .tp-item.active img {
-    opacity: 1;
+.avatar {
+    margin: auto;
+    width: 80px;
+    height: 80px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 100%;
+    font-size: 2rem;
+    font-weight: 600;
+}
+.invite-image {
+    width: 80px;
+    margin: auto;
+    display: block;
 }
 
-.tp-bar .tp-item.active img {
-    box-shadow: 0 0 0 0.3rem #44bdad85;
+.text-single {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
 }

+ 6 - 0
resources/views/layouts/guest-meeting.blade.php → resources/views/layouts/meeting.blade.php

@@ -3,6 +3,7 @@
 <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
 
     <title>X•Y•Z</title>
 
@@ -18,7 +19,12 @@
     <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 
     <link rel="stylesheet" href="/css/meeting.css">
+
+    {{-- WS --}}
+    <script src="/js/sockjs.min.js"></script>
+    <script src="/js/stomp.min.js"></script>
 </head>
+
 <body class="p-0 m-0">
 
 @yield('content')

+ 125 - 11
resources/views/layouts/pro.blade.php

@@ -2,26 +2,37 @@
 <html lang="en">
 
 <head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
+
+    <title>X•Y•Z</title>
+
+    <!-- Fonts -->
+    <link href="https://fonts.googleapis.com/css?family=Nunito:200,600,700" rel="stylesheet">
+
     <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>
+    {{-- Bootstrap --}}
+    <script src="https://code.jquery.com/jquery-3.5.1.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>
+
     <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>
 
-    <script src="{{asset('js/sockjs.min.js')}}"></script>
+    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 
-    <script src="{{asset('js/stomp.min.js')}}"></script>
+    <link rel="stylesheet" href="/css/meeting.css">
 
-    <title>Document</title>
+    {{-- WS --}}
+    <script src="/js/sockjs.min.js"></script>
+    <script src="/js/stomp.min.js"></script>
 </head>
 
-<body>
+<body class="p-0 m-0">
     <nav class="navbar navbar-expand-sm navbar-dark bg-primary">
         <a class="navbar-brand" href="#">Stag</a>
         <button class="navbar-toggler d-lg-none" type="button" data-toggle="collapse" data-target="#collapsibleNavId"
@@ -50,16 +61,119 @@
 
     <div class="container-fluid">
         <div class="row">
-            <div class="col-md-12 pt-2">
+            <div class="col-md-12 p-0">
                 @yield('content')
             </div>
         </div>
     </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 src="{{asset('js/moe.js')}}"></script>
 
+    <script>
+        var stompClient = null;
+
+        function setConnected(connected) {
+            console.log("User is connected");
+            $("#connect").prop("disabled", connected);
+            $("#disconnect").prop("disabled", !connected);
+            if (connected) {
+                $("#conversation").show();
+            }
+            else {
+                $("#conversation").hide();
+            }
+            $("#greetings").html("");
+        }
+
+        function connect() {
+            console.log("connecting...");
+            var socket = new SockJS('http://localhost:8080/ws');
+            stompClient = Stomp.over(socket);
+            stompClient.connect({}, function (frame) {
+                console.log("Connected");
+                setConnected(true);
+                console.log('Connected: ' + frame);
+
+                // stompClient.send("/app/update-participant-status", {}, JSON.stringify({meetingUid:"mymeetinguid", status:'mystatus'}));
+
+                // stompClient.subscribe('/user/topic/on-participant-status-change', function (message) {
+                //     console.log("Participant status changed: ", message);
+                // });
+
+                stompClient.subscribe("/user/topic/on-pro-incoming-call", function(message){
+                    console.log("incoming call:", message);
+
+                    message = JSON.parse(message.body);
+                    console.log(message.meetingUid);
+
+                    $('#incomingCallModal')
+                        .data('meetingUid', message.meetingUid)
+                        .modal('show');
+
+                });
+
+                // connect
+                stompClient.send("/app/pro-connect", {}, JSON.stringify({sessionKey: "<?= request()->cookie('sessionKey') ?>"}));
+
+                console.log('Pro connected')
+
+            });
+        }
+
+        connect();
+
+        function joinCall() {
+            var modal = $('#incomingCallModal');
+            window.location.href = '/pro/meeting/' +
+                modal.data('meetingUid');
+        }
+
+        function disconnect() {
+            if (stompClient !== null) {
+                stompClient.disconnect();
+            }
+            setConnected(false);
+            console.log("Disconnected");
+        }
+
+        function sendName() {
+            stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
+        }
+
+        function showGreeting(message) {
+            $("#greetings").append("<tr><td>" + message + "</td></tr>");
+        }
+
+        $(function () {
+            $("form").on('submit', function (e) {
+                e.preventDefault();
+            });
+            $( "#connect" ).click(function() { connect(); });
+            $( "#disconnect" ).click(function() { disconnect(); });
+            $( "#send" ).click(function() { sendName(); });
+        });
+    </script>
+
 </body>
 
-</html>
+</html>

+ 289 - 58
resources/views/meeting.blade.php

@@ -1,4 +1,4 @@
-@extends('layouts.guest-meeting')
+@extends('layouts.meeting')
 @section('content')
 
     <div id="meetingComponent">
@@ -20,18 +20,19 @@
                      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 class="avatar text-white" :style="'background: ' + guest.color">@{{ getInitials(guest.name) }}</div>
+                    <p class="font-weight-bold text-center mt-1 mb-0 text-single mx-1">@{{ guest.name }}</p>
                 </div>
-                <div v-if="!guest" class="tp-item mb-4">
-                    <img src="/images/person/guest.png" alt="" class="w-100">
+                <div v-if="guest" class="tp-item mb-4">
+                    <img src="/images/person/guest.png" alt="" class="invite-image">
                     <p class="font-weight-bold text-center mt-1">Invite Guest</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">
+                <div class="p-4 w-100 h-100 d-flex align-items-stretch justify-content-end flex-column">
                     <p class="font-weight-bold text-center text-white mt-2">Feed from @{{ activeParticipant.name }}</p>
+                    <button class="btn btn-sm btn-danger px-4 align-self-end mx-auto font-weight-bold"
+                            v-on:click="leaveCall()">Leave</button>
                 </div>
             </div>
             <div class="tp-bar">
@@ -40,9 +41,9 @@
                      :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 class="avatar text-white" :style="'background: ' + pro.color">@{{ getInitials(pro.name) }}</div>
+                        <p class="font-weight-bold text-center mt-1 mb-0 text-single mx-1">@{{ pro.name }}</p>
+                        <p v-if="pro.type" class="font-weight-normal text-center text-single mx-1">@{{ pro.type }}</p>
                     </div>
                     <div v-if="pro.status === 'connecting'">
                         <img src="/images/person/connecting.jpg" alt="" class="w-100">
@@ -52,44 +53,126 @@
                         </p>
                     </div>
                 </div>
+                <div v-if="!guest" class="tp-item mb-4">
+                    <img src="/images/person/guest.png" alt="" class="invite-image">
+                    <p class="font-weight-bold text-center mt-1">Invite Pro</p>
+                </div>
             </div>
         </div>
 
     </div>
 
     <script>
+
+        const allParticipants = {!! json_encode($participants)  !!},
+            allPros = [],
+            allGuests = [];
+        for (let i = 0; i < allParticipants.length; i++) {
+            if(allParticipants[i].guest_or_pro === 'PRO') {
+                allPros.push({
+                    id: allParticipants[i].uid,
+                    name: allParticipants[i].proName,
+                    type: 'Pro',
+                    image: '',
+                    color: '', // fill in mounted()
+                    status: 'active',
+                });
+            }
+            else {
+                allGuests.push({
+                    id: allParticipants[i].uid,
+                    name: allParticipants[i].guest_name,
+                    image: '',
+                    color: '', // fill in mounted()
+                    status: 'active',
+                })
+            }
+        }
+
         new Vue({
             el: '#meetingComponent',
             delimiters: ['@{{', '}}'],
             data: {
                 meetingID: '<?= $meetingID ?>',
-                participantID: '<?= $participantID ?>',
+                participantID: '<?= $guest ? $participantID : '' ?>',
+                socket: null,
+                stompClient: null,
                 time: 0,
                 startTime: 0,
                 started: false,
-                guest: false,
+                guest: <?= $guest ? 'true' : 'false' ?>,
                 activeType: false,
                 activeParticipant: false,
+                exitURL: '<?= $guest ? '/' : '/pro/dashboard' ?>',
+                allPastels: [
+                    '#89cff0',
+                    '#99c5c4',
+                    '#9adedb',
+                    '#aa9499',
+                    '#aaf0d1',
+                    '#b2fba5',
+                    '#b39eb5',
+                    '#bdb0d0',
+                    '#bee7a5',
+                    '#befd73',
+                    '#c1c6fc',
+                    '#c6a4a4',
+                    '#c8ffb0',
+                    '#cb99c9',
+                    '#cef0cc',
+                    '#cfcfc4',
+                    '#d6fffe',
+                    '#d8a1c4',
+                    '#dea5a4',
+                    '#deece1',
+                    '#dfd8e1',
+                    '#e5d9d3',
+                    '#e9d1bf',
+                    '#f49ac2',
+                    '#f4bfff',
+                    '#fdfd96',
+                    '#ff6961',
+                    '#ff964f',
+                    '#ff9899',
+                    '#ffb7ce',
+                    '#ca9bf7'
+                ],
+                usedPastels: [],
                 guests: [
                     {
-                        id: 1,
-                        name: 'You',
-                        image: '/images/person/you.jpg',
-                        status: 'active',
+                        id: '',
+                        name: '',
+                        image: '',
+                        color: '',
+                        status: 'connecting',
                     },
                 ],
                 pros: [
                     {
-                        id: 1,
-                        name: 'Nancy Drew',
-                        type: 'Receptionist',
-                        image: '/images/person/nancy.jpg',
-                        status: 'connecting',
+                        id: '',
+                        name: '',
+                        type: '',
+                        image: '',
+                        color: '',
+                        status: '',
                     }
                 ]
-                // pros: []
             },
             methods: {
+                getUnusedPastel: function() {
+                    for (let i = 0; i < this.allPastels.length; i++) {
+                        if(this.usedPastels.indexOf(this.allPastels[i]) === -1) {
+                            this.usedPastels.push(this.allPastels[i]);
+                            return this.allPastels[i];
+                        }
+                    }
+                    return '#fff';
+                },
+                getInitials: function(_name) {
+                    var parts = _name.split(/\s+/g);
+                    parts = parts.map(_part => _part[0]);
+                    return parts.join('');
+                },
                 setActiveView: function(_type, _participant) {
                     if(_participant.status === 'active') {
                         this.activeType = _type;
@@ -97,60 +180,208 @@
                     }
                 },
                 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');
+                },
+                ifGuestExisting: function(_id) {
+                    var matching = this.guests.filter(function(_item) {
+                        return _item.id === _id;
+                    });
+                    return !!matching.length;
+                },
+                addGuest: function() {
+                    this.guests.push({
+                        id: '',
+                        name: '',
+                        image: '',
+                        color: this.getUnusedPastel(),
+                        status: 'active',
+                    });
+                    return this.guests[this.guests.length - 1];
+                },
+                addPro: function() {
+                    this.pros.push({
+                        id: '0',
+                        name: 'Pro',
+                        type: 'Receptionist',
+                        image: '/images/person/nancy.jpg',
+                        color: this.getUnusedPastel(),
+                        status: 'connecting',
+                    });
+                    return this.pros[this.pros.length - 1];
+                },
+                onProJoined: function(_data) {
+
+                    var found = false;
+
+                    // find pro with participant id
+                    var pro = this.pros.filter(function(_pro) {
+                        return _pro.id === _data.meetingParticipantUid;
+                    });
+
+                    // 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 = _data.meetingParticipantUid;
+                    pro.name = _data.proDisplayName;
+                    pro.type = _data.hcpCategory;
+                    pro.status = 'active';
+
+                    @if($guest)
+                        var self = this;
+                        if(!this.startTime) {
+                            this.startTime = new Date().getTime();
+                            window.setInterval(function() {
+                                self.time = new Date().getTime() - self.startTime;
+                            }, 1000);
+                            this.started = true;
+                        }
+                    @endif
+
+                    // TODO: init pro stream
+                },
+                onParticipantLeft: function(_id) {
+                    this.pros = this.pros.filter(function(_row) {
+                        return _row.id !== _id;
+                    });
+                    this.guests = this.guests.filter(function(_row) {
+                        return _row.id !== _id;
+                    });
+                },
+                leaveCall: function() {
+                    this.stompClient.send("/app/meeting-participant-leave-meeting", {},
+                        JSON.stringify({
+                            meetingParticipantUid: this.participantID
+                        })
+                    );
+                    window.location.href = this.exitURL;
                 }
             },
             mounted: function() {
-                this.activeType = 'guest';
-                this.activeParticipant = this.guests[0];
+
+                this.guests = allGuests;
+                this.pros = allPros;
+
+                for (let i = 0; i < this.guests.length; i++) {
+                    this.guests[i].color = this.getUnusedPastel();
+                }
+                for (let i = 0; i < this.pros.length; i++) {
+                    this.pros[i].color = this.getUnusedPastel();
+                }
 
                 var self = this;
 
-                // after 2 seconds, connect recep
-                window.setTimeout(function() {
-                    self.pros[0].status = 'active';
-                    self.started = true;
-                    self.startTime = new Date().getTime();
-                    self.activeType = 'pro';
-                    self.activeParticipant = self.pros[0];
-                    window.setInterval(function() {
-                        self.time = new Date().getTime() - self.startTime;
-                    }, 1000);
-
-                    // after 1 second, add 2 docs in connecting state
-                    window.setTimeout(function() {
-                        self.pros.push({
-                            id: 2,
-                            name: 'Dr. Strange',
-                            type: 'Dietitian',
-                            image: '/images/person/strange.0.jpg',
-                            status: 'connecting',
+                // 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);
+
+                    @if($guest)
+
+                        // add self and set as active
+                        if(!self.ifGuestExisting('<?= $participantID ?>')) {
+                            var guest = self.addGuest();
+                            guest.id = '<?= $participantID ?>';
+                            guest.status = 'active';
+                            guest.name = 'Guest';
+                        }
+
+                        // WS Subscriptions
+                        self.stompClient.subscribe("/topic/on-pro-join-meeting", function (message) {
+                            console.log("on-pro-join-meeting:", message);
+                            self.onProJoined(JSON.parse(message.body));
+                        });
+                        self.stompClient.subscribe("/topic/on-meeting-participant-left-meeting", function (message) {
+                            console.log("/topic/on-meeting-participant-left-meeting", message);
+                            var parsed = JSON.parse(message.body);
+                            self.onParticipantLeft(parsed.meetingParticipantUid);
+                        });
+
+                        // join self
+                        self.stompClient.send("/app/meeting-participant-join-meeting", {},
+                            JSON.stringify({
+                                meetingParticipantUid: self.participantID
+                            })
+                        );
+
+                        // attempt to connect to first pro if "start"
+                        @if(request('start'))
+                            self.connectToFirstPro();
+                        @endif
+
+                    @else
+
+                        // WS subscriptions
+
+                        // self joined confirmation
+                        self.stompClient.subscribe("/user/topic/on-pro-self-join-meeting", function (message) {
+                            console.log("/user/topic/on-pro-self-join-meeting:", message);
+                            var parsed = JSON.parse(message.body);
+                            self.onProJoined(parsed);
+                            self.participantID = parsed.meetingParticipantUid;
                         });
-                        self.pros.push({
-                            id: 3,
-                            name: 'Dr. Do Little',
-                            type: 'Cardiologist',
-                            image: '/images/person/dl.jpg',
-                            status: 'connecting',
+                        self.stompClient.subscribe("/topic/on-meeting-participant-left-meeting", function (message) {
+                            console.log("/topic/on-meeting-participant-left-meeting", message);
+                            var parsed = JSON.parse(message.body);
+                            self.onParticipantLeft(parsed.meetingParticipantUid);
                         });
 
-                        // after a second, connect strange
-                        window.setTimeout(function() {
-                            self.pros[1].status = 'active';
+                        // join self
+                        self.stompClient.send("/app/pro-join-meeting", {},
+                            JSON.stringify({
+                                sessionKey: "<?= request()->cookie('sessionKey') ?>",
+                                meetingUid: "<?= $meetingID ?>",
+                            })
+                        );
 
-                            window.setTimeout(function() {
-                                self.pros[2].status = 'active';
+                        @if(!$guest)
+                        if(!self.startTime) {
+                            self.startTime = new Date().getTime();
+                            window.setInterval(function() {
+                                self.time = new Date().getTime() - self.startTime;
                             }, 1000);
+                            self.started = true;
+                        }
+                        @endif
+                    @endif
 
-                        }, 1000);
+                    // TODO: init own stream
 
-                    }, 1000);
+                    self.stompClient.subscribe("/topic/on-meeting-participant-connection-status-change", function(message){
+                        /*
+                        String messagePartipantUid;
+                        boolean isConnected;
+                         */
+                        console.log(message);
+                    });
 
-                }, 2000);
+                });
             }
         });
     </script>

+ 1 - 65
resources/views/pro/dashboard.blade.php

@@ -21,69 +21,5 @@
         <button cancel>Cancel</button>
     </form>
 </div>
-<script>
-var stompClient = null;
 
-function setConnected(connected) {
-    console.log("User is connected");
-    $("#connect").prop("disabled", connected);
-    $("#disconnect").prop("disabled", !connected);
-    if (connected) {
-        $("#conversation").show();
-    }
-    else {
-        $("#conversation").hide();
-    }
-    $("#greetings").html("");
-}
-
-function connect() {
-    console.log("connecting...");
-    var socket = new SockJS('http://localhost:8080/ws');
-    stompClient = Stomp.over(socket);
-    stompClient.connect({}, function (frame) {
-        console.log("Connected");
-        setConnected(true);
-        console.log('Connected: ' + frame);
-
-        stompClient.send("/app/update-participant-status", {}, JSON.stringify({meetingUid:"mymeetinguid", status:'mystatus'}));
-        stompClient.send("/app/pro-connect", {}, JSON.stringify({sessionKey: "5ead53eb-25ab-4d3c-b7b8-48d6bb7438bf"}))
-
-        stompClient.subscribe('/user/topic/on-participant-status-change', function (message) {
-            console.log("Participant status changed: ", message);
-        });
-        
-        stompClient.subscribe("/user/topic/on-pro-incoming-call", function(message){
-            console.log("incoming call:", message);
-        });
-    });
-}
-
-connect();
-
-function disconnect() {
-    if (stompClient !== null) {
-        stompClient.disconnect();
-    }
-    setConnected(false);
-    console.log("Disconnected");
-}
-
-function sendName() {
-    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
-}
-
-function showGreeting(message) {
-    $("#greetings").append("<tr><td>" + message + "</td></tr>");
-}
-
-$(function () {
-    $("form").on('submit', function (e) {
-        e.preventDefault();
-    });
-    $( "#connect" ).click(function() { connect(); });
-    $( "#disconnect" ).click(function() { disconnect(); });
-    $( "#send" ).click(function() { sendName(); });
-});
-</script>
-@endsection
+@endsection

+ 4 - 1
resources/views/start.blade.php

@@ -69,7 +69,10 @@
                     }, function (_data) {
                         if(_data.data && _data.data.meetingUid && _data.data.meetingParticipantUid) {
                             self.process = 'Redirecting ...';
-                            window.location.href = '/meeting/' + _data.data.meetingUid + '/' + _data.data.meetingParticipantUid;
+                            window.location.href = '/meeting/'
+                                + _data.data.meetingUid + '/'
+                                + _data.data.meetingParticipantUid
+                                + '?start=1';
                         }
                         else {
                             self.process = false;

+ 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');
 });