feat(web): make google cast opt in (#18514)

* add setting switch

this isnt bound to anything yet

* make google casting opt-in

* doc updates

* lint docs

* remove unneeded translation items

* update mobile openai defs

* fix failing test

we need to mock user prefs since CastButton uses it
This commit is contained in:
Brandon Wees
2025-05-28 15:57:36 -05:00
committed by GitHub
parent b054e9dc2c
commit 78224961d1
19 changed files with 383 additions and 3 deletions

View File

@@ -1,5 +1,6 @@
import { resetSavedUser, user as userStore } from '$lib/stores/user.store';
import { preferences as preferencesStore, resetSavedUser, user as userStore } from '$lib/stores/user.store';
import { assetFactory } from '@test-data/factories/asset-factory';
import { preferencesFactory } from '@test-data/factories/preferences-factory';
import { userAdminFactory } from '@test-data/factories/user-factory';
import '@testing-library/jest-dom';
import { render } from '@testing-library/svelte';
@@ -42,6 +43,9 @@ describe('AssetViewerNavBar component', () => {
});
it('shows back button', () => {
const prefs = preferencesFactory.build({ cast: { gCastEnabled: false } });
preferencesStore.set(prefs);
const asset = assetFactory.build({ isTrashed: false });
const { getByTitle } = render(AssetViewerNavBar, { asset, ...additionalProps });
expect(getByTitle('go_back')).toBeInTheDocument();
@@ -53,6 +57,10 @@ describe('AssetViewerNavBar component', () => {
const user = userAdminFactory.build({ id: ownerId });
const asset = assetFactory.build({ ownerId, isTrashed: false });
userStore.set(user);
const prefs = preferencesFactory.build({ cast: { gCastEnabled: false } });
preferencesStore.set(prefs);
const { getByTitle } = render(AssetViewerNavBar, { asset, ...additionalProps });
expect(getByTitle('delete')).toBeInTheDocument();
});

View File

@@ -34,6 +34,9 @@
let tagsEnabled = $state($preferences?.tags?.enabled ?? false);
let tagsSidebar = $state($preferences?.tags?.sidebarWeb ?? false);
// Cast
let gCastEnabled = $state($preferences?.cast?.gCastEnabled ?? false);
const handleSave = async () => {
try {
const data = await updateMyPreferences({
@@ -44,6 +47,7 @@
ratings: { enabled: ratingsEnabled },
sharedLinks: { enabled: sharedLinksEnabled, sidebarWeb: sharedLinkSidebar },
tags: { enabled: tagsEnabled, sidebarWeb: tagsSidebar },
cast: { gCastEnabled },
},
});
@@ -138,6 +142,16 @@
{/if}
</SettingAccordion>
<SettingAccordion key="cast" title={$t('cast')} subtitle={$t('cast_description')}>
<div class="ms-4 mt-6">
<SettingSwitch
title={$t('gcast_enabled')}
subtitle={$t('gcast_enabled_description')}
bind:checked={gCastEnabled}
/>
</div>
</SettingAccordion>
<div class="flex justify-end">
<Button shape="round" type="submit" size="small" onclick={() => handleSave()}>{$t('save')}</Button>
</div>

View File

@@ -1,6 +1,8 @@
import { CastDestinationType, CastState, type ICastDestination } from '$lib/managers/cast-manager.svelte';
import { preferences } from '$lib/stores/user.store';
import 'chromecast-caf-sender';
import { Duration } from 'luxon';
import { get } from 'svelte/store';
const FRAMEWORK_LINK = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1';
@@ -24,6 +26,12 @@ export class GCastDestination implements ICastDestination {
private currentUrl: string | null = null;
async initialize(): Promise<boolean> {
const preferencesStore = get(preferences);
if (!preferencesStore.cast.gCastEnabled) {
this.isAvailable = false;
return false;
}
// this is a really messy way since google does a pseudo-callbak
// in the form of a global window event. We will give Chrome 3 seconds to respond
// or we will mark the destination as unavailable

View File

@@ -0,0 +1,43 @@
import type { UserPreferencesResponseDto } from '@immich/sdk';
import { Sync } from 'factory.ts';
export const preferencesFactory = Sync.makeFactory<UserPreferencesResponseDto>({
cast: {
gCastEnabled: false,
},
download: {
archiveSize: 0,
includeEmbeddedVideos: false,
},
emailNotifications: {
albumInvite: false,
albumUpdate: false,
enabled: false,
},
folders: {
enabled: false,
sidebarWeb: false,
},
memories: {
enabled: false,
},
people: {
enabled: false,
sidebarWeb: false,
},
purchase: {
hideBuyButtonUntil: '',
showSupportBadge: false,
},
ratings: {
enabled: false,
},
sharedLinks: {
enabled: false,
sidebarWeb: false,
},
tags: {
enabled: false,
sidebarWeb: false,
},
});