mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-11 18:19:58 +00:00
Add media controls (#229)
This commit is contained in:
179
ext/src/ui/popup/Receiver.svelte
Normal file
179
ext/src/ui/popup/Receiver.svelte
Normal file
@@ -0,0 +1,179 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
|
||||
import { PlayerState } from "../../cast/sdk/media/enums";
|
||||
import { SenderMediaMessage, SenderMessage } from "../../cast/sdk/types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
import { Port } from "../../messaging";
|
||||
|
||||
import LoadingIndicator from "../LoadingIndicator.svelte";
|
||||
import ReceiverMedia from "./ReceiverMedia.svelte";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
cast: { device: ReceiverDevice };
|
||||
stop: { device: ReceiverDevice };
|
||||
}>();
|
||||
|
||||
export let port: Nullable<Port>;
|
||||
|
||||
/** Whether there are sessions being established for any receiver. */
|
||||
export let isAnyConnecting: boolean;
|
||||
/** Whether the selected media type is available for this receiver. */
|
||||
export let isMediaTypeAvailable: boolean;
|
||||
|
||||
/** Receiver device to display. */
|
||||
export let device: ReceiverDevice;
|
||||
|
||||
/** Current receiver application (if available) */
|
||||
$: application = device.status?.applications?.[0];
|
||||
/** Current media status (if available) */
|
||||
$: mediaStatus = device.mediaStatus;
|
||||
|
||||
let isExpanded = false;
|
||||
let isConnecting = false;
|
||||
|
||||
function sendReceiverMessage(
|
||||
partialMessage: DistributiveOmit<SenderMessage, "requestId">
|
||||
) {
|
||||
const message: SenderMessage = {
|
||||
...partialMessage,
|
||||
requestId: 0
|
||||
};
|
||||
|
||||
port?.postMessage({
|
||||
subject: "receiverSelector:receiverMessage",
|
||||
data: { deviceId: device.id, message }
|
||||
});
|
||||
}
|
||||
function sendMediaMessage(
|
||||
partialMessage: DistributiveOmit<
|
||||
SenderMediaMessage,
|
||||
"requestId" | "mediaSessionId"
|
||||
>
|
||||
) {
|
||||
if (!device.mediaStatus) return;
|
||||
|
||||
const message: SenderMediaMessage = {
|
||||
...(partialMessage as any),
|
||||
requestId: 0,
|
||||
mediaSessionId: device.mediaStatus.mediaSessionId
|
||||
};
|
||||
|
||||
port?.postMessage({
|
||||
subject: "receiverSelector:mediaMessage",
|
||||
data: { deviceId: device.id, message }
|
||||
});
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
sendMediaMessage({
|
||||
type: "GET_STATUS"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<li class="receiver">
|
||||
<div class="receiver__details">
|
||||
<div class="receiver__name">
|
||||
{device.friendlyName}
|
||||
</div>
|
||||
{#if application && !application.isIdleScreen}
|
||||
<div class="receiver__status">
|
||||
<span class="receiver__app-name">
|
||||
{application.displayName}
|
||||
</span>
|
||||
{#if application.statusText !== application.displayName}
|
||||
· {application.statusText}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if application && !application.isIdleScreen}
|
||||
<button
|
||||
class="receiver__stop-button"
|
||||
on:click={() => dispatch("stop", { device })}
|
||||
>
|
||||
{_("popupStopButtonTitle")}
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="receiver__cast-button"
|
||||
disabled={isConnecting || isAnyConnecting || !isMediaTypeAvailable}
|
||||
on:click={() => {
|
||||
isConnecting = true;
|
||||
dispatch("cast", { device });
|
||||
}}
|
||||
>
|
||||
{#if isConnecting}
|
||||
{_("popupCastingButtonTitle", "")}<LoadingIndicator />
|
||||
{:else}
|
||||
{_("popupCastButtonTitle")}
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="receiver__expand-button ghost"
|
||||
class:receiver__expand-button--expanded={isExpanded}
|
||||
title={_("popupShowDetailsTitle")}
|
||||
disabled={!mediaStatus}
|
||||
on:click={() => {
|
||||
isExpanded = !isExpanded;
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if isExpanded}
|
||||
<div class="receiver__expanded">
|
||||
{#if mediaStatus}
|
||||
<ReceiverMedia
|
||||
status={mediaStatus}
|
||||
{device}
|
||||
on:togglePlayback={() => {
|
||||
switch (mediaStatus?.playerState) {
|
||||
case PlayerState.PLAYING:
|
||||
sendMediaMessage({ type: "PAUSE" });
|
||||
break;
|
||||
case PlayerState.PAUSED:
|
||||
sendMediaMessage({ type: "PLAY" });
|
||||
break;
|
||||
}
|
||||
}}
|
||||
on:previous={() => {
|
||||
sendMediaMessage({
|
||||
type: "QUEUE_UPDATE",
|
||||
jump: -1
|
||||
});
|
||||
}}
|
||||
on:next={() => {
|
||||
sendMediaMessage({
|
||||
type: "QUEUE_UPDATE",
|
||||
jump: 1
|
||||
});
|
||||
}}
|
||||
on:seek={ev => {
|
||||
sendMediaMessage({
|
||||
type: "SEEK",
|
||||
currentTime: ev.detail.position
|
||||
});
|
||||
}}
|
||||
on:trackChanged={ev => {
|
||||
sendMediaMessage({
|
||||
type: "EDIT_TRACKS_INFO",
|
||||
activeTrackIds: ev.detail.activeTrackIds
|
||||
});
|
||||
}}
|
||||
on:volumeChanged={ev => {
|
||||
sendReceiverMessage({
|
||||
type: "SET_VOLUME",
|
||||
volume: ev.detail
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</li>
|
||||
Reference in New Issue
Block a user