User controls, UI improvement
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="player">
|
||||
<youtube
|
||||
ref="youtube"
|
||||
v-bind:fitParent="true"
|
||||
:video-id="roomStatus.current.linkID"
|
||||
:player-vars="playerVars"
|
||||
@playing="roomStatus.player.playing">
|
||||
</youtube>
|
||||
<hr>
|
||||
<b-field position="is-centered">
|
||||
<b-button class="playerButton" @click="play" :icon-right="roomStatus.player.playing ? 'pause' : 'play'"/>
|
||||
<b-button class="playerButton" @click="mute" icon-right="volume-mute"/>
|
||||
<b-button class="playerButton" @click="skip" icon-right="skip-next" v-if="isAdmin"/>
|
||||
<b-slider class="playerVolume" :min="0" :max="100" :value="100" @change="volume"/>
|
||||
<b-button class="playerButton" @click="play" size="is-large" :icon-right="roomStatus.player.playing ? 'pause' : 'play'" :disabled="!(isAdmin || roomSettings.userControl)"/>
|
||||
<b-button class="playerButton" @click="mute" size="is-large" icon-right="volume-mute"/>
|
||||
<b-button class="playerButton" @click="skip" size="is-large" icon-right="skip-next" :disabled="!(isAdmin || roomSettings.userControl)"/>
|
||||
<b-slider class="playerVolume" size="is-large" :min="0" :max="100" :value="100" @change="volume"/>
|
||||
</b-field>
|
||||
<b-field position="is-centered">
|
||||
<b-slider
|
||||
rounded
|
||||
size="is-medium"
|
||||
:min="0"
|
||||
:max="roomStatus.player.timeLength"
|
||||
:value="roomStatus.player.timeCode"
|
||||
:custom-formatter="value => convertTimeCode(value)"
|
||||
:disabled="!isAdmin"
|
||||
:disabled="!(isAdmin || roomSettings.userControl)"
|
||||
@change="seek"/>
|
||||
</b-field>
|
||||
<h2 class="subtitle is-6 time">{{convertTimeCode(roomStatus.player.timeCode)}} / {{convertTimeCode(roomStatus.player.timeLength)}}</h2>
|
||||
@@ -31,7 +31,6 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'Player',
|
||||
props: ['settings'],
|
||||
computed: {
|
||||
isAdmin () {
|
||||
return this.$store.state.room.admin
|
||||
@@ -39,6 +38,12 @@ export default {
|
||||
roomStatus () {
|
||||
return this.$store.state.room.roomStatus
|
||||
},
|
||||
roomSettings () {
|
||||
return this.$store.state.room.roomSettings
|
||||
},
|
||||
localSettings () {
|
||||
return this.$store.state.room.localSettings
|
||||
},
|
||||
player () {
|
||||
return this.$refs.youtube.player
|
||||
},
|
||||
@@ -66,7 +71,7 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
roomStatus: function (status) {
|
||||
if (!this.settings.playLink) {
|
||||
if (!this.localSettings.playLink) {
|
||||
this.player.pauseVideo()
|
||||
return
|
||||
}
|
||||
@@ -80,13 +85,16 @@ export default {
|
||||
async playerStateChange (event) {
|
||||
console.log('[PLAYER] Status change ' + event.data)
|
||||
console.log(await event.target.getVideoData())
|
||||
if (this.isAdmin) {
|
||||
if (this.isAdmin || this.roomSettings.userControl) {
|
||||
this.$store.dispatch('room/setCurrent', {
|
||||
playerStatus: event.data,
|
||||
timeCode: await this.player.getCurrentTime(),
|
||||
timeLength: await this.player.getDuration(),
|
||||
title: await event.target.getVideoData().title
|
||||
})
|
||||
} else {
|
||||
if (this.roomStatus.player.playing) this.player.playVideo()
|
||||
else this.player.pauseVideo()
|
||||
}
|
||||
},
|
||||
play () {
|
||||
@@ -107,7 +115,7 @@ export default {
|
||||
this.player.seekTo(time, true)
|
||||
},
|
||||
async updateTimeCode () {
|
||||
if (this.settings.playLink) this.$store.dispatch('room/setTimeCode', await this.player.getCurrentTime())
|
||||
if (this.localSettings.playLink) this.$store.dispatch('room/setTimeCode', await this.player.getCurrentTime())
|
||||
},
|
||||
convertTimeCode (timeCode) {
|
||||
var minutes = Math.round(timeCode / 60)
|
||||
@@ -136,6 +144,10 @@ export default {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.player {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.time {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-button icon-left="plus" @click="addLinkPrompt">Add link</b-button>
|
||||
<b-button v-if="roomSettings.userLink" icon-left="plus" size="is-medium" @click="addLinkPrompt">Add link</b-button>
|
||||
<hr>
|
||||
<b-table :data="roomStatus.playlist" striped hoverable default-sort="vote">
|
||||
<template slot-scope="props">
|
||||
@@ -21,9 +21,9 @@
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column>
|
||||
<b-button v-if="hasVoted(props.row)" icon-left="arrow-down-bold-outline" type="is-primary" @click="vote (props.row.link, props.row.linkID, false)"/>
|
||||
<b-button v-else icon-left="arrow-up-bold-outline" type="is-primary" @click="vote (props.row.link, props.row.linkID, true)"/>
|
||||
<b-button v-if="isAdmin" class="actionButton" icon-left="delete-forever" type="is-danger" @click="removePlay (props.row.linkID)"/>
|
||||
<b-button v-if="hasVoted(props.row)" icon-left="arrow-down-bold-outline" type="is-primary" size="is-medium" @click="vote (props.row.title, props.row.link, props.row.linkID, false)"/>
|
||||
<b-button v-else icon-left="arrow-up-bold-outline" type="is-primary" size="is-medium" @click="vote (props.row.title, props.row.link, props.row.linkID, true)"/>
|
||||
<b-button v-if="isAdmin" class="actionButton" icon-left="delete-forever" type="is-danger" size="is-medium" @click="removePlay (props.row.linkID)"/>
|
||||
</b-table-column>
|
||||
</template>
|
||||
</b-table>
|
||||
@@ -32,13 +32,16 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Palylist',
|
||||
name: 'Playlist',
|
||||
computed: {
|
||||
isAdmin () {
|
||||
return this.$store.state.room.admin
|
||||
},
|
||||
roomStatus () {
|
||||
return this.$store.state.room.roomStatus
|
||||
},
|
||||
roomSettings () {
|
||||
return this.$store.state.room.roomSettings
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -89,7 +92,7 @@ export default {
|
||||
this.$store.dispatch('room/removePlay', linkID)
|
||||
},
|
||||
async getInfos (linkID) {
|
||||
const response = await fetch('http://noembed.com/embed?format=json&' + 'url=https://www.youtube.com/watch?v=' + linkID)
|
||||
const response = await fetch('https://noembed.com/embed?format=json&' + 'url=https://www.youtube.com/watch?v=' + linkID)
|
||||
return await response.json()
|
||||
}
|
||||
}
|
||||
|
||||
58
client/src/components/Settings.vue
Normal file
58
client/src/components/Settings.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-field label="Player">
|
||||
<b-switch size="is-medium" v-model="localSettings.playLink" @input="setLocalSettings">Play link</b-switch>
|
||||
</b-field>
|
||||
<b-field>
|
||||
<b-switch size="is-medium" v-model="localSettings.externalSearch" @input="setLocalSettings">Enable Youtube search</b-switch>
|
||||
</b-field>
|
||||
<div v-if="isAdmin">
|
||||
<hr>
|
||||
<b-field label="Admin">
|
||||
<b-switch size="is-medium" v-model="roomSettings.userControl" @input="setRoomSettings">Users can control video</b-switch>
|
||||
</b-field>
|
||||
<b-field>
|
||||
<b-switch size="is-medium" v-model="roomSettings.userLink" @input="setRoomSettings">Users can add link</b-switch>
|
||||
</b-field>
|
||||
</div>
|
||||
<hr>
|
||||
<b-field label="User">
|
||||
<b-button type="is-danger" icon-right="exit-to-app" @click="leave">Leave</b-button>
|
||||
</b-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Settings',
|
||||
data () {
|
||||
return {
|
||||
localSettings: this.$store.state.room.localSettings,
|
||||
roomSettings: this.$store.state.room.roomSettings
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isAdmin () {
|
||||
return this.$store.state.room.admin
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.isAdmin) {
|
||||
this.localSettings.playLink = true
|
||||
this.setLocalSettings()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
leave () {
|
||||
this.$store.dispatch('room/leave')
|
||||
this.$router.push({ name: 'Home' })
|
||||
},
|
||||
setLocalSettings () {
|
||||
this.$store.dispatch('room/setLocalSettings', this.localSettings)
|
||||
},
|
||||
setRoomSettings () {
|
||||
this.$store.dispatch('room/setRoomSettings', this.roomSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@ import router from '@/router/index'
|
||||
|
||||
const state = {
|
||||
signalServerConnected: false,
|
||||
loginSuccess: false,
|
||||
loginSuccess: null,
|
||||
error: null,
|
||||
serverStatus: {}
|
||||
}
|
||||
@@ -23,9 +23,13 @@ const actions = {
|
||||
commit('SIGNAL_ERROR', error)
|
||||
},
|
||||
login ({ commit }, success) {
|
||||
if (success) commit('LOGIN_SUCCESS')
|
||||
// le JSON parser veut pas voir le boolean
|
||||
if (success === 'true') commit('LOGIN_SUCCESS')
|
||||
else commit('LOGIN_ERROR')
|
||||
},
|
||||
resetLogin ({ commit }) {
|
||||
commit('LOGIN_RESET')
|
||||
},
|
||||
serverStatus ({ commit }, serverStatus) {
|
||||
commit('SET_SERVERSTATUS', serverStatus)
|
||||
},
|
||||
@@ -57,6 +61,9 @@ const mutations = {
|
||||
LOGIN_ERROR (state) {
|
||||
state.loginSuccess = false
|
||||
},
|
||||
LOGIN_RESET (state) {
|
||||
state.loginSuccess = null
|
||||
},
|
||||
SET_SERVERSTATUS (state, serverStatus) {
|
||||
state.serverStatus = serverStatus
|
||||
},
|
||||
|
||||
@@ -16,6 +16,14 @@ const state = {
|
||||
voters: []
|
||||
},
|
||||
playlist: []
|
||||
},
|
||||
roomSettings: {
|
||||
userControl: false,
|
||||
userLink: true
|
||||
},
|
||||
localSettings: {
|
||||
playLink: false,
|
||||
externalSearch: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +40,13 @@ const actions = {
|
||||
setRoomStatus ({ commit }, roomStatus) {
|
||||
commit('SET_ROOMSTATUS', roomStatus)
|
||||
},
|
||||
setRoomSettings ({ commit, dispatch, state }, roomSettings) {
|
||||
commit('SET_ROOMSETTINGS', roomSettings)
|
||||
if (state.admin) dispatch('rtc/broadcast', { message: state.roomSettings, type: 'settings' }, { root: true })
|
||||
},
|
||||
setLocalSettings ({ commit }, localSettings) {
|
||||
commit('SET_LOCALSETTINGS', localSettings)
|
||||
},
|
||||
setAdmin ({ commit }) {
|
||||
commit('SET_ADMIN')
|
||||
},
|
||||
@@ -107,6 +122,12 @@ const mutations = {
|
||||
SET_ROOMSTATUS (state, roomStatus) {
|
||||
state.roomStatus = roomStatus
|
||||
},
|
||||
SET_ROOMSETTINGS (state, roomSettings) {
|
||||
state.roomSettings = roomSettings
|
||||
},
|
||||
SET_LOCALSETTINGS (state, localSettings) {
|
||||
state.localSettings = localSettings
|
||||
},
|
||||
SET_ADMIN (state) {
|
||||
state.admin = true
|
||||
},
|
||||
|
||||
@@ -195,7 +195,10 @@ function handleDataChannelMessage (event) {
|
||||
console.log('[RTC] data channel message type ' + data.type)
|
||||
switch (data.type) {
|
||||
case 'status':
|
||||
store.dispatch('room/setRoomStatus', data.message)
|
||||
if (!store.state.room.admin || store.state.room.roomSettings.userControl) store.dispatch('room/setRoomStatus', data.message)
|
||||
break
|
||||
case 'settings':
|
||||
if (!store.state.room.admin) store.dispatch('room/setRoomSettings', data.message)
|
||||
break
|
||||
case 'vote':
|
||||
store.dispatch('room/vote', { link: data.message.link, linkID: data.message.linkID, isPositive: data.message.isPositive, voterName: data.message.voterName })
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
<h1 class="subtitle is-6">Connected as {{userName}}</h1>
|
||||
<hr>
|
||||
<b-field position="is-centered">
|
||||
<b-button type="is-primary" @click="connectToRoomPrompt">Join a room</b-button>
|
||||
<b-button type="is-primary" size="is-large" @click="connectToRoomPrompt">Join a room</b-button>
|
||||
</b-field>
|
||||
<b-field position="is-centered">
|
||||
<b-button @click="isQRModalActive = true" icon-right="qrcode" size="is-large"/>
|
||||
<b-button size="is-large" icon-right="qrcode" @click="isQRModalActive = true" />
|
||||
</b-field>
|
||||
<hr>
|
||||
<b-button type="is-primary" @click="makeRoomPrompt">Make a room</b-button>
|
||||
<b-button type="is-primary" size="is-large" @click="makeRoomPrompt">Make a room</b-button>
|
||||
<hr>
|
||||
<b-button @click="changeName">Change name</b-button>
|
||||
<b-modal :active.sync="isQRModalActive" has-modal-card trap-focus>
|
||||
<QRReader v-on:get-code="connectToRoom"/>
|
||||
</b-modal>
|
||||
@@ -56,8 +58,24 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
serverConnected: function (isConnected) {
|
||||
if (!this.isLoggedIn && this.serverConnected) this.loginPrompt()
|
||||
serverConnected: async function (isConnected) {
|
||||
if (!this.isLoggedIn && this.serverConnected) {
|
||||
const name = await localStorage.getItem('name')
|
||||
if (name) this.login(name)
|
||||
else this.loginPrompt()
|
||||
}
|
||||
},
|
||||
isLoggedIn: function (success) {
|
||||
console.log('wwiguhspgus ' + success)
|
||||
// a cause de null = false
|
||||
if (success === false) {
|
||||
this.$buefy.toast.open({
|
||||
message: 'Invalid login',
|
||||
type: 'is-danger'
|
||||
})
|
||||
this.$store.dispatch('app/resetLogin')
|
||||
this.loginPrompt()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -72,7 +90,10 @@ export default {
|
||||
maxlength: 30
|
||||
},
|
||||
confirmText: 'KK',
|
||||
onConfirm: (name) => this.login(name)
|
||||
onConfirm: (name) => {
|
||||
localStorage.setItem('name', name)
|
||||
this.login(name)
|
||||
}
|
||||
})
|
||||
},
|
||||
login (name) {
|
||||
@@ -122,6 +143,10 @@ export default {
|
||||
type: 'connectRoom',
|
||||
name: code
|
||||
})
|
||||
},
|
||||
changeName () {
|
||||
localStorage.removeItem('name')
|
||||
this.loginPrompt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
<template>
|
||||
<div class="room container">
|
||||
<div class="roomTitle">
|
||||
<h1 class="title is-1">{{roomStatus.roomName}}</h1>
|
||||
<h2 class="subtitle">{{roomStatus.current.title}}</h2>
|
||||
</div>
|
||||
|
||||
<b-tabs>
|
||||
<b-tab-item label="Playlist">
|
||||
<b-tabs type="is-boxed" expanded>
|
||||
<b-tab-item label="Playlist" icon="playlist-play">
|
||||
<Playlist />
|
||||
</b-tab-item>
|
||||
|
||||
<b-tab-item label="Player" :visible="settings.showPlayer && settings.playLink">
|
||||
<Player v-bind:settings="settings"/>
|
||||
<b-tab-item label="Player" icon="youtube" :visible="localSettings.playLink">
|
||||
<Player />
|
||||
</b-tab-item>
|
||||
|
||||
<b-tab-item label="Admin" :visible="isAdmin">
|
||||
<b-tab-item label="Peers" icon="lan" :visible="isAdmin">
|
||||
<Admin />
|
||||
</b-tab-item>
|
||||
|
||||
<b-tab-item label="Invite">
|
||||
<b-tab-item label="Invite" icon="qrcode">
|
||||
<Invite v-bind:roomCode="roomStatus.roomCode"/>
|
||||
</b-tab-item>
|
||||
|
||||
<b-tab-item label="Settings">
|
||||
<b-field label="Player">
|
||||
<b-switch v-model="settings.playLink">Play link</b-switch>
|
||||
</b-field>
|
||||
<b-field>
|
||||
<b-switch v-model="settings.showPlayer" :disabled="!settings.playLink">Show player</b-switch>
|
||||
</b-field>
|
||||
<hr>
|
||||
<b-field label="User">
|
||||
<b-button type="is-danger" icon-right="exit-to-app" @click="leave">Leave</b-button>
|
||||
</b-field>
|
||||
<b-tab-item label="Settings" icon="cog">
|
||||
<Settings />
|
||||
</b-tab-item>
|
||||
|
||||
</b-tabs>
|
||||
@@ -43,6 +36,7 @@ import Invite from './../components/Invite'
|
||||
import Admin from './../components/Admin'
|
||||
import Playlist from './../components/Playlist'
|
||||
import Player from './../components/Player'
|
||||
import Settings from './../components/Settings'
|
||||
|
||||
export default {
|
||||
name: 'Room',
|
||||
@@ -50,7 +44,8 @@ export default {
|
||||
Invite,
|
||||
Admin,
|
||||
Playlist,
|
||||
Player
|
||||
Player,
|
||||
Settings
|
||||
},
|
||||
computed: {
|
||||
roomStatus () {
|
||||
@@ -64,28 +59,13 @@ export default {
|
||||
},
|
||||
isRoomLoading () {
|
||||
return this.roomStatus.roomName === ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
settings: {
|
||||
playLink: false,
|
||||
showPlayer: false
|
||||
}
|
||||
localSettings () {
|
||||
return this.$store.state.room.localSettings
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (!this.isLoggedIn) this.$router.push({ name: 'Home' })
|
||||
if (this.isAdmin) {
|
||||
this.settings.playLink = true
|
||||
this.settings.showPlayer = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
leave () {
|
||||
this.$store.dispatch('room/leave')
|
||||
this.$router.push({ name: 'Home' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -94,4 +74,9 @@ export default {
|
||||
.room {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.roomTitle {
|
||||
padding-bottom: 30px;
|
||||
padding-left: 25px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,8 +12,8 @@ import gltronic.voozik.model.BiMap;
|
||||
|
||||
@Service
|
||||
public class RoomManager implements IRoomManager{
|
||||
BiMap<String, WebSocketSession> users = new BiMap<String, WebSocketSession>();
|
||||
BiMap<String, String> rooms = new BiMap<String, String>();
|
||||
private volatile BiMap<String, WebSocketSession> users = new BiMap<String, WebSocketSession>();
|
||||
private volatile BiMap<String, String> rooms = new BiMap<String, String>();
|
||||
|
||||
public void login(WebSocketSession session, String name) throws InterruptedException, IOException {
|
||||
if (name == null) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import gltronic.voozik.business.IRoomManager;
|
||||
|
||||
@Component
|
||||
public class SocketHandler extends TextWebSocketHandler {
|
||||
List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
|
||||
private volatile List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
|
||||
|
||||
@Autowired
|
||||
IRoomManager roomManager;
|
||||
@@ -57,7 +57,7 @@ public class SocketHandler extends TextWebSocketHandler {
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
System.err.println("[WS] new connection");
|
||||
System.err.println("[WS] new connection " + this);
|
||||
sessions.add(session);
|
||||
}
|
||||
|
||||
|
||||
BIN
voozik.jar
BIN
voozik.jar
Binary file not shown.
Reference in New Issue
Block a user