First working POC
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { sessions, controllableSessions, selectedSession } from "$lib/stores";
|
||||
import SessionPickerModal from "./SessionPickerModal.svelte";
|
||||
|
||||
interface Props {
|
||||
size?: "sm" | "md" | "lg";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
let { size = "md", className = "" }: Props = $props();
|
||||
|
||||
let showPicker = $state(false);
|
||||
|
||||
// Size classes
|
||||
const sizeClasses = {
|
||||
sm: "w-4 h-4",
|
||||
md: "w-5 h-5",
|
||||
lg: "w-6 h-6",
|
||||
};
|
||||
|
||||
const buttonSizeClasses = {
|
||||
sm: "p-1.5",
|
||||
md: "p-2",
|
||||
lg: "p-2.5",
|
||||
};
|
||||
|
||||
function handleClick() {
|
||||
showPicker = true;
|
||||
}
|
||||
|
||||
function closePicker() {
|
||||
showPicker = false;
|
||||
}
|
||||
|
||||
// Set polling hints when component mounts/unmounts
|
||||
onMount(async () => {
|
||||
// Initial manual refresh to get sessions
|
||||
sessions.refresh();
|
||||
|
||||
// Set initial hint based on connection state
|
||||
const hint = isConnected ? "cast_active" : "cast_discovery";
|
||||
await invoke("sessions_set_polling_hint", { hint });
|
||||
});
|
||||
|
||||
onDestroy(async () => {
|
||||
// Reset to normal polling when component unmounts
|
||||
await invoke("sessions_set_polling_hint", { hint: "normal" });
|
||||
});
|
||||
|
||||
// Update polling hint when connection state changes
|
||||
$effect(() => {
|
||||
const hint = isConnected ? "cast_active" : "cast_discovery";
|
||||
invoke("sessions_set_polling_hint", { hint });
|
||||
});
|
||||
|
||||
const isConnected = $derived($selectedSession !== null);
|
||||
const sessionCount = $derived($controllableSessions.length);
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={handleClick}
|
||||
class="{buttonSizeClasses[size]} {className} rounded-lg transition-colors relative {isConnected
|
||||
? 'text-[var(--color-jellyfin)] hover:bg-[var(--color-jellyfin)]/10'
|
||||
: 'text-gray-400 hover:text-white hover:bg-white/10'}"
|
||||
title={isConnected
|
||||
? `Casting to ${$selectedSession?.deviceName}`
|
||||
: sessionCount > 0
|
||||
? `Cast to ${sessionCount} available device${sessionCount !== 1 ? 's' : ''}`
|
||||
: 'No devices available'}
|
||||
aria-label="Cast"
|
||||
>
|
||||
<!-- Cast Icon -->
|
||||
<svg class={sizeClasses[size]} fill="currentColor" viewBox="0 0 24 24">
|
||||
{#if isConnected}
|
||||
<!-- Connected cast icon -->
|
||||
<path d="M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zM21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z" />
|
||||
{:else}
|
||||
<!-- Standard cast icon -->
|
||||
<path d="M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z" />
|
||||
{/if}
|
||||
</svg>
|
||||
|
||||
<!-- Badge for available sessions count -->
|
||||
{#if !isConnected && sessionCount > 0}
|
||||
<span
|
||||
class="absolute -top-1 -right-1 w-4 h-4 bg-[var(--color-jellyfin)] text-white text-[10px] font-bold rounded-full flex items-center justify-center"
|
||||
>
|
||||
{sessionCount}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<!-- Connected indicator -->
|
||||
{#if isConnected}
|
||||
<span class="absolute bottom-0 right-0 w-2 h-2 bg-[var(--color-jellyfin)] rounded-full border-2 border-[var(--color-surface)]"></span>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<SessionPickerModal isOpen={showPicker} onClose={closePicker} />
|
||||
Reference in New Issue
Block a user