Refactor messages, improve mobile accessibility

- Split messages into separate, reusable components
- Move messages to below video on mobile
- Size player to 16x9 on mobile
- Disable send button if no message
- Add icon to send button
- Switch message field to single-line for compactness
This commit is contained in:
David Roizenman 2019-06-06 05:49:42 -04:00
parent 3ff80a1c63
commit ecde1b7005
6 changed files with 177 additions and 113 deletions

View File

@ -318,6 +318,13 @@ video.vjs-tech,
padding-top: 0px;
}
/* Make player 16x9 relative to viewport width on mobile */
@media screen and (max-width: 1264px) {
.ptplayer .video-js {
height: calc(0.5625 * 100vw);
}
}
.ptplayer .vjs_video_1177-dimensions.vjs-fluid {
padding-top: 0;
}

View File

@ -14,48 +14,51 @@
:initialOffset="offset"
:createdAt="playerCreatedAt"
></videoplayer>
<div v-if="playingMetadata && chosenServer">
<transition name="fade">
<div v-show="hovered">
<v-layout row wrap style="position: absolute; top: 0; left: 0; z-index: 2" class="pa-3 hidden-xs-only">
<img :src="thumbUrl" class="elevation-20" style="height: 80px; width: auto; vertical-align: middle; margin-left: auto; margin-right: auto;" />
<v-flex class="pl-3">
<v-container class="pa-0" fill-height>
<v-layout column wrap justify-space-apart>
<v-flex>
<h1>{{ getTitle(playingMetadata) }}</h1>
</v-flex>
<v-flex>
<h3>{{ getUnder(playingMetadata) }}</h3>
</v-flex>
<v-flex>
<h5>Playing from {{ chosenServer.name }}</h5>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</v-layout>
<v-layout row wrap style="position: absolute; top: 0; right: 0; z-index: 2" class="pa-3 hidden-xs-only">
<v-flex class="text-xs-right pa-1">
<div class="hidden-xs-only">
<v-tooltip bottom color="accent" v-if="me && me.role !== 'host'">
<v-icon slot="activator" color="white" class="clickable" :disabled="manualSyncQueued" v-on:click="doManualSync">compare_arrows</v-icon>
Manual Sync
</v-tooltip>
<v-icon slot="activator" color="white" class="clickable pl-3" v-on:click="dialog = !dialog">settings</v-icon>
<router-link to="/browse" slot="activator">
<v-icon color="white" class="pl-3" v-on:click.native="stopPlayback()">close</v-icon>
</router-link>
</div>
</v-flex>
</v-layout>
<v-layout row wrap class="hoverBar" style="height: 120px; width: 100%; pointer-events: none; position: absolute; top: 0;">
<v-flex xs12>
</v-flex>
</v-layout>
</div>
</transition>
</div>
</div>
<div v-if="playingMetadata && chosenServer">
<transition name="fade">
<div v-show="hovered">
<v-layout row wrap style="position: absolute; top: 0; left: 0; z-index: 2" class="pa-3 hidden-xs-only">
<img :src="thumbUrl" class="elevation-20" style="height: 80px; width: auto; vertical-align: middle; margin-left: auto; margin-right: auto;" />
<v-flex class="pl-3">
<v-container class="pa-0" fill-height>
<v-layout column wrap justify-space-apart>
<v-flex>
<h1>{{ getTitle(playingMetadata) }}</h1>
</v-flex>
<v-flex>
<h3>{{ getUnder(playingMetadata) }}</h3>
</v-flex>
<v-flex>
<h5>Playing from {{ chosenServer.name }}</h5>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</v-layout>
<v-layout row wrap style="position: absolute; top: 0; right: 0; z-index: 2" class="pa-3 hidden-xs-only">
<v-flex class="text-xs-right pa-1">
<div class="hidden-xs-only">
<v-tooltip bottom color="accent" v-if="me && me.role !== 'host'">
<v-icon slot="activator" color="white" class="clickable" :disabled="manualSyncQueued" v-on:click="doManualSync">compare_arrows</v-icon>
Manual Sync
</v-tooltip>
<v-icon slot="activator" color="white" class="clickable pl-3" v-on:click="dialog = !dialog">settings</v-icon>
<router-link to="/browse" slot="activator">
<v-icon color="white" class="pl-3" v-on:click.native="stopPlayback()">close</v-icon>
</router-link>
</div>
</v-flex>
</v-layout>
<v-layout row wrap class="hoverBar" style="height: 120px; width: 100%; pointer-events: none; position: absolute; top: 0;">
<v-flex xs12>
</v-flex>
</v-layout>
</div>
</transition>
</div>
<div class="messages-wrapper" v-if="$vuetify.breakpoint.mdAndDown">
<messages :ptRoom="'room'"></messages>
</div>
<v-dialog v-model="dialog" width="350">
<v-card>
@ -106,9 +109,9 @@
</v-card-actions>
</v-card>
</v-dialog>
<v-layout v-if="playingMetadata && chosenServer" justify-center align-center row class="pa-3 hidden-sm-and-up">
<v-layout v-if="playingMetadata && chosenServer" justify-center row class="pa-3 hidden-sm-and-up">
<v-flex xs12>
<v-layout row wrap align-center justify-start>
<v-layout row wrap>
<img :src="thumbUrl" class="elevation-20" style="height: 80px; width: auto; vertical-align: middle; margin-left: auto; margin-right: auto" />
<v-flex class="pl-2">
<v-container class="pa-0" fill-height>
@ -144,8 +147,18 @@
</div>
</template>
<style scoped>
.messages-wrapper {
height: calc(100vh - (0.5625 * 100vw) - 150px);
}
.is-fullscreen .messages-wrapper {
height: calc(100vh - (0.5625 * 100vw));
}
</style>
<script>
import videoplayer from './ptplayer/videoplayer.vue';
import messages from '@/components/messages';
const plexthumb = require('./plexbrowser/plexthumb.vue');
@ -155,7 +168,7 @@ const parseXMLString = require('xml2js').parseString;
export default {
name: 'ptplayer',
components: {
videoplayer, plexthumb,
videoplayer, plexthumb, messages,
},
mounted() {
// Check if we have params

View File

@ -18,7 +18,7 @@
@statechanged="playerStateChanged($event)"
@ready="playerReadied"
style="background-color:transparent !important; min-height: 75vh"
style="background-color:transparent !important;"
class="ptplayer"
>
</video-player>

View File

@ -0,0 +1,30 @@
<template>
<v-flex align-center xs12 class="pb-1">
<v-layout row wrap justify-start>
<v-flex xs3 class="text-xs-center">
<img :src="message.user.thumb || message.user.avatarUrl" style="width: 36px; height: 36px; border-radius: 50%" />
</v-flex>
<v-flex xs9 class="pr-2">
<v-layout row wrap>
<v-flex><b style="opacity:1;font-size:80%; float:left"> {{ message.user.username }}</b></v-flex>
<v-flex><span style="opacity:0.6;font-size:60%; float:right"> {{ message.time}}</span></v-flex>
<v-flex xs12>
<div style="opacity:0.8;color:white;font-size:90%; max-width: 100%; word-wrap: break-word"> {{ message.msg }}</div>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
<v-layout row class="pt-1">
<v-divider style="opacity: 0.6"></v-divider>
</v-layout>
</v-flex>
</template>
<script>
export default {
props: ['message'],
methods: {
}
}
</script>

View File

@ -0,0 +1,77 @@
<template>
<v-layout row wrap style="height: 100%;">
<v-flex xs12 style="height: calc(100% - 96px)">
<v-divider class="hidden-md-and-down"></v-divider>
<v-layout row wrap id="chatbox" v-if="messages.length > 0" style="max-height: 100%; overflow-y: scroll">
<message :message="msg" :id="getMsgId(msg)" v-for="(msg, index) in messages" :key="index"></message>
</v-layout>
<v-subheader v-else>Say something to the room down below :)</v-subheader>
</v-flex>
<v-flex xs12>
<v-text-field
prepend-icon="message"
:label="'Send a message to ' + '#' + ptRoom"
hide-details
single-line
class="ma-0 ml-1 pr-1 wideinput"
v-on:keyup.enter.native="sendMessage()"
v-model="messageToBeSent"
></v-text-field>
<v-btn block color="primary" @click="sendMessage()" :disabled="messageToBeSent.length === 0">Send<v-icon right>send</v-icon></v-btn>
</v-flex>
</v-layout>
</template>
<script>
import message from '@/components/message';
import fscreen from 'fscreen';
import { mapActions, mapGetters } from 'vuex';
export default {
components: {
message
},
props: ['ptRoom'],
data() {
return {
messageToBeSent: '',
}
},
watch: {
messages() {
const options = {
container: '#chatbox',
easing: 'linear',
duration: 1,
cancelable: false,
};
console.info(this.$vuetify.breakpoint)
this.$scrollTo('#lastMessage', 5, options);
},
},
computed: {
messages() {
return this.$store.getters.getMessages;
},
chatBoxMessage() {
return `Message ${this.$store.getters.getRoom}`;
},
},
methods: {
getMsgId(msg) {
if (this.messages && msg === this.messages[this.messages.length - 1]) {
return 'lastMessage';
}
},
sendMessage() {
if (this.messageToBeSent === '') {
return;
}
console.log(`We should send this message: ${this.messageToBeSent}`);
this.$store.dispatch('sendNewMessage', this.messageToBeSent);
this.messageToBeSent = '';
},
},
}
</script>

View File

@ -92,44 +92,7 @@
</v-list>
</v-flex>
<v-flex xs12 style="position: relative; height: 50vh; max-height: 50vh">
<v-layout row wrap style="height: 100%">
<v-flex style="height: calc(100% - 96px); max-height: calc(100% - 96px)">
<v-divider></v-divider>
<v-subheader>Messages</v-subheader>
<v-layout row wrap id="chatbox" style="max-height: calc(100% - 48px); overflow-y: auto">
<v-flex align-center xs12 class="pb-1" :id="getMsgId(msg)" v-for="(msg, index) in messages" :key="index">
<v-layout row wrap justify-start>
<v-flex xs3 class="text-xs-center">
<img :src="msg.user.thumb || msg.user.avatarUrl" style="width: 36px; height: 36px; border-radius: 50%" />
</v-flex>
<v-flex xs9 class="pr-2">
<v-layout row wrap>
<v-flex><b style="opacity:1;font-size:80%; float:left"> {{ msg.user.username }}</b></v-flex>
<v-flex><span style="opacity:0.6;font-size:60%; float:right"> {{ msg.time}}</span></v-flex>
<v-flex xs12>
<div style="opacity:0.8;color:white;font-size:90%; max-width: 100%; word-wrap: break-word"> {{ msg.msg }}</div>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
<v-layout row class="pt-1">
<v-divider style="opacity: 0.6"></v-divider>
</v-layout>
</v-flex>
</v-layout>
</v-flex>
<v-flex>
<v-text-field
prepend-icon="message"
:label="'Send a message to ' + '#' + ptRoom"
hide-details
class="ma-0 ml-1 pr-1 wideinput"
v-on:keyup.enter.native="sendMessage()"
v-model="messageToBeSent"
></v-text-field>
<v-btn block color="primary" @click="sendMessage()">Send</v-btn>
</v-flex>
</v-layout>
<messages :ptRoom="ptRoom" v-if="$vuetify.breakpoint.lgAndUp"></messages>
</v-flex>
</v-layout>
</v-container>
@ -139,12 +102,14 @@
import { mapActions, mapGetters } from 'vuex';
import messages from '@/components/messages.vue';
export default {
components: {
messages
},
data() {
return {
messageToBeSent: '',
lastRecievedUpdate: new Date().getTime(),
now: new Date().getTime(),
@ -157,15 +122,6 @@ export default {
}, 250);
},
watch: {
messages() {
const options = {
container: '#chatbox',
easing: 'linear',
duration: 1,
cancelable: false,
};
this.$scrollTo('#lastMessage', 5, options);
},
ptUsers: {
deep: true,
handler() {
@ -245,9 +201,6 @@ export default {
}
return `${count} users`;
},
chatBoxMessage() {
return `Message ${this.$store.getters.getRoom}`;
},
playercount() {
if (this.$store.state.plex && this.$store.state.plex.gotDevices) {
return `(${this.$store.state.plex.clients.length})`;
@ -269,9 +222,6 @@ export default {
serverDelay() {
return Math.round(this.$store.state.synclounge.commands[Object.keys(this.$store.state.synclounge.commands).length - 1].difference);
},
messages() {
return this.$store.getters.getMessages;
},
difference() {
return Math.abs(this.now - this.lastRecievedUpdate);
},
@ -331,11 +281,6 @@ export default {
}
return perc;
},
getMsgId(msg) {
if (this.messages && msg === this.messages[this.messages.length - 1]) {
return 'lastMessage';
}
},
getCurrent(user) {
if (isNaN(user.time) || user.time === 0 || !user.time) {
return this.getTimeFromMs(0);
@ -360,14 +305,6 @@ export default {
}
return 'Nothing';
},
sendMessage() {
if (this.messageToBeSent === '') {
return;
}
console.log(`We should send this message: ${this.messageToBeSent}`);
this.$store.dispatch('sendNewMessage', this.messageToBeSent);
this.messageToBeSent = '';
},
playerState(user) {
if (user.playerState) {
if (user.playerState === 'stopped') {