Skip to content

Commit

Permalink
feat(web): working radio control
Browse files Browse the repository at this point in the history
  • Loading branch information
ItsNotGoodName committed Apr 28, 2022
1 parent 30c8649 commit fe231c5
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 30 deletions.
31 changes: 20 additions & 11 deletions left/web/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
<script setup lang="ts">
import { ref } from "vue";
import { watch, ref } from "vue";
import { PAGE_HOME, PAGE_EDIT } from "./constants";
import { useWS, useRadiosQuery } from "./hooks"
import RadioStatus from "./components/RadioStatus.vue";
import RadioTitle from "./components/RadioTitle.vue";
import Preset from "./components/Preset.vue";
import RadioPower from "./components/RadioPower.vue";
import RadioName from "./components/RadioName.vue";
import DButton from "./components/DaisyUI/DButton.vue";
import RadioAudiosource from "./components/RadioAudiosource.vue";
import HamburgerMenu from "./components/HamburgerMenu.vue";
import { PAGE_HOME, PAGE_EDIT } from "./constants";
import { useWS, useRadiosQuery } from "./hooks"
import RadioVolume from "./components/RadioVolume.vue"
import RadiosDiscover from "./components/RadiosDiscover.vue";
import RadioPresets from "./components/RadioPresets.vue";
const page = ref(PAGE_HOME);
const setPage = (value: string) => {
Expand All @@ -20,7 +22,18 @@ const setPage = (value: string) => {
const radioUUID = ref("");
const { data: radios, isLoading: radiosLoading, refetch: radiosRefetch } = useRadiosQuery();
const { connecting, disconnected, reconnect, radio } = useWS(radioUUID);
const { radio, connecting, disconnected, reconnect } = useWS(radioUUID);
watch(radios, (newRadios) => {
if (newRadios) {
for (const r of newRadios) {
if (r.uuid == radioUUID.value) {
return
}
}
radioUUID.value = ""
}
});
</script>

<template>
Expand All @@ -33,7 +46,7 @@ const { connecting, disconnected, reconnect, radio } = useWS(radioUUID);
<div class="mx-5 pt-20 pb-36">
<!-- Homepage -->
<div v-if="page == PAGE_HOME && radio" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<preset :key="p.number" v-for="p of radio.presets" :selected="radio.preset_number == p.number" :preset="p" />
<radio-presets :radio="radio" />
</div>
<!-- Edit Presets -->
<div v-else-if="page == PAGE_EDIT">
Expand Down Expand Up @@ -67,11 +80,7 @@ const { connecting, disconnected, reconnect, radio } = useWS(radioUUID);
<div class="grow flex gap-2">
<hamburger-menu :page="page" :set-page="setPage" />
<div class="grow flex">
<div class="tooltip" data-tip="Discover">
<d-button class="btn-primary rounded-none rounded-l-md" aria-label="Discover">
<v-icon name="fa-search" />
</d-button>
</div>
<radios-discover class="btn-primary rounded-none rounded-l-md" />
<select v-model="radioUUID" :disabled="radiosLoading" class="select select-primary rounded-none flex-grow">
<option disabled selected value="">Select Radio</option>
<template v-if="radios">
Expand Down
1 change: 1 addition & 0 deletions left/web/src/components/HamburgerMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { PropType } from 'vue';
import { PAGE_HOME, PAGE_EDIT } from "../constants"
import DDropdownButton from './DaisyUI/DDropdownButton.vue';
Expand Down
6 changes: 4 additions & 2 deletions left/web/src/components/RadioAudiosource.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useRadioMutation } from "../hooks";
import DButton from './DaisyUI/DButton.vue';
import DDropdownButton from './DaisyUI/DDropdownButton.vue';
Expand All @@ -10,7 +11,7 @@ const { radio } = defineProps({
}
});
const { mutate, isLoading } = useRadioMutation(radio.uuid)
const { mutate, isLoading } = useRadioMutation()
</script>

<template>
Expand All @@ -21,7 +22,8 @@ const { mutate, isLoading } = useRadioMutation(radio.uuid)
<ul tabindex="0" class="menu menu-compact dropdown-content mb-2 p-2 shadow bg-base-200 rounded-box w-52 space-y-2">
<span class="mx-auto">Audio Source</span>
<d-button :loading="isLoading" :key="a" v-for="a in radio.audio_sources"
:class="{ 'btn-secondary': a == radio.audio_source }" @click="() => { mutate({ audio_source: a }) }">
:class="{ 'btn-secondary': a == radio.audio_source }"
@click="() => { mutate({ uuid: radio.uuid, audio_source: a }) }">
{{ a }}
</d-button>
</ul>
Expand Down
9 changes: 5 additions & 4 deletions left/web/src/components/RadioPower.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
<script setup lang="ts">
import { useRadioMutation } from "../hooks";
import DButton from "./DaisyUI/DButton.vue";
const { radio } = defineProps({
defineProps({
radio: {
type: Object as () => Radio,
required: true,
},
});
const { mutate, isLoading } = useRadioMutation(radio.uuid)
const { mutate, isLoading } = useRadioMutation()
</script>

<template>
<d-button v-if="radio.power" class="btn-success" aria-label="Powered ON" :loading="isLoading"
@click="() => mutate({ power: false })">
@click="() => mutate({ uuid: radio.uuid, power: false })">
<v-icon name="fa-power-off" />
</d-button>
<d-button v-else class="btn-error" aria-label="Powered OFF" :loading="isLoading"
@click="() => mutate({ power: true })">
@click="() => mutate({ uuid: radio.uuid, power: true })">
<v-icon name="fa-power-off" />
</d-button>
</template>
Expand Down
22 changes: 22 additions & 0 deletions left/web/src/components/RadioPresets.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { useRadioMutation } from "../hooks";
import Preset from "./Preset.vue";
defineProps({
radio: {
type: Object as () => Radio,
required: true,
},
});
const { mutate, isLoading } = useRadioMutation()
</script>

<template>
<preset :key="p.number" v-for="p of radio.presets" :selected="radio.preset_number == p.number" :preset="p"
:loading="isLoading" @click="() => mutate({ uuid: radio.uuid, preset: p.number })" />
</template>

<style>
</style>
4 changes: 2 additions & 2 deletions left/web/src/components/RadioStatus.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script setup lang="ts">
import DButton from "./DaisyUI/DButton.vue";
import { STATUS_CONNECTING, STATUS_PLAYING, STATUS_STOPPED } from "../constants";
import DButton from "./DaisyUI/DButton.vue";
defineProps({
radio: {
type: Object as () => Radio,
Expand Down
9 changes: 5 additions & 4 deletions left/web/src/components/RadioVolume.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
<script setup lang="ts">
import { useRadioMutation } from "../hooks";
import DButton from "./DaisyUI/DButton.vue";
const { radio } = defineProps({
defineProps({
radio: {
type: Object as () => Radio,
required: true,
},
});
const { mutate, isLoading } = useRadioMutation(radio.uuid)
const { mutate, isLoading } = useRadioMutation()
</script>

<template>
<div v-if="!radio.is_muted" class="btn-group">
<d-button class="btn-info" aria-label="Volume Down" :loading="isLoading"
@click="mutate({ volume: radio.volume - 5 })">
@click="mutate({ uuid: radio.uuid, volume: radio.volume - 5 })">
<v-icon name="fa-volume-down" />
</d-button>
<d-button class="btn-info px-0 w-10">{{ radio.volume }}%</d-button>
<d-button class="btn-info" aria-label="Volume Up" :loading="isLoading"
@click="mutate({ volume: radio.volume + 5 })">
@click="mutate({ uuid: radio.uuid, volume: radio.volume + 5 })">
<v-icon name="fa-volume-up" />
</d-button>
</div>
Expand Down
18 changes: 18 additions & 0 deletions left/web/src/components/RadiosDiscover.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script setup lang="ts">
import { useRadiosDiscoverMutation } from "../hooks";
import DButton from "./DaisyUI/DButton.vue";
const { mutate, isLoading } = useRadiosDiscoverMutation()
</script>

<template>
<div class="tooltip" data-tip="Discover">
<d-button v-bind="$attrs" aria-label="Discover" :loading="isLoading" @click="() => mutate({})">
<v-icon name="fa-search" />
</d-button>
</div>
</template>

<style>
</style>
2 changes: 1 addition & 1 deletion left/web/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { useWS } from "./ws"
export { useRadiosQuery } from "./query"
export { useRadioMutation } from "./mutation"
export { useRadioMutation, useRadiosDiscoverMutation } from "./mutation"
1 change: 1 addition & 0 deletions left/web/src/hooks/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const KEY_RADIOS = "radios"
30 changes: 25 additions & 5 deletions left/web/src/hooks/mutation.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
import { useMutation } from "vue-query";
import { useMutation, useQueryClient } from "vue-query";

import { API_URL } from "../constants"
import { KEY_RADIOS } from "./key";

export function useRadioMutation(uuid: string) {
return useMutation((req: RadioMutation) => fetch(API_URL + "/api/radio/" + uuid, { body: JSON.stringify(req), method: "PATCH" })
export function useRadioMutation() {
return useMutation((req: RadioMutation) => {
const { uuid, ...jreq } = req
return fetch(API_URL + "/api/radio/" + uuid, { body: JSON.stringify(jreq), method: "PATCH" })
.then((res) => res.json())
.then((json: APIResponse<void>) => {
if (!json.ok) {
throw new Error(json.error.message);
}
})
})
};

export function useRadiosDiscoverMutation() {
const queryClient = useQueryClient()
return useMutation((_: RadiosDiscoverMutation) => fetch(API_URL + "/api/radios", { method: "POST" })
.then((res) => res.json())
.then((json: APIResponse<void>) => {
.then((json: APIResponse<number>) => {
if (!json.ok) {
throw new Error(json.error.message);
}
}))
return json.data
}), {
onSuccess: (_: number) => {
queryClient.invalidateQueries(KEY_RADIOS)
}
})
};
3 changes: 2 additions & 1 deletion left/web/src/hooks/query.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useQuery } from "vue-query";

import { API_URL } from "../constants"
import { KEY_RADIOS } from "./key";

export function useRadiosQuery() {
return useQuery("radios", () =>
return useQuery(KEY_RADIOS, () =>
fetch(API_URL + "/api/radios")
.then((response) => response.json())
.then((json: APIResponse<Radio[]>) => {
Expand Down
1 change: 1 addition & 0 deletions left/web/src/hooks/ws.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { watch, ref, Ref } from "vue";

import { WS_URL } from "../constants"

const subscribe = (ws: WebSocket, uuid: string) => {
Expand Down
5 changes: 5 additions & 0 deletions left/web/src/types/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ declare interface APIResponse<T> {
}

declare interface RadioMutation {
uuid: string,
power?: boolean
audio_source?: string
preset?: number
volume?: number
}

declare interface RadiosDiscoverMutation {
force?: boolean
}

0 comments on commit fe231c5

Please sign in to comment.