Flavionel 5 年之前
父节点
当前提交
a33731d782

+ 30 - 1
resources/js/components/pages/MeetingsAppRoot.vue

@@ -34,6 +34,7 @@
 </template>
 
 <script>
+import { mapState } from "vuex";
 import VueSocketIO from "vue-socket.io"
 import SocketIO from "socket.io-client"
 
@@ -45,12 +46,27 @@ Vue.use(
 )
 
 export default {
+    props: {
+        //lobbies:
+    },
     data(){
         return {
             active_panel: [0,1,2],
-            incomingCallDetails: null
+            incomingCallDetails: null,
+            lobbies: [
+                {
+                    lobbyUid: '1',
+                    name: 'Test Lobby',
+                    description: null,
+                    isStrangerAccessible: true,
+                    isClientAccessible: true
+                }
+            ]
         }
     },
+    computed: {
+        ...mapState(['user'])
+    },
     sockets: {
         incomingCall: function(data) {
             let self = this
@@ -80,6 +96,19 @@ export default {
         let user = {}
 
         this.$socket.emit("userData", { user: this.user })
+
+        let lobbies = this.lobbies
+
+        lobbies.map((x) => {
+            x.selected_meeting = null,
+            x.meetings = []
+        })
+
+        this.$store.commit('setLobbies', lobbies)
+
+        this.$socket.emit("lobbyDataRequest", (data) => {
+            console.log(data)
+        })
     }
 }
 </script>

+ 90 - 2
resources/js/components/partials/LobbyList.vue

@@ -1,11 +1,99 @@
 <template>
   <div>
-    This is a Lobby List
+    <v-card v-for="lobby in lobbies" flat :key="lobby.id" :loading="loading">
+
+    <div class="d-flex flex-row justify-content-between">
+        <div>
+            <v-card-title>{{lobby.name}}</v-card-title>
+
+            <v-card-text>
+                <div>{{lobby.description || 'No description available.'}}</div>
+            </v-card-text>
+        </div>
+    </div>
+
+    <v-divider class="mx-4"></v-divider>
+
+    <v-list dense v-if="lobby.meetings.length">
+        <v-container class="overflow-y-auto px-0 pt-0">
+            <v-subheader class="d-flex flex-row justify-content-between">
+                <span>Meetings ({{lobby.meetings.length}})</span>
+            </v-subheader>
+        </v-container>
+        <v-container style="height: 210px" class="overflow-y-auto">
+            <v-row>
+                <v-list-item-group class="w-100" v-model="lobby.selected_meeting" color="primary">
+                    <v-list-item v-for="(item, i) in lobby.meetings" :key="i" :value="item">
+                        <v-list-item-icon>
+                            <v-icon v-text="'mdi-clock'"></v-icon>
+                        </v-list-item-icon>
+                        <v-list-item-content>
+                            <v-list-item-title v-text="item.name"></v-list-item-title>
+                            <v-tooltip top v-if="item.associates_online.length > 0">
+                                <template v-slot:activator="{ on }">
+                                    <v-list-item-subtitle
+                                        v-on="on"
+                                        v-text="item.associates_online.length > 0 ? `Associates Connected (${item.associates_online.length})` : 'No Associates Connected'"
+                                    ></v-list-item-subtitle>
+                                </template>
+                                <span v-for="associate in item.associates_online" :key="associate.UID">
+                                    {{associate.name}}
+                                    <br />
+                                </span>
+                            </v-tooltip>
+                            <v-list-item-subtitle v-else v-text="item.associates_online.length > 0 ? `Associates Connected (${item.associates_online.length})` : 'No Associates Connected'"></v-list-item-subtitle>
+                        </v-list-item-content>
+                        <v-list-item-action>
+                            <v-list-item-action-text :class="{'status':true, 'is-active':item.active_members.length > 0}" v-text="'Online: ' + item.active_members.length"></v-list-item-action-text>
+                        </v-list-item-action>
+                    </v-list-item>
+                </v-list-item-group>
+            </v-row>
+        </v-container>
+    </v-list>
+
+    <v-list v-else>
+        <v-subheader class="d-flex flex-row justify-content-between">
+            <span>Meetings ({{lobby.meetings.length}})</span>
+        </v-subheader>
+        <v-container style="height: 210px" class="overflow-y-auto">
+            <v-row>
+                <v-list-item-group class="w-100 text-center" color="primary">
+                    <v-list-item>
+                        <v-list-item-content>
+                            <v-list-item-title muted v-text="'No Meetings found for this lobby.'"></v-list-item-title>
+                        </v-list-item-content>
+                    </v-list-item>
+                </v-list-item-group>
+            </v-row>
+        </v-container>
+    </v-list>
+
+    <transition-expand>
+        <div v-if="lobby.meetings.length">
+            <v-divider class="mx-4"></v-divider>
+
+            <v-card-actions>
+                <v-btn :disabled="lobby.selected_meeting !== 0 && !lobby.selected_meeting" color="deep-purple accent-4" text @click="joinMeeting(lobby)">Join</v-btn>
+                <v-btn :disabled="lobby.selected_meeting !== 0 && !lobby.selected_meeting" color="deep-purple accent-4" text @click="inviteToMeeting(lobby)">Invite</v-btn>
+            </v-card-actions>
+        </div>
+    </transition-expand>
+</v-card>
   </div>
 </template>
 
 <script>
-export default {
+import { mapState } from "vuex";
 
+export default {
+  data(){
+    return {
+      loading: false
+    }
+  },
+  computed: {
+    ...mapState(["lobbies"]),
+  },
 }
 </script>

+ 82 - 0
resources/js/components/utils/TransitionExpand.vue

@@ -0,0 +1,82 @@
+<template>
+  <transition
+    :name="animation_name || 'expand'"
+    @enter="enter"
+    @after-enter="afterEnter"
+    @leave="leave"
+  >
+    <slot/>
+  </transition>
+</template>
+
+<script>
+export default {
+  name: 'TransitionExpand',
+  props: ['animation_name'],
+  methods: {
+    enter(element) {
+      let self = this
+      const width = getComputedStyle(element).width;
+
+      element.style.width = width;
+      element.style.position = 'absolute';
+      element.style.visibility = 'hidden';
+      element.style.height = 'auto';
+
+      const height = getComputedStyle(element).height;
+
+      element.style.width = null;
+      element.style.position = null;
+      element.style.visibility = null;
+      element.style.height = 0;
+
+      // Force repaint to make sure the
+      // animation is triggered correctly.
+      getComputedStyle(element).height;
+
+      // Trigger the animation.
+      // We use `setTimeout` because we need
+      // to make sure the browser has finished
+      // painting after setting the `height`
+      // to `0` in the line above.
+      setTimeout(() => {
+        element.style.height = height;
+      });
+
+      this.$emit('init')
+    },
+    afterEnter(element) {
+      element.style.height = 'auto';
+    },
+    leave(element) {
+      const height = getComputedStyle(element).height;
+
+      element.style.height = height;
+
+      // Force repaint to make sure the
+      // animation is triggered correctly.
+      getComputedStyle(element).height;
+
+      setTimeout(() => {
+        element.style.height = 0;
+        element.style.paddingTop = 0;
+        element.style.paddingBottom = 0;
+      });
+
+      this.$emit('init')
+    }
+  },
+  mounted() {
+    this.$emit('init')
+  }
+};
+</script>
+
+<style scoped>
+* {
+  will-change: height;
+  transform: translateZ(0);
+  backface-visibility: hidden;
+  perspective: 1000px;
+}
+</style>

+ 32 - 22
resources/js/components/vuex/index.js

@@ -18,16 +18,7 @@ export default () => new Vuex.Store({
             is_active_and_visible: true,
             isAdmin: false
         },
-        session: {
-            screenSharingAvailable: false,
-            sessionConnected: false,
-            screenSharingActive: false
-        },
-        active_menu_item: {
-            name: 'Lobbies',
-            class: 'mdi mdi-view-dashboard',
-            template: 'lobby-list'
-        },
+        lobbies: [],
         meeting: {
             id: '',
             name: '',
@@ -37,12 +28,18 @@ export default () => new Vuex.Store({
             scheduledDate: null,
             startedAt: null
         },
-        lobbies: [],
+        session: {
+            screenSharingAvailable: false,
+            sessionConnected: false,
+            screenSharingActive: false
+        },
         settings: {
             newMeetingNotificationExpanded: localStorage.getItem('newMeetingNotificationExpanded') == 'true' ? true : false
         }
     },
     mutations: {
+        /* User */
+
         setUsername: (state, data) => {
             state.user.name = data
         },
@@ -55,15 +52,15 @@ export default () => new Vuex.Store({
         setUser(state, data) {
             state.user = data
         },
-        setUserAvatarFile(state, data) {
+        /* setUserAvatarFile(state, data) {
             state.user.avatarFile = data
-        },
-        setUserActivityState: (state, data) => {
+        }, */
+        /* setUserActivityState: (state, data) => {
             state.user.is_active_and_visible = data
-        },
-        setScreenShareState: (state, data) => {
-            state.session.screenSharingActive = data
-        },
+        }, */
+
+        /* Lobbies */
+
         setLobbies(state, data) {
             state.lobbies = data
         },
@@ -78,7 +75,7 @@ export default () => new Vuex.Store({
                     m.active_members = []
                     m.associates_online = []
                     return m
-                });
+                })
             }
         },
         setLobbyActivity(state, data) {
@@ -96,6 +93,13 @@ export default () => new Vuex.Store({
             }
 
         },
+
+        /* Meetings */
+
+        setMeeting(state, data) {
+            state.meeting = data
+        },
+
         setNewMeetingInLobby(state, data) {
             let lobby = state.lobbies.filter((cur) => cur.id == parseInt(data.lobby.id))
 
@@ -107,15 +111,21 @@ export default () => new Vuex.Store({
                 })
             }
         },
-        setMeeting(state, data) {
-            state.meeting = data
-        },
+
+        /* Session OpenTok */
+
         setScreesharingAvailability(state, data) {
             state.session.screenSharingAvailable = data
         },
+        setScreenShareState: (state, data) => {
+            state.session.screenSharingActive = data
+        },
         setSessionConnectivityState(state, data) {
             state.session.sessionConnected = data
         },
+
+        /* Settings */
+
         updateSettings(state, data){
             state.settings = data
         }

+ 1 - 1
resources/js/components/widgets/CallBubble.vue

@@ -49,7 +49,7 @@
 </template>
 
 <script>
-import { mapState } from "vuex";
+import { mapState } from "vuex"
 
 export default {
     props: {

+ 26 - 0
resources/sass/app.scss

@@ -1,6 +1,9 @@
 //
 //Vuetify
 
+$darker-green: #0DBDA7;
+$orange: #F08322;
+
 @import '~vuetify/dist/vuetify.min.css';
 
 .col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col-auto, .col-lg, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-auto, .col-md, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md-auto, .col-sm, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-auto {
@@ -9,4 +12,27 @@
 
 .v-input__slot, .v-label {
     margin-bottom: 0;
+}
+
+.v-expansion-panel-content__wrap {
+    padding: 0 !important;
+}
+
+.status {
+    position: relative;
+	&:before {
+		position: absolute;
+		content: "";
+		width: 8px;
+		height: 8px;
+		border-radius: 8px;
+		left: -1rem;
+		bottom: 5px;
+		background-color: $orange;
+	}
+	&.is-active {
+        &:before {
+            background-color: $darker-green;
+        }
+	}
 }