371 lines
12 KiB
Vue
371 lines
12 KiB
Vue
<script setup>
|
|
import { computed, ref, onMounted, onDeactivated } from 'vue'
|
|
import { io } from "socket.io-client"
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import $ from 'jquery'
|
|
|
|
import MobileLayout from './components/MobileLayout.vue'
|
|
import DesktopLayout from './components/DesktopLayout.vue'
|
|
import WelcomeReveal from './components/WelcomeReveal.vue'
|
|
import GetUserName from './components/GetUserName.vue'
|
|
import Footer from './components/Footer.vue'
|
|
import AlreadyQueued from './components/AlreadyQueued.vue'
|
|
import Config from './components/Config.vue'
|
|
|
|
const router = useRouter()
|
|
|
|
const state = ref({
|
|
'search': {'searchTerm': '', 'searchResults': []},
|
|
'queue': [ ],
|
|
'waiting_room': [ ],
|
|
'room': useRoute().params.room,
|
|
'name': null,
|
|
'joined': false,
|
|
'server': window.location.protocol + "//" + window.location.host + "/",
|
|
'socket': null,
|
|
'is_small': window.innerWidth < 768,
|
|
'admin': false,
|
|
'secret': null,
|
|
'current_entry': null,
|
|
'current_name': null,
|
|
'searching': false,
|
|
'last_msg': "",
|
|
'join_msg': null,
|
|
'uid': null,
|
|
'double_entry': {'artist': null, 'title': null, 'reason': null},
|
|
'waiting_room_policy': null,
|
|
'config': {}
|
|
})
|
|
|
|
onMounted(() => {
|
|
window.addEventListener("resize", (e) => { state.value.is_small = (e.target.innerWidth < 768) });
|
|
$(document).foundation();
|
|
if(localStorage.name && localStorage.name != "null"){ state.value.name = localStorage.name }
|
|
if(localStorage.server){ state.value.server = localStorage.server }
|
|
if(!(state.value.room)) {
|
|
if(localStorage.room){ state.value.room = localStorage.room }
|
|
}
|
|
if(localStorage.secret){ state.value.secret = localStorage.secret }
|
|
if(localStorage.uid){ state.value.uid = localStorage.uid }
|
|
if(state.value.server && state.value.room && state.value.name) {
|
|
connect()
|
|
}
|
|
})
|
|
|
|
function emptyLocalStorageAndLogout() {
|
|
localStorage.removeItem('server')
|
|
localStorage.removeItem('secret')
|
|
localStorage.removeItem('name')
|
|
localStorage.removeItem('room')
|
|
localStorage.removeItem('uid')
|
|
state.socket.disconnect()
|
|
state.value.joined = false
|
|
}
|
|
|
|
function setRoomCode(room) { state.value.room = room; }
|
|
function setSecret(secret) { state.value.secret = secret; }
|
|
function setName(name) { state.value.name = name; localStorage.name=name }
|
|
function setCurrentName(name) { state.value.current_name = name }
|
|
function updateName(evt) { evt.target.textContent = state.value.name;}
|
|
function setServer(server) { state.value.server = server }
|
|
function setSearchTerm(searchTerm) { state.value.search.searchTerm = searchTerm }
|
|
function update_config(config) {
|
|
state.socket.emit("update_config", {"config": config})
|
|
close_config()
|
|
}
|
|
|
|
function search() {
|
|
var yt_checker = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
|
|
if (state.value.search.searchTerm.match(yt_checker)) {
|
|
append({"ident": state.value.search.searchTerm, "source": "youtube"})
|
|
} else {
|
|
state.value.searching = true
|
|
state.socket.emit("search", {"query": state.value.search.searchTerm })
|
|
}
|
|
}
|
|
|
|
function show_config() {
|
|
state.socket.emit("show_config");
|
|
}
|
|
|
|
function waitingRoomToQueue(uuid) {
|
|
state.socket.emit("waiting-room-to-queue", {"uuid": uuid})
|
|
}
|
|
|
|
function append(entry) {
|
|
checked_append_with_name(entry, state.value.name)
|
|
}
|
|
function checked_append_with_name(entry, name) {
|
|
if(name == "" || name == null) {
|
|
state.value.current_entry = entry;
|
|
state.value.current_name = "";
|
|
$("#getusername").foundation("open");
|
|
} else {
|
|
$("#getusername").foundation("close");
|
|
raw_append(entry.ident, name, entry.source, state.value.uid);
|
|
}
|
|
}
|
|
|
|
function append_anyway(ident, name, source, uid) {
|
|
$("#getusername").foundation("close");
|
|
$("#alreadyqueued").foundation("close");
|
|
|
|
state.value.current_name = null;
|
|
state.value.current_entry = null;
|
|
state.value.double_entry = {'artist': null, 'title': null, 'reason': null};
|
|
state.socket.emit("append-anyway", {"ident": ident, "performer": name, "source": source, "uid": uid });
|
|
$("#queue-tab-title").click();
|
|
}
|
|
|
|
function raw_append(ident, name, source, uid) {
|
|
$("#getusername").foundation("close");
|
|
$("#alreadyqueued").foundation("close");
|
|
|
|
state.value.current_name = null;
|
|
state.value.current_entry = null;
|
|
state.value.double_entry = {'artist': null, 'title': null, 'reason': null};
|
|
state.socket.emit("append", {"ident": ident, "performer": name, "source": source, "uid": uid });
|
|
$("#queue-tab-title").click();
|
|
}
|
|
|
|
function wait_append(ident, name, source, uid) {
|
|
$("#getusername").foundation("close");
|
|
$("#alreadyqueued").foundation("close");
|
|
|
|
state.value.current_name = null;
|
|
state.value.current_entry = null;
|
|
state.socket.emit("waiting-room-append", {"ident": ident, "performer": name, "source": source, "uid": uid });
|
|
$("#queue-tab-title").click();
|
|
}
|
|
|
|
function close_name() {
|
|
$("#getusername").foundation("close")
|
|
state.value.current_entry = null
|
|
state.value.current_name = null
|
|
}
|
|
function close_config() {
|
|
$("#config").foundation("close")
|
|
}
|
|
|
|
function close_already_queued() {
|
|
$("#alreadyqueued").foundation("close");
|
|
state.value.current_entry = null;
|
|
state.value.current_name = null;
|
|
}
|
|
|
|
function connect() {
|
|
if(!state.value.uid || state.value.uid == "null") {
|
|
if(isSecureContext) {
|
|
state.value.uid = crypto.randomUUID();
|
|
} else {
|
|
console.log("Insecure connection detected, user ids may not be unique");
|
|
state.value.uid = Math.random();
|
|
}
|
|
}
|
|
registerSocketEvents(state.socket)
|
|
}
|
|
|
|
function skipCurrent() {
|
|
state.socket.emit("skip-current")
|
|
}
|
|
|
|
function moveUp(uuid) {
|
|
state.socket.emit("move-up", {"uuid": uuid})
|
|
}
|
|
|
|
function skip(uuid) {
|
|
state.socket.emit("skip", {"uuid": uuid})
|
|
}
|
|
|
|
function registerSocketEvents() {
|
|
state.socket = io(state.value.server)
|
|
state.socket.on("search-results", (results) => {
|
|
state.value.searching = false
|
|
state.value.search.searchResults = results.results
|
|
})
|
|
|
|
state.socket.on("connect", () => { joinRoom() })
|
|
|
|
state.socket.io.on("reconnect", () => { joinRoom() })
|
|
|
|
state.socket.on("state", (val) => {
|
|
state.value.queue=val.queue
|
|
state.value.recent=val.recent
|
|
state.value.waiting_room = val.waiting_room
|
|
state.value.waiting_room_policy = val.config.waiting_room_policy
|
|
})
|
|
|
|
state.socket.on("config", (response) => {
|
|
state.value.config=response
|
|
$("#config").foundation("open")
|
|
})
|
|
|
|
state.socket.on("update_config", (response) => {
|
|
console.log(response)
|
|
state.value.waiting_room_policy = response["waiting_room_policy"]
|
|
console.log(state)
|
|
})
|
|
|
|
state.socket.on("msg", (response) => {
|
|
state.value.last_msg = response.msg
|
|
$("#msg").foundation("open")
|
|
})
|
|
|
|
state.socket.on("ask_for_waitingroom", (response) => {
|
|
state.value.double_entry = response.old_entry;
|
|
state.value.current_entry = response.current_entry;
|
|
$("#alreadyqueued").foundation("open");
|
|
})
|
|
|
|
state.socket.on("err", (response) => {
|
|
console.log(response)
|
|
switch(response.type) {
|
|
case "QUEUE_FULL":
|
|
var prefix = "The song queue is full and ends at ";
|
|
var date = new Date(response.end_time * 1000).toLocaleString();
|
|
state.value.last_msg = prefix + date;
|
|
break;
|
|
case "JSON_MALFORMED":
|
|
state.value.last_msg = "Malformed JSON in config"
|
|
break;
|
|
case "NO_ADMIN":
|
|
state.value.last_msg = "Forbidden: Not in admin mode"
|
|
break;
|
|
case "PROFANITY":
|
|
state.value.last_msg = "Your name \"" + response.name + "\" may contain profanity, please choose another name!"
|
|
break;
|
|
case "NAME_LENGTH":
|
|
state.value.last_msg = "Your name \"" + response.name + "\" is to long (> 50 Characters), please choose another name!"
|
|
break;
|
|
default:
|
|
state.value.last_msg = "Unknown Error";
|
|
break;
|
|
}
|
|
$("#msg").foundation("open")
|
|
})
|
|
}
|
|
|
|
function joinRoom() {
|
|
console.log("Joining room " + state.value.room)
|
|
state.socket.emit("register-web", {"room": state.value.room}, (response) => {
|
|
if(response === true) {
|
|
localStorage.name = state.value.name
|
|
localStorage.server = state.value.server
|
|
localStorage.room = state.value.room
|
|
localStorage.secret = state.value.secret
|
|
localStorage.uid = state.value.uid
|
|
state.value.joined = true
|
|
router.push({name: "room", params: {room: state.value.room}})
|
|
if (state.value.secret) {
|
|
state.socket.emit("register-admin", {"secret": state.value.secret}, (response) => {
|
|
state.value.admin = response
|
|
})
|
|
}
|
|
} else {
|
|
state.value.join_msg = "<strong>No such room!</strong> <br/>" +
|
|
"Please use the correct room code your organizer provided you.<br/>" +
|
|
"To host your own syng powered karaoke parties, please download and " +
|
|
"install <a href='https://git.k-fortytwo.de/christofsteel/syng2.git' " +
|
|
"target='_blank'>Syng</a> and run it with <pre>syng-client " +
|
|
state.value.server + "</pre>"
|
|
if(state.value.joined) {
|
|
state.socket.disconnect()
|
|
setTimeout(() => connect(), 2000)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="page">
|
|
<div class="row" id="main-content">
|
|
<MobileLayout
|
|
v-show="state.is_small"
|
|
:state="state"
|
|
@update:searchTerm="setSearchTerm"
|
|
@search="search"
|
|
@append="append"
|
|
@skip="skip"
|
|
@skipCurrent="skipCurrent"
|
|
@moveUp="moveUp"
|
|
@waitingRoomToQueue="waitingRoomToQueue"
|
|
/>
|
|
<DesktopLayout
|
|
v-show="!state.is_small"
|
|
:state="state"
|
|
@update:searchTerm="setSearchTerm"
|
|
@search="search"
|
|
@append="append"
|
|
@skip="skip"
|
|
@skipCurrent="skipCurrent"
|
|
@moveUp="moveUp"
|
|
@waitingRoomToQueue="waitingRoomToQueue"
|
|
/>
|
|
<WelcomeReveal
|
|
v-if="!state.joined"
|
|
:room="state.room"
|
|
:server="state.server"
|
|
:joinMsg="state.join_msg"
|
|
:name="state.name"
|
|
:secret="state.secret"
|
|
@connect="connect"
|
|
@update:room="setRoomCode"
|
|
@update:name="setName"
|
|
@update:server="setServer"
|
|
@update:secret="setSecret"
|
|
/>
|
|
<GetUserName
|
|
:current_name="state.current_name"
|
|
:current_entry="state.current_entry"
|
|
@update:currentName="setCurrentName"
|
|
@append="checked_append_with_name(state.current_entry, state.current_name)"
|
|
@close_name="close_name"
|
|
/>
|
|
<AlreadyQueued
|
|
@append="append_anyway(state.current_entry.ident, state.name ? state.name : state.current_name, state.current_entry.source, state.uid)"
|
|
@wait="(uid) => wait_append(state.current_entry.ident, state.name ? state.name : state.current_name, state.current_entry.source, null)"
|
|
@cancel="close_already_queued"
|
|
:waiting_room_policy="state.waiting_room_policy"
|
|
:double_entry="state.double_entry"
|
|
/>
|
|
<div class="reveal" id="msg" data-reveal>
|
|
{{ state.last_msg }}
|
|
<button class="close-button" data-close aria-label="Close modal" type="button">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<Footer
|
|
:name="state.name"
|
|
:admin="state.admin"
|
|
:show_name="state.show_name"
|
|
@update:name="setName"
|
|
@logout="emptyLocalStorageAndLogout"
|
|
@config="show_config"
|
|
/>
|
|
<Config :config="state.config" @update_config="update_config" @close="close_config"/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.page {
|
|
height:100vh;
|
|
background: url('assets/syng.png') fixed;
|
|
background-color: #8a8a8a;
|
|
background-size: auto 50%;
|
|
background-repeat: no-repeat;
|
|
background-position: center;
|
|
}
|
|
|
|
.page {
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
|
|
#main-content {
|
|
height: calc(100vh - 50px);
|
|
max-height: 100vh;
|
|
max-width: 100vw;
|
|
}
|
|
</style>
|