Something went wrong. Try again.
a simple but full-featured web player for OpenSubsonic music servers (Navidrome, Gonic, Ampache, etc) devins.page/tinysub
svelte typescript javascript subsonic gonic navidrome
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152<script lang="ts">import { getSourceApi } from "../client.svelte.js";import { t } from "../languages/translate.svelte.js";import { lib, loadRadioStations } from "../library.svelte.js";import { ui } from "../ui.svelte.js";import Modal from "./Modal.svelte";
let isSaving = $state(false);let isDeleting = $state(false);let deleteCountdown = $state(0);const isEditing = $derived(ui.stationForm?.mode === "edit");
$effect(() => { if (!ui.confirmDeleteStation) return; deleteCountdown = 2; const interval = setInterval(() => { deleteCountdown--; if (deleteCountdown <= 0) clearInterval(interval); }, 1000); return () => clearInterval(interval);});
async function doSave() { const form = ui.stationForm; if (!form) return; isSaving = true; const name = form.name; const streamUrl = form.streamUrl; const homepageUrl = form.homepageUrl || undefined; const sourceApi = getSourceApi(form.sourceId); try { if (form.mode === "edit") { const id = form.id ?? ""; await sourceApi.updateInternetRadioStation( id, streamUrl, name, homepageUrl, ); const station = lib.radioStations.find((item) => item.id === id); if (station) { station.name = name; station.streamUrl = streamUrl; station.homePageUrl = homepageUrl; } } else { await sourceApi.createInternetRadioStation(streamUrl, name, homepageUrl); await loadRadioStations(form.sourceId); } ui.stationForm = null; } catch (err) { console.error("failed to save station:", err); alert(t("message.failed_save")); } finally { isSaving = false; }}
async function doDelete() { if (!ui.confirmDeleteStation) return; isDeleting = true; try { await getSourceApi( ui.confirmDeleteStation.sourceId, ).deleteInternetRadioStation(ui.confirmDeleteStation.id); await loadRadioStations(ui.confirmDeleteStation.sourceId); ui.confirmDeleteStation = null; } catch (err) { console.error("failed to delete station:", err); alert(t("message.failed_delete")); } finally { isDeleting = false; }}</script>
<Modal open={!!ui.stationForm} onclose={() => (ui.stationForm = null)} title={isEditing ? t("action.update_station") : t("action.new_station")}> {#if ui.stationForm} <section class="modal-section"> <div class="field"> <label for="station-name">{t("common.name")}</label> <input id="station-name" type="text" bind:value={ui.stationForm.name}> </div> <div class="field"> <label for="station-stream">{t("action.stream_url")}</label> <input id="station-stream" type="url" bind:value={ui.stationForm.streamUrl} > </div> <div class="field"> <label for="station-homepage">{t("action.homepage_url")}</label> <input id="station-homepage" type="url" bind:value={ui.stationForm.homepageUrl} > </div> <div class="actions"> <button class="native" type="button" onclick={doSave} disabled={isSaving || !ui.stationForm.name.trim() || !ui.stationForm.streamUrl.trim()} > {isSaving ? t("common.busy") : isEditing ? t("common.update") : t("common.create")} </button> </div> </section> {/if}</Modal>
<Modal open={!!ui.confirmDeleteStation} onclose={() => (ui.confirmDeleteStation = null)} title={t("action.delete_station")}> {#if ui.confirmDeleteStation} <section class="modal-section"> <p> {t("message.delete_confirm", { name: ui.confirmDeleteStation.name })} </p> <p class="warn">{t("message.undo_warning")}</p> <div class="actions"> <button class="native" type="button" onclick={doDelete} disabled={isDeleting || deleteCountdown > 0} > {isDeleting ? t("common.busy") : deleteCountdown > 0 ? `${t("common.delete")} (${deleteCountdown})` : t("common.delete")} </button> </div> </section> {/if}</Modal>