Compare commits

...
Sign in to create a new pull request.

22 commits

Author SHA1 Message Date
d6e88c55f9 Fixed scrolling in mobile 2025-07-04 00:21:11 +02:00
4f7ce45749 Fixed Typo in privacy url 2025-07-04 00:20:46 +02:00
226688c447 Added link to privacy policy 2025-07-02 23:56:43 +02:00
fee872ea5b Merge branch 'touch-move' 2025-06-23 16:24:54 +02:00
a8403741d3 Do not drag and drop into current and waitingroom 2025-06-23 16:24:34 +02:00
0515d7b486 Merge branch 'touch-move' 2025-06-23 15:39:28 +02:00
516780ab2c Implemented drag and drop in move 2025-06-23 15:38:32 +02:00
0c8554f494 Reworked registering clients into the initial connection 2025-06-12 23:14:52 +02:00
161a3c237c Added maximum amount of connection attempts 2025-06-11 14:07:10 +02:00
2e35ece72d Do not reconnect, if register-web fails 2025-06-11 01:06:10 +02:00
9b70f7e285 Add button to send a song to the waiting room 2025-05-23 15:47:06 +02:00
3a66bf28cd Do not show config button on admin connection. It never worked properly... 2025-05-23 15:46:35 +02:00
23f9429762 I hate JS 2024-11-15 22:11:48 +01:00
2dff2d047c If countdown falls under 0 seconds (due to delays) show a countdown of 0 seconds. 2024-11-09 23:00:42 +01:00
4a58b55b88 Tell server the metadata, that was queried so it does not need to be queried again 2024-10-14 18:21:13 +02:00
a289dde5e3 Removed the "move up" button and implemented "drag and drop" 2024-10-13 15:36:04 +02:00
877d53f8f8 Updated js dependencies and version bump 2024-10-13 15:35:29 +02:00
36f9777e2a Implemented proper configurable kiosk mode 2024-10-11 01:08:34 +02:00
ebd8d1c6e0 Fix: Entry not forwarded correctly in kiosk mode 2024-10-11 00:56:32 +02:00
47c2b9337d Fix waiting room not visible on mobile 2024-10-11 00:55:58 +02:00
5df08a0b28 added and updated documentation 2024-09-23 18:13:32 +02:00
bfecd42e4d updated dependencies 2024-09-23 18:13:17 +02:00
14 changed files with 1103 additions and 875 deletions

View file

@ -1,20 +1,7 @@
# foundation-vue
# Syng web client
This template should help get you started developing with Vue 3 in Vite.
This is the web client for [Syng](https://github.com/christofsteel/syng)
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development

1622
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "syng-web",
"version": "2.0.0",
"version": "2.0.4",
"scripts": {
"dev": "vite",
"build": "vite build",

View file

@ -34,7 +34,8 @@ const state = ref({
'uid': null,
'double_entry': {'artist': null, 'title': null, 'reason': null},
'waiting_room_policy': null,
'config': {}
'config': {},
'kiosk': false
})
onMounted(() => {
@ -73,6 +74,7 @@ function update_config(config) {
state.socket.emit("update_config", {"config": config})
close_config()
}
function setKiosk(kiosk) { state.value.kiosk = kiosk }
function search() {
var yt_checker = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
@ -92,6 +94,11 @@ function waitingRoomToQueue(uuid) {
state.socket.emit("waiting-room-to-queue", {"uuid": uuid})
}
function queueToWaitingRoom(uuid) {
console.log(uuid)
state.socket.emit("queue-to-waiting-room", {"uuid": uuid})
}
function append(entry) {
checked_append_with_name(entry, state.value.name)
}
@ -102,39 +109,39 @@ function checked_append_with_name(entry, name) {
$("#getusername").foundation("open");
} else {
$("#getusername").foundation("close");
raw_append(entry.ident, name, entry.source, state.value.uid);
raw_append(entry.ident, name, entry.source, entry.title, entry.artist, state.value.uid);
}
}
function append_anyway(ident, name, source, uid) {
function append_anyway(entry) {
$("#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 });
state.socket.emit("append-anyway", {"ident": entry.ident, "performer": entry.performer, "source": entry.source, "title": entry.title, "artist": entry.artist, "uid": null });
$("#queue-tab-title").click();
}
function raw_append(ident, name, source, uid) {
function raw_append(ident, name, source, title, artist, 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 });
state.socket.emit("append", {"ident": ident, "performer": name, "source": source, "title":title, "artist": artist,"uid": uid });
$("#queue-tab-title").click();
}
function wait_append(ident, name, source, uid) {
function wait_append(entry) {
$("#getusername").foundation("close");
$("#alreadyqueued").foundation("close");
state.socket.emit("waiting-room-append", {"ident": entry.ident, "performer": entry.performer, "source": entry.source, "title": entry.title, "artist": entry.artist, "uid": null });
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();
}
@ -173,20 +180,53 @@ function moveUp(uuid) {
state.socket.emit("move-up", {"uuid": uuid})
}
function moveTo(data) {
state.socket.emit("move-to", data)
}
function skip(uuid) {
state.socket.emit("skip", {"uuid": uuid})
}
function registerSocketEvents() {
state.socket = io(state.value.server)
state.socket = io(state.value.server,
{
auth: {
type: "web",
room: state.value.room,
secret: state.value.secret,
}
}
)
state.socket.on("search-results", (results) => {
state.value.searching = false
state.value.search.searchResults = results.results
})
state.socket.on("disconnect", (reason) => {
console.warn("Disconnected from server")
state.value.joined = false
state.value.join_msg = "Disconnected from server, please check your connection and try again."
})
state.socket.on("connect", () => { joinRoom() })
state.socket.on("connect_error", (err) => {
console.warn("Connection error:", err.message);
state.value.joined = false;
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 events, please download and " +
"install <a href='https://github.com/christofsteel/syng' target='_blank'>Syng</a>"
})
state.socket.io.on("reconnect", () => { joinRoom() })
state.socket.io.on("reconnect_error", () => {
state.value.joined = false;
})
state.socket.on("admin", (is_admin) => {
console.log("Admin status: " + is_admin)
state.value.admin = is_admin
})
state.socket.on("state", (val) => {
state.value.queue=val.queue
@ -247,7 +287,14 @@ function registerSocketEvents() {
function joinRoom() {
console.log("Joining room " + state.value.room)
state.socket.emit("register-web", {"room": state.value.room}, (response) => {
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}})
/* state.socket.emit("register-web", {"room": state.value.room}, (response) => {
if(response === true) {
localStorage.name = state.value.name
localStorage.server = state.value.server
@ -264,22 +311,17 @@ function joinRoom() {
} 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) {
"To host your own syng powered karaoke events, please download and " +
"install <a href='https://github.com/christofsteel/syng' target='_blank'>Syng</a>"
state.socket.disconnect()
setTimeout(() => connect(), 2000)
}
}
})
}) */
}
</script>
<template>
<div class="page">
<div class="row" id="main-content">
<div class="row" :id="state.kiosk ? 'main-content-kiosk' : 'main-content'">
<MobileLayout
v-show="state.is_small"
:state="state"
@ -289,7 +331,9 @@ function joinRoom() {
@skip="skip"
@skipCurrent="skipCurrent"
@moveUp="moveUp"
@moveTo="moveTo"
@waitingRoomToQueue="waitingRoomToQueue"
@queueToWaitingRoom="queueToWaitingRoom"
/>
<DesktopLayout
v-show="!state.is_small"
@ -300,7 +344,9 @@ function joinRoom() {
@skip="skip"
@skipCurrent="skipCurrent"
@moveUp="moveUp"
@moveTo="moveTo"
@waitingRoomToQueue="waitingRoomToQueue"
@queueToWaitingRoom="queueToWaitingRoom"
/>
<WelcomeReveal
v-if="!state.joined"
@ -309,11 +355,13 @@ function joinRoom() {
:joinMsg="state.join_msg"
:name="state.name"
:secret="state.secret"
:kiosk="state.kiosk"
@connect="connect"
@update:room="setRoomCode"
@update:name="setName"
@update:server="setServer"
@update:secret="setSecret"
@update:kiosk="setKiosk"
/>
<GetUserName
:current_name="state.current_name"
@ -323,11 +371,12 @@ function joinRoom() {
@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)"
@append="append_anyway"
@wait="wait_append"
@cancel="close_already_queued"
:waiting_room_policy="state.waiting_room_policy"
:double_entry="state.double_entry"
:current_entry="state.current_entry"
/>
<div class="reveal" id="msg" data-reveal>
{{ state.last_msg }}
@ -339,7 +388,7 @@ function joinRoom() {
<Footer
:name="state.name"
:admin="state.admin"
:show_name="state.show_name"
:kiosk="state.kiosk"
@update:name="setName"
@logout="emptyLocalStorageAndLogout"
@config="show_config"
@ -362,10 +411,15 @@ function joinRoom() {
height: 100%;
position: relative;
}
#main-content {
height: calc(100vh - 50px);
max-height: 100vh;
max-width: 100vw;
}
#main-content-kiosk {
height: 100vh;
max-height: 100vh;
max-width: 100vw;
}
</style>

View file

@ -1,6 +1,6 @@
<script setup>
const emits = defineEmits(["append", "wait", "cancel"]);
const props = defineProps(["double_entry", "waiting_room_policy"]);
const props = defineProps(["double_entry", "current_entry", "waiting_room_policy"]);
</script>
<template>
<div class="reveal" id="alreadyqueued" data-reveal >
@ -10,21 +10,20 @@ const props = defineProps(["double_entry", "waiting_room_policy"]);
</p>
<p>To give everyone a chance to sing, you are limited to one song in the active queue, but you can add yourself to the <b>waiting room</b>.
Songs in the waiting room will be added to the queue, once your old song leaves the queue.</p>
<p>If you think this is an error, you can
<p>If you think this is an error, you can</p>
<ul>
<li>choose another name, or</li>
<li>add it to the waiting room and talk to the organizers for manual insertion into the queue.</li>
</ul>
</p>
<div class="grid-x">
<div class="cell medium-6 small-12 btn">
<button class="button expanded" @click="$emit('wait', double_entry.reason == 'uid' ? double_entry.uid : null)">Add to waiting room</button>
<button class="button expanded" @click="$emit('wait', current_entry)">Add to waiting room</button>
</div>
<div class="cell medium-6 small-12 btn">
<button class="button expanded alert" @click="$emit('cancel')">Cancel</button>
</div>
<div v-show="waiting_room_policy && waiting_room_policy.toLowerCase() == 'optional'" class="cell medium-12 small-12 btn">
<button class="button expanded" @click="$emit('append')">Append Anyway</button>
<button class="button expanded" @click="$emit('append', current_entry)">Append Anyway</button>
</div>
</div>
</div>

View file

@ -4,7 +4,7 @@ import QueueDesktop from './QueueDesktop.vue'
import RecentDesktop from './RecentDesktop.vue'
const props = defineProps(['state']);
const emit = defineEmits(['update:searchTerm', 'search', 'append', 'skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'])
const emit = defineEmits(['update:searchTerm', 'search', 'append', 'skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue', 'queueToWaitingRoom', 'moveTo'])
</script>
@ -18,8 +18,10 @@ const emit = defineEmits(['update:searchTerm', 'search', 'append', 'skip', 'skip
:admin="state.admin"
@skip="(uuid) => $emit('skip', uuid)"
@moveUp="(uuid) => $emit('moveUp', uuid)"
@moveTo="(data) => $emit('moveTo', data)"
@skipCurrent="$emit('skipCurrent')"
@waitingRoomToQueue="(uuid) => $emit('waitingRoomToQueue', uuid)"
@queueToWaitingRoom="(uuid) => $emit('queueToWaitingRoom', uuid)"
/>
<RecentDesktop :recent="state.recent" :admin="state.admin" />
</div>

View file

@ -1,7 +1,9 @@
<script setup>
import { computed } from 'vue'
const props = defineProps(['admin', 'entry', 'current', 'firstStartedAt', 'offset', 'currentTime', 'waitingRoom'])
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'])
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue', 'queueToWaitingRoom', 'moveTo'])
var in_touch_event = false
function skip() {
if(props.current) {
@ -16,6 +18,9 @@ const eta = computed(() =>{
let startTime = new Date(props.firstStartedAt * 1000)
let playBackSeconds = (now - startTime) / 1000
let etaSeconds = Math.round(props.offset - playBackSeconds)
if (etaSeconds < 0) {
etaSeconds = 0
}
if (isNaN(etaSeconds)) {
return null
@ -24,12 +29,128 @@ const eta = computed(() =>{
}
})
const dragging = (e) => {
if (props.waitingRoom || props.current || !props.admin) {
return
}
if (e.type == 'touchstart') {
e.preventDefault()
in_touch_event = true
const list_target = e.target.closest('li')
list_target.style.opacity = 0.5
return
}
const element = e.target.closest('li')
const data = {
uuid: props.entry.uuid,
index: $(element).index()
}
e.dataTransfer.clearData()
e.dataTransfer.setData('application/json', JSON.stringify(data))
e.dataTransfer.setDragImage(element, 0, 0)
e.dataTransfer.effectAllowed = 'move'
}
const dragend = (e) => {
if (props.waitingRoom || props.current || !props.admin) {
return
}
e.preventDefault()
if (e.type == 'touchend') {
in_touch_event = false
const drop_target = document.getElementById('draggedover')
if (drop_target) {
drop_target.removeAttribute('id')
drop_target.classList.remove('draggedoverTop')
drop_target.classList.remove('draggedoverBottom')
}
e.target.closest('li').style.opacity = 1
const drop_index = $(drop_target.closest('li')).index()
const source_index = $(e.target.closest('li')).index()
if(source_index < drop_index) {
emits('moveTo', {"uuid": props.entry.uuid, "target": drop_index + 1})
} else {
emits('moveTo', {"uuid": props.entry.uuid, "target": drop_index})
}
return
}
e.target.closest('li').classList.remove('draggedoverBottom')
e.target.closest('li').classList.remove('draggedoverTop')
}
const dropped = (e) => {
if (props.waitingRoom || props.current || !props.admin) {
return
}
e.preventDefault()
e.target.closest('li').classList.remove('draggedoverBottom')
e.target.closest('li').classList.remove('draggedoverTop')
const drop_index = $(e.target.closest('li')).index()
const element = JSON.parse(e.dataTransfer.getData('application/json'))
if (element.index < drop_index) {
emits('moveTo', {"uuid": element.uuid , "target": drop_index + 1})
} else {
emits('moveTo', {"uuid": element.uuid , "target": drop_index})
}
}
const dragover = (e) => {
if (props.waitingRoom || props.current || !props.admin || !in_touch_event) {
return
}
e.preventDefault()
var source_index = 0
var target = null
if (e.type == 'touchmove') {
target = document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY)
if (!target) {
return
}
if(target.closest('li') == null || target.closest('li').classList.contains('current') || target.closest('li').classList.contains('waitingRoom')) {
return
}
source_index = $(e.target.closest('li')).index()
const old_draggedover = document.getElementById('draggedover')
if (old_draggedover) {
old_draggedover.removeAttribute('id')
old_draggedover.classList.remove('draggedoverTop')
old_draggedover.classList.remove('draggedoverBottom')
}
} else {
e.dataTransfer.dropEffect = 'move'
source_index = JSON.parse(e.dataTransfer.getData('application/json')).index
target = e.target
}
target.closest('li').id = 'draggedover'
const index = $(target.closest('li')).index()
if (index < source_index) {
target.closest('li').classList.add('draggedoverTop')
} else {
target.closest('li').classList.add('draggedoverBottom')
}
}
const dragleave = (e) => {
if (props.waitingRoom || props.current || !props.admin) {
return
}
e.preventDefault()
e.target.closest('li').classList.remove('draggedoverTop')
e.target.closest('li').classList.remove('draggedoverBottom')
}
</script>
<template>
<li :class="[{ current: current }, {waitingRoom: waitingRoom}]">
<li :class="[{ current: current }, {waitingRoom: waitingRoom}]" class="entry" @dragover="dragover" @dragleave="dragleave" @dragend="dragend" @drop="dropped" @touchmove="dragover" @touchend="dragend">
<div class="grid-x">
<div class="cell" :class="{'small-9': admin}">
<span v-if="admin && !current && !waitingRoom" class="handle"
:draggable="admin"
@dragstart="dragging"
@touchstart="dragging"
>
... ... ... ... ... ... ... ...
</span>
<span class="artist">{{ entry.artist }}</span>
<span class="title">{{ entry.title }}</span><br />
<span class="performer">{{ entry.performer }}</span>
@ -39,18 +160,24 @@ const eta = computed(() =>{
<button v-if="!waitingRoom" class="button alert fright" @click="skip">
<font-awesome-icon icon="fa-solid fa-times" />
</button>
<button
class="button success fright"
v-if="!waitingRoom && !current"
@click="$emit('queueToWaitingRoom', entry.uuid)" >
<font-awesome-icon icon="fa-solid fa-arrows-down-to-line" />
</button>
<button
class="button success fright"
v-if="waitingRoom"
@click="$emit('waitingRoomToQueue', entry.uuid)" >
<font-awesome-icon icon="fa-solid fa-arrows-up-to-line" />
</button>
<button
class="button warning fright"
v-if="!current && !waitingRoom"
@click="$emit('moveUp', entry.uuid)" >
<font-awesome-icon icon="fa-solid fa-arrow-up" />
</button>
<!-- <button -->
<!-- class="button warning fright" -->
<!-- v-if="!current && !waitingRoom" -->
<!-- @click="$emit('moveUp', entry.uuid)" > -->
<!-- <font-awesome-icon icon="fa-solid fa-arrow-up" /> -->
<!-- </button> -->
</div>
</div>
@ -58,6 +185,24 @@ const eta = computed(() =>{
</template>
<style scoped>
span.handle {
width: 25px;
display: inline-block;
overflow: hidden;
line-height: 5px;
cursor: move;
vertical-align: middle;
margin-top: auto;
margin-bottom: auto;
font-size: 12px;
font-family: sans-serif;
letter-spacing: 2px;
color: #cccccc;
height: 100%;
float: left;
touch-action: none;
text-shadow: 1px 0 1px black;
}
.current {
background-color: #008000 !important;
}
@ -76,4 +221,13 @@ const eta = computed(() =>{
content: "in ";
}
#draggedover.draggedoverTop {
border-top: 2px solid #008000;
}
#draggedover.draggedoverBottom {
border-bottom: 2px solid #008000;
}
</style>

View file

@ -1,9 +1,9 @@
<script setup>
const props = defineProps(['name', 'admin', 'show_name'])
const props = defineProps(['name', 'admin', 'kiosk'])
const emits = defineEmits(['update:name', 'logout', 'config'])
</script>
<template>
<footer>
<footer v-if="!kiosk">
Name: <span
class="userName"
@keyup.enter="(evt) => evt.target.blur()"
@ -13,7 +13,7 @@ const emits = defineEmits(['update:name', 'logout', 'config'])
{{ name }}
</span>
<div class="button alert fright" @click="$emit('logout')">Log out</div>
<div v-if="admin" class="button alert fright" @click="$emit('config')">Config</div>
<!-- <div v-if="admin" class="button alert fright" @click="$emit('config')">Config</div> -->
</footer>
</template>
<style scoped>

View file

@ -5,7 +5,7 @@ import RecentTab from './RecentTab.vue'
import TabHeader from './TabHeader.vue'
const props = defineProps(['state']);
const emit = defineEmits(['update:searchTerm', 'search', 'append', 'skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'])
const emit = defineEmits(['update:searchTerm', 'search', 'append', 'skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue', 'queueToWaitingRoom', 'moveTo'])
</script>
@ -23,11 +23,13 @@ const emit = defineEmits(['update:searchTerm', 'search', 'append', 'skip', 'skip
:queue="state.queue"
:admin="state.admin"
:waiting_room="state.waiting_room"
:waiting_room_enabled="state.waiting_room_enabled"
:waiting_room_policy="state.waiting_room_policy"
@skip="(uuid) => $emit('skip', uuid)"
@moveUp="(uuid) => $emit('moveUp', uuid)"
@moveTo="(data) => $emit('moveTo', data)"
@skipCurrent="$emit('skipCurrent')"
@waitingRoomToQueue="(uuid) => $emit('waitingRoomToQueue', uuid)"
@queueToWaitingRoom="(uuid) => $emit('queueToWaitingRoom', uuid)"
/>
<RecentTab :recent="state.recent" :admin="state.admin" />
</div>

View file

@ -2,7 +2,7 @@
import QueueInner from './QueueInner.vue'
const props = defineProps(['queue', 'waiting_room', 'admin', 'waiting_room_policy']);
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'])
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue', 'queueToWaitingRoom', 'moveTo'])
</script>
<template>
@ -15,8 +15,10 @@ const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'
:waiting_room_policy="waiting_room_policy"
@skip="(uuid) => $emit('skip', uuid)"
@moveUp="(uuid) => $emit('moveUp', uuid)"
@moveTo="(data) => $emit('moveTo', data)"
@skipCurrent="$emit('skipCurrent')"
@waitingRoomToQueue="(uuid) => $emit('waitingRoomToQueue', uuid)"
@queueToWaitingRoom="(uuid) => $emit('queueToWaitingRoom', uuid)"
/>
</div>
</template>

View file

@ -3,7 +3,7 @@ import { onMounted, reactive } from 'vue'
import Entry from './Entry.vue'
const props = defineProps(['queue', 'waiting_room', 'admin', 'waiting_room_policy']);
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'])
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue', 'queueToWaitingRoom', 'moveTo'])
let currentTime = reactive({time: Date.now()})
@ -23,12 +23,15 @@ function offset(index) {
}
return _offset
}
</script>
<template>
<div class="vsplit">
<div id="queue-list-wrapper" class="results">
<ul id="queue" class="vertical menu">
<ul id="queue" class="vertical menu" @drop="dropHandler">
<Entry
v-for="(entry, index) in queue"
:entry="entry"
@ -40,6 +43,8 @@ function offset(index) {
@skip="(uuid) => $emit('skip', uuid)"
@skipCurrent="$emit('skipCurrent')"
@moveUp="(uuid) => $emit('moveUp', uuid)"
@moveTo="(data) => $emit('moveTo', data)"
@queueToWaitingRoom="(uuid) => $emit('queueToWaitingRoom', uuid)"
/>
</ul>
<div v-show="waiting_room_policy" class="header">Waiting room</div>

View file

@ -2,7 +2,7 @@
import QueueInner from './QueueInner.vue'
const props = defineProps(['queue', 'waiting_room', 'admin', 'waiting_room_policy']);
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'])
const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue', 'queueToWaitingRoom', 'moveTo'])
</script>
<template>
@ -16,6 +16,8 @@ const emits = defineEmits(['skip', 'skipCurrent', 'moveUp', 'waitingRoomToQueue'
@moveUp="(uuid) => $emit('moveUp', uuid)"
@skipCurrent="$emit('skipCurrent')"
@waitingRoomToQueue="(uuid) => $emit('waitingRoomToQueue', uuid)"
@queueToWaitingRoom="(uuid) => $emit('queueToWaitingRoom', uuid)"
@moveTo="(data) => $emit('moveTo', data)"
/>
</div>
</template>

View file

@ -2,8 +2,8 @@
import { onMounted, onBeforeUnmount } from 'vue'
import $ from 'jquery'
const emits = defineEmits(['connect', 'update:room', 'update:name', 'update:server', 'update:secret'])
const props = defineProps(['room', 'server', 'secret', 'name', 'joinMsg'])
const emits = defineEmits(['connect', 'update:room', 'update:name', 'update:server', 'update:secret', 'update:kiosk'])
const props = defineProps(['room', 'server', 'secret', 'name', 'joinMsg', 'kiosk'])
onMounted(() => {
$(document).foundation();
@ -54,10 +54,26 @@ onBeforeUnmount(() => {
<input type="password" @input="$emit('update:secret', $event.target.value)" :value="secret" placeholder="Leave free, if not admin">
</label>
</div>
<div class="medium-12 cell">
<label>Kiosk mode
<input type="checkbox" id="kiosk" @change="$emit('update:kiosk', $event.target.checked)">
</label>
</div>
</div>
</div>
</div>
</li>
</ul>
<p class="privacy-link">
<a href="https://site.syng.rocks/privacy.html" target="_blank">Privacy Policy</a>
</p>
</div>
</template>
<style scoped>
.privacy-link {
font-size: 0.8em;
color: #666;
text-align: right;
}
</style>

View file

@ -6,7 +6,7 @@ import App from './App.vue'
import Main from './Main.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faMagnifyingGlass, faList, faChair, faArrowUp, faHistory, faPlus, faStepForward, faTimes, faArrowsUpToLine } from '@fortawesome/free-solid-svg-icons'
import { faMagnifyingGlass, faList, faChair, faArrowUp, faHistory, faPlus, faStepForward, faTimes, faArrowsUpToLine, faArrowsDownToLine } from '@fortawesome/free-solid-svg-icons'
import { faYoutube } from '@fortawesome/free-brands-svg-icons'
import 'foundation-sites/dist/css/foundation.min.css'
@ -21,6 +21,7 @@ library.add(faTimes)
library.add(faArrowUp)
library.add(faChair)
library.add(faArrowsUpToLine)
library.add(faArrowsDownToLine)
window.jQuery = jquery;
window.$ = jquery;