Skip to content

Commit

Permalink
on floor display wall
Browse files Browse the repository at this point in the history
  • Loading branch information
swantzter committed Jul 13, 2024
1 parent 7b37de7 commit 6b5f1d3
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 61 deletions.
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
<button-link to="/device-stream">
Device Stream
</button-link>
<button-link to="/on-floor-wall">
On Floor Wall
</button-link>
<button-link to="/groups">
Groups
</button-link>
Expand Down
Binary file added src/assets/ec24-bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/SystemSettingsFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const newName = ref('')
User ID: <code class="bg-gray-100 px-2 rounded">{{ auth.userLoading.value ? 'Loading...' : auth.user.value?.id }}</code>
</p>

<form v-if="auth.isAuthenticated.value" class="border-l pl-4 grid grid-cols-[max-content_auto_max-content_max-content] gap-2 items-center" @submit.prevent="auth.update({ name: newName })">
<!-- <form v-if="auth.isAuthenticated.value" class="border-l pl-4 grid grid-cols-[max-content_auto_max-content_max-content] gap-2 items-center" @submit.prevent="auth.update({ name: newName })">
System name:
<text-field
:model-value="auth.user.value?.name ?? ''"
Expand All @@ -47,6 +47,6 @@ const newName = ref('')
<text-button color="orange" dense :loading="auth.userLoading.value" type="button" @click="auth.logout()">
Log out
</text-button>
</form>
</form> -->
</div>
</template>
50 changes: 50 additions & 0 deletions src/hooks/heat-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { type Ref, ref, watch } from 'vue'
import { useFetch, useTimeoutPoll } from '@vueuse/core'
import type { ServoCurrentHeatInfoConfig } from './stream-pools'

export interface ServoHeatPoolInfo {
PROGRAM: 'ON' | ''
Station: number
HeatNumber: string
Event: string
Team: string
TeamCountryCode: string
TeamCountryName: string
TeamCountryFlagUrl: string
[nameKey: `Part${number}`]: string
[lastNameKey: `Part${number}_Last`]: string
}

export function useHeatInfo (settings: Ref<ServoCurrentHeatInfoConfig | undefined>) {
const servoPollUrl = ref<string>('')

const servoCurrentHeatFetch = useFetch(servoPollUrl, {
headers: {
accept: 'application/json'
}
}, {
immediate: false
}).get().json<ServoHeatPoolInfo[]>()

const servoPoll = useTimeoutPoll(() => {
servoCurrentHeatFetch.execute()
}, 10_000, { immediate: false })

watch(() => settings.value, heatInfoConfig => {
// start by just disabling all polling
servoPoll.pause()

if (heatInfoConfig?.system === 'servo' && heatInfoConfig.competitionId != null) {
let url
try {
url = new URL(`/api/v1/competitions/${heatInfoConfig.competitionId}/info/current`, heatInfoConfig.baseUrl as string)
} catch {
return
}
servoPollUrl.value = url.href
servoPoll.resume()
}
}, { immediate: true })

return servoCurrentHeatFetch
}
13 changes: 13 additions & 0 deletions src/hooks/on-floor-wall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useLocalStorage } from '@vueuse/core'
import type { ServoCurrentHeatInfoConfig } from './stream-pools'

export interface OnFloorWallSettings {
heatInfo?: ServoCurrentHeatInfoConfig
background?: string
}

const settings = useLocalStorage<OnFloorWallSettings>('rs-on-floor-wall-settings', {})

export function useOnFloorWallSettings () {
return settings
}
4 changes: 2 additions & 2 deletions src/hooks/stream-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface StreamPool {
label?: number
}

export interface ServoPoolBackgroundsConfig {
export interface ServoCurrentHeatInfoConfig {
system: 'servo'
baseUrl: string
competitionId?: number
Expand All @@ -19,7 +19,7 @@ export interface ScreenConfig {
}

export interface DeviceStreamSettings {
poolBackgrounds?: ServoPoolBackgroundsConfig
heatInfo?: ServoCurrentHeatInfoConfig
screens?: Record<string, ScreenConfig>
}

Expand Down
7 changes: 5 additions & 2 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ const router = createRouter({
{ path: '/groups/:groupId/next-up', component: async () => import('./views/NextUp.vue'), meta: { fullscreen: true, authRequired: true } },
{ path: '/groups/:groupId/leaderboard', component: async () => import('./views/Leaderboard.vue'), meta: { fullscreen: true, authRequired: true } },

{ path: '/device-stream', component: async () => import('./views/DeviceStreamSetup.vue'), meta: { authRequired: true } },
{ path: '/device-stream', component: async () => import('./views/DeviceStreamConfig.vue'), meta: { authRequired: true } },
{ path: '/device-stream/live', component: async () => import('./views/DeviceStreamLive.vue'), meta: { fullscreen: true, authRequired: true } },

{ path: '/podium', component: async () => import('./views/PodiumConfig.vue') },
{ path: '/podium/live', component: async () => import('./views/PodiumLive.vue'), meta: { fullscreen: true } }
{ path: '/podium/live', component: async () => import('./views/PodiumLive.vue'), meta: { fullscreen: true } },

{ path: '/on-floor-wall/', component: async () => import('./views/OnFloorWallConfig.vue') },
{ path: '/on-floor-wall/:vendor/:id', component: async () => import('./views/OnFloorWallLive.vue'), meta: { fullscreen: true } }
]
})
export default router
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,20 @@
</h2>
<div>
<select-field
:model-value="settings.poolBackgrounds?.system"
:model-value="settings.heatInfo?.system"
label="Pool Backgrounds System"
:data-list="['servo']"
@update:model-value="setPoolBackgroundsSystem($event as string)"
@update:model-value="setCurrentHeatInfoSystem($event as string)"
/>
<text-field
v-if="settings.poolBackgrounds?.system === 'servo'"
v-model="settings.poolBackgrounds.baseUrl"
v-if="settings.heatInfo?.system === 'servo'"
v-model="settings.heatInfo.baseUrl"
label="Servo Scoring Base URL"
type="url"
/>
<number-field
v-if="settings.poolBackgrounds?.system === 'servo'"
v-model="settings.poolBackgrounds.competitionId"
v-if="settings.heatInfo?.system === 'servo'"
v-model="settings.heatInfo.competitionId"
label="Servo Scoring Competition ID"
:min="0"
:step="1"
Expand Down Expand Up @@ -237,14 +237,14 @@ requestShare.onDone(() => {
const settings = useDeviceStreamPools()
function setPoolBackgroundsSystem (system: string | undefined) {
if (system === 'servo' && settings.value.poolBackgrounds?.system !== 'servo') {
settings.value.poolBackgrounds = {
function setCurrentHeatInfoSystem (system: string | undefined) {
if (system === 'servo' && settings.value.heatInfo?.system !== 'servo') {
settings.value.heatInfo = {
system: 'servo',
baseUrl: 'https://scoring.ijru.sport'
}
} else {
settings.value.poolBackgrounds = undefined
settings.value.heatInfo = undefined
}
}
Expand Down
52 changes: 7 additions & 45 deletions src/views/DeviceStreamLive.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ import { type ScoreTally, type Mark } from '../helpers'
import { useDeviceStreamPools } from '../hooks/stream-pools'
import { useHead } from '@vueuse/head'
import { useRouteQuery } from '@vueuse/router'
import { useFetch, useTimeoutPoll } from '@vueuse/core'
import { useTheme, type Theme } from '../hooks/theme'
import { useTheme } from '../hooks/theme'
import DeviceNotSet from '../components/DeviceNotSet.vue'
import SpeedLiveScore from '../components/SpeedLiveScore.vue'
import TimingLiveScore from '../components/TimingLiveScore.vue'
import UnsupportedCompetitionEvent from '../components/UnsupportedCompetitionEvent.vue'
import { useHeatInfo } from '../hooks/heat-info'
useHead({
title: '📺 Device Stream (Live)'
Expand Down Expand Up @@ -152,30 +152,12 @@ function poolBgUrl (poolLabel: number | undefined) {
return poolBackgrounds.value.find(pb => `${pb.poolLabel}` === `${poolLabel}`)?.bgUrl
}
const servoPollUrl = ref<string>('')
interface ServoHeatPoolInfo {
PROGRAM: 'ON' | ''
Station: number
HeatNumber: string
Event: string
Team: string
TeamCountryCode: string
TeamCountryName: string
TeamCountryFlagUrl: string
[nameKey: `Part${number}`]: string
[lastNameKey: `Part${number}_Last`]: string
}
const hic = ref(settings.value.heatInfo)
watch(() => settings.value.heatInfo, newHeatInfo => { hic.value = newHeatInfo })
const servoCurrentHeatFetch = useFetch(servoPollUrl, {
headers: {
accept: 'application/json'
}
}, {
immediate: false
}).get().json<ServoHeatPoolInfo[]>()
const heatInfo = useHeatInfo(hic)
watch(servoCurrentHeatFetch.data, heatInfo => {
watch(heatInfo.data, heatInfo => {
if (heatInfo == null) {
poolBackgrounds.value = []
return
Expand All @@ -185,31 +167,11 @@ watch(servoCurrentHeatFetch.data, heatInfo => {
bgUrl: hi.TeamCountryFlagUrl || (hi.TeamCountryCode ? `/flags/${hi.TeamCountryCode.toLocaleLowerCase()}.svg` : undefined)
}))
})
const servoPoll = useTimeoutPoll(() => {
servoCurrentHeatFetch.execute()
}, 10_000, { immediate: false })
watch(() => settings.value.poolBackgrounds, poolBackgrounds => {
// start by just disabling all polling
servoPoll.pause()
if (poolBackgrounds?.system === 'servo' && poolBackgrounds.competitionId != null) {
let url
try {
url = new URL(`/api/v1/competitions/${poolBackgrounds.competitionId}/info/current`, poolBackgrounds.baseUrl)
} catch {
return
}
servoPollUrl.value = url.href
servoPoll.resume()
}
}, { immediate: true })
</script>
<style scoped>
.custom-grid {
grid-template-columns: repeat(v-bind(cols), minmax(0, 1fr));
grid-template-rows: repeat(v-bind(rows), minmax(0, 1fr));
}
</style>
</style>../hooks/heat-info
76 changes: 76 additions & 0 deletions src/views/OnFloorWallConfig.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<div class="mb-4 mx-auto container flex justify-between">
<div />
<button-link
:to="`/on-floor-wall/${settings.heatInfo?.system}/${settings.heatInfo?.competitionId}?theme=${theme}`"
:disabled="settings.heatInfo?.system == null || settings.heatInfo?.competitionId == null"
>
Display
</button-link>
</div>
<section class="container mx-auto">
<h2 class="text-xl">
Heat Info
</h2>
<div>
<select-field
:model-value="settings.heatInfo?.system"
label="Heat Info System"
:data-list="['servo']"
@update:model-value="setCurrentHeatInfoSystem($event as string)"
/>
<text-field
v-if="settings.heatInfo?.system === 'servo'"
v-model="settings.heatInfo.baseUrl"
label="Servo Scoring Base URL"
type="url"
/>
<number-field
v-if="settings.heatInfo?.system === 'servo'"
v-model="settings.heatInfo.competitionId"
label="Servo Scoring Competition ID"
:min="0"
:step="1"
/>
</div>
</section>
<section class="mx-auto container flex justify-center mt-8">
<div>
<photo-picker
v-model="settings.background"
label="Background image"
/>
</div>
</section>
</template>
<script lang="ts" setup>
import countries from '../assets/countries.json'
import { SelectField, NumberField, TextField, ButtonLink } from '@ropescore/components'
import { useHead } from '@vueuse/head'
import PhotoPicker from '../components/PhotoPicker.vue'
import { useTheme } from '../hooks/theme'
import { useOnFloorWallSettings } from '../hooks/on-floor-wall'
useHead({
title: 'On Floor Wall'
})
const theme = useTheme()
const settings = useOnFloorWallSettings()
const countriesList = countries.map(c => ({ text: c.name, value: c.code }))
function setCurrentHeatInfoSystem (system: string | undefined) {
if (system === 'servo' && settings.value.heatInfo?.system !== 'servo') {
settings.value.heatInfo = {
system: 'servo',
baseUrl: 'https://scoring.ijru.sport'
}
} else {
settings.value.heatInfo = undefined
}
}
</script>
Loading

0 comments on commit 6b5f1d3

Please sign in to comment.