mirror of
https://github.com/immich-app/immich.git
synced 2026-07-14 17:11:15 +00:00
refactor: auth manager (#27638)
This commit is contained in:
parent
daed3f0966
commit
1ba0989e15
77 changed files with 387 additions and 379 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import OnEvents from '$lib/components/OnEvents.svelte';
|
import OnEvents from '$lib/components/OnEvents.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import VersionAnnouncementModal from '$lib/modals/VersionAnnouncementModal.svelte';
|
import VersionAnnouncementModal from '$lib/modals/VersionAnnouncementModal.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import type { ReleaseEvent } from '$lib/types';
|
import type { ReleaseEvent } from '$lib/types';
|
||||||
import { getReleaseType, semverToName } from '$lib/utils';
|
import { getReleaseType, semverToName } from '$lib/utils';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const onReleaseEvent = async (release: ReleaseEvent) => {
|
const onReleaseEvent = async (release: ReleaseEvent) => {
|
||||||
if (!release.isAvailable || !$user.isAdmin) {
|
if (!release.isAvailable || !authManager.user.isAdmin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
import { SettingInputFieldType } from '$lib/constants';
|
import { SettingInputFieldType } from '$lib/constants';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { handleSystemConfigSave } from '$lib/services/system-config.service';
|
import { handleSystemConfigSave } from '$lib/services/system-config.service';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { sendTestEmailAdmin } from '@immich/sdk';
|
import { sendTestEmailAdmin } from '@immich/sdk';
|
||||||
import { Button, toastManager } from '@immich/ui';
|
import { Button, toastManager } from '@immich/ui';
|
||||||
|
|
@ -45,7 +45,9 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
toastManager.primary($t('admin.notification_email_test_email_sent', { values: { email: $user.email } }));
|
toastManager.primary(
|
||||||
|
$t('admin.notification_email_test_email_sent', { values: { email: authManager.user.email } }),
|
||||||
|
);
|
||||||
|
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
await handleSystemConfigSave({ notifications: configToEdit.notifications });
|
await handleSystemConfigSave({ notifications: configToEdit.notifications });
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { SettingInputFieldType } from '$lib/constants';
|
import { SettingInputFieldType } from '$lib/constants';
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { handleSystemConfigSave } from '$lib/services/system-config.service';
|
import { handleSystemConfigSave } from '$lib/services/system-config.service';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getStorageTemplateOptions, type SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
|
import { getStorageTemplateOptions, type SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
|
||||||
import { Heading, Link, LoadingSpinner, Text } from '@immich/ui';
|
import { Heading, Link, LoadingSpinner, Text } from '@immich/ui';
|
||||||
import handlebar from 'handlebars';
|
import handlebar from 'handlebars';
|
||||||
|
|
@ -177,7 +177,10 @@
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
<FormatMessage
|
<FormatMessage
|
||||||
key="admin.storage_template_path_length"
|
key="admin.storage_template_path_length"
|
||||||
values={{ length: parsedTemplate().length + $user.id.length + 'UPLOAD_LOCATION'.length, limit: 260 }}
|
values={{
|
||||||
|
length: parsedTemplate().length + authManager.user.id.length + 'UPLOAD_LOCATION'.length,
|
||||||
|
limit: 260,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<span class="font-semibold text-primary">{message}</span>
|
<span class="font-semibold text-primary">{message}</span>
|
||||||
|
|
@ -186,7 +189,10 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
<FormatMessage key="admin.storage_template_user_label" values={{ label: $user.storageLabel || $user.id }}>
|
<FormatMessage
|
||||||
|
key="admin.storage_template_user_label"
|
||||||
|
values={{ label: authManager.user.storageLabel || authManager.user.id }}
|
||||||
|
>
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<code class="text-primary">{message}</code>
|
<code class="text-primary">{message}</code>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
@ -195,7 +201,7 @@
|
||||||
|
|
||||||
<p class="p-4 py-2 mt-2 text-xs bg-gray-200 rounded-lg dark:bg-gray-700 dark:text-immich-dark-fg">
|
<p class="p-4 py-2 mt-2 text-xs bg-gray-200 rounded-lg dark:bg-gray-700 dark:text-immich-dark-fg">
|
||||||
<span class="text-immich-fg/25 dark:text-immich-dark-fg/50"
|
<span class="text-immich-fg/25 dark:text-immich-dark-fg/50"
|
||||||
>UPLOAD_LOCATION/library/{$user.storageLabel || $user.id}</span
|
>UPLOAD_LOCATION/library/{authManager.user.storageLabel || authManager.user.id}</span
|
||||||
>/{parsedTemplate()}.jpg
|
>/{parsedTemplate()}.jpg
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { getContextMenuPositionFromEvent, type ContextMenuPosition } from '$lib/utils/context-menu';
|
import { getContextMenuPositionFromEvent, type ContextMenuPosition } from '$lib/utils/context-menu';
|
||||||
import { getShortDateRange } from '$lib/utils/date-time';
|
import { getShortDateRange } from '$lib/utils/date-time';
|
||||||
import type { AlbumResponseDto } from '@immich/sdk';
|
import type { AlbumResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if showOwner}
|
{#if showOwner}
|
||||||
{#if $user.id === album.ownerId}
|
{#if authManager.user.id === album.ownerId}
|
||||||
<p>{$t('owned')}</p>
|
<p>{$t('owned')}</p>
|
||||||
{:else if album.owner}
|
{:else if album.owner}
|
||||||
<p>{$t('shared_by_user', { values: { user: album.owner.name } })}</p>
|
<p>{$t('shared_by_user', { values: { user: album.owner.name } })}</p>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
import OnEvents from '$lib/components/OnEvents.svelte';
|
import OnEvents from '$lib/components/OnEvents.svelte';
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import RightClickContextMenu from '$lib/components/shared-components/context-menu/right-click-context-menu.svelte';
|
import RightClickContextMenu from '$lib/components/shared-components/context-menu/right-click-context-menu.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import AlbumEditModal from '$lib/modals/AlbumEditModal.svelte';
|
import AlbumEditModal from '$lib/modals/AlbumEditModal.svelte';
|
||||||
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
||||||
import { handleDeleteAlbum, handleDownloadAlbum } from '$lib/services/album.service';
|
import { handleDeleteAlbum, handleDownloadAlbum } from '$lib/services/album.service';
|
||||||
|
|
@ -16,7 +17,6 @@
|
||||||
SortOrder,
|
SortOrder,
|
||||||
type AlbumViewSettings,
|
type AlbumViewSettings,
|
||||||
} from '$lib/stores/preferences.store';
|
} from '$lib/stores/preferences.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getSelectedAlbumGroupOption, sortAlbums, stringToSortOrder, type AlbumGroup } from '$lib/utils/album-utils';
|
import { getSelectedAlbumGroupOption, sortAlbums, stringToSortOrder, type AlbumGroup } from '$lib/utils/album-utils';
|
||||||
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
||||||
import { normalizeSearchString } from '$lib/utils/string-utils';
|
import { normalizeSearchString } from '$lib/utils/string-utils';
|
||||||
|
|
@ -97,7 +97,7 @@
|
||||||
|
|
||||||
/** Group by owner */
|
/** Group by owner */
|
||||||
[AlbumGroupBy.Owner]: (order, albums): AlbumGroup[] => {
|
[AlbumGroupBy.Owner]: (order, albums): AlbumGroup[] => {
|
||||||
const currentUserId = $user.id;
|
const currentUserId = authManager.user.id;
|
||||||
const groupedByOwnerIds = groupBy(albums, 'ownerId');
|
const groupedByOwnerIds = groupBy(albums, 'ownerId');
|
||||||
|
|
||||||
const sortSign = order === SortOrder.Desc ? -1 : 1;
|
const sortSign = order === SortOrder.Desc ? -1 : 1;
|
||||||
|
|
@ -130,7 +130,7 @@
|
||||||
return sharedAlbums;
|
return sharedAlbums;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
const nonOwnedAlbums = sharedAlbums.filter((album) => album.ownerId !== $user.id);
|
const nonOwnedAlbums = sharedAlbums.filter((album) => album.ownerId !== authManager.user.id);
|
||||||
return nonOwnedAlbums.length > 0 ? ownedAlbums.concat(nonOwnedAlbums) : ownedAlbums;
|
return nonOwnedAlbums.length > 0 ? ownedAlbums.concat(nonOwnedAlbums) : ownedAlbums;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
albumGroupIds = groupedAlbums.map(({ id }) => id);
|
albumGroupIds = groupedAlbums.map(({ id }) => id);
|
||||||
});
|
});
|
||||||
|
|
||||||
let showFullContextMenu = $derived(allowEdit && selectedAlbum && selectedAlbum.ownerId === $user.id);
|
let showFullContextMenu = $derived(allowEdit && selectedAlbum && selectedAlbum.ownerId === authManager.user.id);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (allowEdit) {
|
if (allowEdit) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { dateFormats } from '$lib/constants';
|
import { dateFormats } from '$lib/constants';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
||||||
import type { AlbumResponseDto } from '@immich/sdk';
|
import type { AlbumResponseDto } from '@immich/sdk';
|
||||||
import { Icon } from '@immich/ui';
|
import { Icon } from '@immich/ui';
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
icon={mdiShareVariantOutline}
|
icon={mdiShareVariantOutline}
|
||||||
size="16"
|
size="16"
|
||||||
class="inline ms-1 opacity-70"
|
class="inline ms-1 opacity-70"
|
||||||
title={album.ownerId === $user.id
|
title={album.ownerId === authManager.user.id
|
||||||
? $t('shared_by_you')
|
? $t('shared_by_you')
|
||||||
: $t('shared_by_user', { values: { user: album.owner.name } })}
|
: $t('shared_by_user', { values: { user: album.owner.name } })}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { shortcuts } from '$lib/actions/shortcut';
|
import { shortcuts } from '$lib/actions/shortcut';
|
||||||
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
|
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
|
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:document
|
<svelte:document
|
||||||
use:shortcuts={$preferences?.ratings.enabled
|
use:shortcuts={authManager.authenticated && authManager.preferences.ratings.enabled
|
||||||
? [
|
? [
|
||||||
{ shortcut: { key: '0' }, onShortcut: () => rateAsset(null) },
|
{ shortcut: { key: '0' }, onShortcut: () => rateAsset(null) },
|
||||||
...[1, 2, 3, 4, 5].map((rating) => ({
|
...[1, 2, 3, 4, 5].map((rating) => ({
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
||||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { getAssetMediaUrl } from '$lib/utils';
|
import { getAssetMediaUrl } from '$lib/utils';
|
||||||
import { getAssetType } from '$lib/utils/asset-utils';
|
import { getAssetType } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { isTenMinutesApart } from '$lib/utils/timesince';
|
import { isTenMinutesApart } from '$lib/utils/timesince';
|
||||||
import { ReactionType, type ActivityResponseDto, type AssetTypeEnum, type UserResponseDto } from '@immich/sdk';
|
import { ReactionType, type ActivityResponseDto, type AssetTypeEnum } from '@immich/sdk';
|
||||||
import { Icon, IconButton, LoadingSpinner, Textarea, toastManager } from '@immich/ui';
|
import { Icon, IconButton, LoadingSpinner, Textarea, toastManager } from '@immich/ui';
|
||||||
import { mdiClose, mdiDeleteOutline, mdiDotsVertical, mdiSend, mdiThumbUp } from '@mdi/js';
|
import { mdiClose, mdiDeleteOutline, mdiDotsVertical, mdiSend, mdiThumbUp } from '@mdi/js';
|
||||||
import * as luxon from 'luxon';
|
import * as luxon from 'luxon';
|
||||||
|
|
@ -39,7 +40,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: UserResponseDto;
|
|
||||||
assetId?: string | undefined;
|
assetId?: string | undefined;
|
||||||
albumId: string;
|
albumId: string;
|
||||||
assetType?: AssetTypeEnum | undefined;
|
assetType?: AssetTypeEnum | undefined;
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { user, assetId = undefined, albumId, assetType = undefined, albumOwnerId, disabled }: Props = $props();
|
let { assetId = undefined, albumId, assetType = undefined, albumOwnerId, disabled }: Props = $props();
|
||||||
|
|
||||||
let innerHeight: number = $state(0);
|
let innerHeight: number = $state(0);
|
||||||
let activityHeight: number = $state(0);
|
let activityHeight: number = $state(0);
|
||||||
|
|
@ -147,7 +147,7 @@
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
{#if reaction.user.id === authManager.user.id || albumOwnerId === authManager.user.id}
|
||||||
<div class="me-4">
|
<div class="me-4">
|
||||||
<ButtonContextMenu
|
<ButtonContextMenu
|
||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
|
|
@ -200,7 +200,7 @@
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
{#if reaction.user.id === authManager.user.id || albumOwnerId === authManager.user.id}
|
||||||
<div class="me-4">
|
<div class="me-4">
|
||||||
<ButtonContextMenu
|
<ButtonContextMenu
|
||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
|
|
@ -238,7 +238,7 @@
|
||||||
<div class="flex items-center justify-center p-2" bind:clientHeight={chatHeight}>
|
<div class="flex items-center justify-center p-2" bind:clientHeight={chatHeight}>
|
||||||
<div class="flex p-2 gap-4 h-fit bg-gray-200 text-immich-dark-gray rounded-3xl w-full">
|
<div class="flex p-2 gap-4 h-fit bg-gray-200 text-immich-dark-gray rounded-3xl w-full">
|
||||||
<div>
|
<div>
|
||||||
<UserAvatar {user} size="md" noTitle />
|
<UserAvatar user={authManager.user} size="md" noTitle />
|
||||||
</div>
|
</div>
|
||||||
<form class="flex w-full items-center max-h-56 gap-1" {onsubmit}>
|
<form class="flex w-full items-center max-h-56 gap-1" {onsubmit}>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getResizeObserverMock } from '$lib/__mocks__/resize-observer.mock';
|
import { getResizeObserverMock } from '$lib/__mocks__/resize-observer.mock';
|
||||||
import { preferences as preferencesStore, resetSavedUser, user as userStore } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { renderWithTooltips } from '$tests/helpers';
|
import { renderWithTooltips } from '$tests/helpers';
|
||||||
import { assetFactory } from '@test-data/factories/asset-factory';
|
import { assetFactory } from '@test-data/factories/asset-factory';
|
||||||
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||||
|
|
@ -36,7 +36,7 @@ describe('AssetViewerNavBar component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
resetSavedUser();
|
authManager.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
|
@ -44,8 +44,8 @@ describe('AssetViewerNavBar component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows back button', () => {
|
it('shows back button', () => {
|
||||||
const prefs = preferencesFactory.build({ cast: { gCastEnabled: false } });
|
const preferences = preferencesFactory.build({ cast: { gCastEnabled: false } });
|
||||||
preferencesStore.set(prefs);
|
authManager.setPreferences(preferences);
|
||||||
|
|
||||||
const asset = assetFactory.build({ isTrashed: false });
|
const asset = assetFactory.build({ isTrashed: false });
|
||||||
const { getByLabelText } = renderWithTooltips(AssetViewerNavBar, { asset, ...additionalProps });
|
const { getByLabelText } = renderWithTooltips(AssetViewerNavBar, { asset, ...additionalProps });
|
||||||
|
|
@ -57,10 +57,10 @@ describe('AssetViewerNavBar component', () => {
|
||||||
const ownerId = 'id-of-the-user';
|
const ownerId = 'id-of-the-user';
|
||||||
const user = userAdminFactory.build({ id: ownerId });
|
const user = userAdminFactory.build({ id: ownerId });
|
||||||
const asset = assetFactory.build({ ownerId, isTrashed: false });
|
const asset = assetFactory.build({ ownerId, isTrashed: false });
|
||||||
userStore.set(user);
|
authManager.setUser(user);
|
||||||
|
|
||||||
const prefs = preferencesFactory.build({ cast: { gCastEnabled: false } });
|
const preferences = preferencesFactory.build({ cast: { gCastEnabled: false } });
|
||||||
preferencesStore.set(prefs);
|
authManager.setPreferences(preferences);
|
||||||
|
|
||||||
const { getByLabelText } = renderWithTooltips(AssetViewerNavBar, { asset, ...additionalProps });
|
const { getByLabelText } = renderWithTooltips(AssetViewerNavBar, { asset, ...additionalProps });
|
||||||
expect(getByLabelText('delete')).toBeInTheDocument();
|
expect(getByLabelText('delete')).toBeInTheDocument();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte';
|
import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte';
|
||||||
import RatingAction from '$lib/components/asset-viewer/actions/rating-action.svelte';
|
import RatingAction from '$lib/components/asset-viewer/actions/rating-action.svelte';
|
||||||
import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/remove-asset-from-stack.svelte';
|
import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/remove-asset-from-stack.svelte';
|
||||||
import RemoveFromAlbumAction from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte';
|
|
||||||
import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
|
import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
|
||||||
import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
|
import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
|
||||||
import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte';
|
import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte';
|
||||||
|
|
@ -19,13 +18,14 @@
|
||||||
import LoadingDots from '$lib/components/LoadingDots.svelte';
|
import LoadingDots from '$lib/components/LoadingDots.svelte';
|
||||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
|
import RemoveFromAlbumAction from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte';
|
||||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { languageManager } from '$lib/managers/language-manager.svelte';
|
import { languageManager } from '$lib/managers/language-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getGlobalActions } from '$lib/services/app.service';
|
import { getGlobalActions } from '$lib/services/app.service';
|
||||||
import { getAssetActions } from '$lib/services/asset.service';
|
import { getAssetActions } from '$lib/services/asset.service';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getSharedLink, withoutIcons } from '$lib/utils';
|
import { getSharedLink, withoutIcons } from '$lib/utils';
|
||||||
import type { OnUndoDelete } from '$lib/utils/actions';
|
import type { OnUndoDelete } from '$lib/utils/actions';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
|
|
@ -81,8 +81,8 @@
|
||||||
setPlayOriginalVideo,
|
setPlayOriginalVideo,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const isOwner = $derived($user && asset.ownerId === $user?.id);
|
const isOwner = $derived(authManager.authenticated && asset.ownerId === authManager.user.id);
|
||||||
const isAlbumOwner = $derived($user && album?.ownerId === $user?.id);
|
const isAlbumOwner = $derived(authManager.authenticated && album?.ownerId === authManager.user.id);
|
||||||
const isLocked = $derived(asset.visibility === AssetVisibility.Locked);
|
const isLocked = $derived(asset.visibility === AssetVisibility.Locked);
|
||||||
const smartSearchEnabled = $derived(featureFlagsManager.value.smartSearch);
|
const smartSearchEnabled = $derived(featureFlagsManager.value.smartSearch);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { getAnimateMock } from '$lib/__mocks__/animate.mock';
|
import { getAnimateMock } from '$lib/__mocks__/animate.mock';
|
||||||
import { getResizeObserverMock } from '$lib/__mocks__/resize-observer.mock';
|
import { getResizeObserverMock } from '$lib/__mocks__/resize-observer.mock';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { preferences as preferencesStore, resetSavedUser, user as userStore } from '$lib/stores/user.store';
|
|
||||||
import { renderWithTooltips } from '$tests/helpers';
|
import { renderWithTooltips } from '$tests/helpers';
|
||||||
import { updateAsset } from '@immich/sdk';
|
import { updateAsset } from '@immich/sdk';
|
||||||
import { assetFactory } from '@test-data/factories/asset-factory';
|
import { assetFactory } from '@test-data/factories/asset-factory';
|
||||||
|
|
@ -43,7 +43,7 @@ describe('AssetViewer', () => {
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
slideshowStore.slideshowState.set(SlideshowState.None);
|
slideshowStore.slideshowState.set(SlideshowState.None);
|
||||||
resetSavedUser();
|
authManager.reset();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -56,8 +56,9 @@ describe('AssetViewer', () => {
|
||||||
const user = userAdminFactory.build({ id: ownerId });
|
const user = userAdminFactory.build({ id: ownerId });
|
||||||
const asset = assetFactory.build({ ownerId, isFavorite: false, isTrashed: false });
|
const asset = assetFactory.build({ ownerId, isFavorite: false, isTrashed: false });
|
||||||
|
|
||||||
userStore.set(user);
|
authManager.setUser(user);
|
||||||
preferencesStore.set(preferencesFactory.build({ cast: { gCastEnabled: false } }));
|
authManager.setPreferences(preferencesFactory.build({ cast: { gCastEnabled: false } }));
|
||||||
|
|
||||||
vi.mocked(updateAsset).mockResolvedValue({ ...asset, isFavorite: true });
|
vi.mocked(updateAsset).mockResolvedValue({ ...asset, isFavorite: true });
|
||||||
|
|
||||||
const { getByLabelText, queryByLabelText } = renderWithTooltips(AssetViewer, {
|
const { getByLabelText, queryByLabelText } = renderWithTooltips(AssetViewer, {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
import { ocrManager } from '$lib/stores/ocr.svelte';
|
import { ocrManager } from '$lib/stores/ocr.svelte';
|
||||||
import { alwaysLoadOriginalVideo } from '$lib/stores/preferences.store';
|
import { alwaysLoadOriginalVideo } from '$lib/stores/preferences.store';
|
||||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getSharedLink, handlePromiseError } from '$lib/utils';
|
import { getSharedLink, handlePromiseError } from '$lib/utils';
|
||||||
import type { OnUndoDelete } from '$lib/utils/actions';
|
import type { OnUndoDelete } from '$lib/utils/actions';
|
||||||
import { navigateToAsset } from '$lib/utils/asset-utils';
|
import { navigateToAsset } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -629,7 +628,7 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isShared && album && assetViewerManager.isShowActivityPanel && $user}
|
{#if isShared && album && assetViewerManager.isShowActivityPanel && authManager.authenticated}
|
||||||
<div
|
<div
|
||||||
transition:fly={{ duration: 150 }}
|
transition:fly={{ duration: 150 }}
|
||||||
id="activity-panel"
|
id="activity-panel"
|
||||||
|
|
@ -637,7 +636,6 @@
|
||||||
translate="yes"
|
translate="yes"
|
||||||
>
|
>
|
||||||
<ActivityViewer
|
<ActivityViewer
|
||||||
user={$user}
|
|
||||||
disabled={!album.isActivityEnabled}
|
disabled={!album.isActivityEnabled}
|
||||||
assetType={asset.type}
|
assetType={asset.type}
|
||||||
albumOwnerId={album.ownerId}
|
albumOwnerId={album.ownerId}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import StarRating, { type Rating } from '$lib/elements/StarRating.svelte';
|
import StarRating, { type Rating } from '$lib/elements/StarRating.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
|
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -25,7 +24,7 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !authManager.isSharedLink && $preferences?.ratings.enabled}
|
{#if !authManager.isSharedLink && authManager.authenticated && authManager.preferences.ratings.enabled}
|
||||||
<section class="px-4 pt-4">
|
<section class="px-4 pt-4">
|
||||||
<StarRating {rating} readOnly={!isOwner} onRating={(rating) => handlePromiseError(handleChangeRating(rating))} />
|
<StarRating {rating} readOnly={!isOwner} onRating={(rating) => handlePromiseError(handleChangeRating(rating))} />
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { preferences, user } from '$lib/stores/user.store';
|
|
||||||
import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
||||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||||
|
|
@ -56,7 +55,7 @@
|
||||||
let { asset, currentAlbum = null }: Props = $props();
|
let { asset, currentAlbum = null }: Props = $props();
|
||||||
|
|
||||||
let showEditFaces = $state(false);
|
let showEditFaces = $state(false);
|
||||||
let isOwner = $derived($user?.id === asset.ownerId);
|
let isOwner = $derived(authManager.authenticated && authManager.user.id === asset.ownerId);
|
||||||
let people = $derived(asset.people || []);
|
let people = $derived(asset.people || []);
|
||||||
let unassignedFaces = $derived(asset.unassignedFaces || []);
|
let unassignedFaces = $derived(asset.unassignedFaces || []);
|
||||||
let showingHiddenPeople = $state(false);
|
let showingHiddenPeople = $state(false);
|
||||||
|
|
@ -164,7 +163,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="border border-t-0 border-red-400 bg-red-100 px-4 py-3 text-red-700">
|
<div class="border border-t-0 border-red-400 bg-red-100 px-4 py-3 text-red-700">
|
||||||
<p>
|
<p>
|
||||||
{#if $user?.isAdmin}
|
{#if authManager.authenticated && authManager.user.isAdmin}
|
||||||
{$t('admin.asset_offline_description')}
|
{$t('admin.asset_offline_description')}
|
||||||
{:else}
|
{:else}
|
||||||
{$t('asset_offline_description')}
|
{$t('asset_offline_description')}
|
||||||
|
|
@ -563,7 +562,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
{#if $preferences?.tags?.enabled}
|
{#if authManager.authenticated && authManager.preferences.tags.enabled}
|
||||||
<section class="relative px-2 pb-12 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
|
<section class="relative px-2 pb-12 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
|
||||||
<DetailPanelTags {asset} {isOwner} />
|
<DetailPanelTags {asset} {isOwner} />
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { locale, videoViewerMuted, videoViewerVolume } from '$lib/stores/preferences.store';
|
import { locale, videoViewerMuted, videoViewerVolume } from '$lib/stores/preferences.store';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { getAssetMediaUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
import { getAssetMediaUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
||||||
import { fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
import { fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
import { AssetMediaSize, AssetTypeEnum, getAssetInfo } from '@immich/sdk';
|
import { AssetMediaSize, AssetTypeEnum, getAssetInfo } from '@immich/sdk';
|
||||||
|
|
@ -104,7 +103,8 @@
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
progressBarController = new Tween<number>(0, {
|
progressBarController = new Tween<number>(0, {
|
||||||
duration: (from: number, to: number) => (to ? $preferences.memories.duration * 1000 * (to - from) : 0),
|
duration: (from: number, to: number) =>
|
||||||
|
to ? authManager.preferences.memories.duration * 1000 * (to - from) : 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -362,7 +362,7 @@
|
||||||
unarchive={assetMultiSelectManager.isAllArchived}
|
unarchive={assetMultiSelectManager.isAllArchived}
|
||||||
onArchive={handleDeleteOrArchiveAssets}
|
onArchive={handleDeleteOrArchiveAssets}
|
||||||
/>
|
/>
|
||||||
{#if $preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
{#if authManager.preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<DeleteAssets menuItem onAssetDelete={handleDeleteOrArchiveAssets} />
|
<DeleteAssets menuItem onAssetDelete={handleDeleteOrArchiveAssets} />
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { OnboardingRole } from '$lib/types';
|
import { OnboardingRole } from '$lib/types';
|
||||||
import { Logo } from '@immich/ui';
|
import { Logo } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
let userRole = $derived(
|
let userRole = $derived(
|
||||||
$user.isAdmin && !serverConfigManager.value.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER,
|
authManager.user.isAdmin && !serverConfigManager.value.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER,
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="gap-4">
|
<div class="gap-4">
|
||||||
<Logo variant="icon" size="giant" class="mb-2" />
|
<Logo variant="icon" size="giant" class="mb-2" />
|
||||||
<p class="font-medium mb-6 text-6xl text-primary">
|
<p class="font-medium mb-6 text-6xl text-primary">
|
||||||
{$t('onboarding_welcome_user', { values: { user: $user.name } })}
|
{$t('onboarding_welcome_user', { values: { user: authManager.user.name } })}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-3xl pb-6 font-light">
|
<p class="text-3xl pb-6 font-light">
|
||||||
{userRole == OnboardingRole.SERVER
|
{userRole == OnboardingRole.SERVER
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import StorageTemplateSettings from '$lib/components/admin-settings/StorageTemplateSettings.svelte';
|
import StorageTemplateSettings from '$lib/components/admin-settings/StorageTemplateSettings.svelte';
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Link } from '@immich/ui';
|
import { Link } from '@immich/ui';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
</FormatMessage>
|
</FormatMessage>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{#if $user}
|
{#if authManager.authenticated}
|
||||||
<StorageTemplateSettings minified duration={0} saveOnClose />
|
<StorageTemplateSettings minified duration={0} saveOnClose />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,17 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateMyPreferences } from '@immich/sdk';
|
import { updateMyPreferences } from '@immich/sdk';
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
let gCastEnabled = $state($preferences?.cast?.gCastEnabled ?? false);
|
let gCastEnabled = $state(authManager.authenticated ? authManager.preferences.cast.gCastEnabled : false);
|
||||||
|
|
||||||
onDestroy(async () => {
|
onDestroy(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await updateMyPreferences({
|
const response = await updateMyPreferences({ userPreferencesUpdateDto: { cast: { gCastEnabled } } });
|
||||||
userPreferencesUpdateDto: {
|
authManager.setPreferences(response);
|
||||||
cast: { gCastEnabled },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
$preferences = { ...data };
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_update_settings'));
|
handleError(error, $t('errors.unable_to_update_settings'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||||
import ThemeButton from '$lib/components/shared-components/theme-button.svelte';
|
import ThemeButton from '$lib/components/shared-components/theme-button.svelte';
|
||||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { setSharedLink } from '$lib/utils';
|
import { setSharedLink } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { navigate } from '$lib/utils/navigation';
|
import { navigate } from '$lib/utils/navigation';
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
let { sharedLink, passwordRequired, key, slug, meta } = $state(data);
|
let { sharedLink, passwordRequired, key, slug, meta } = $state(data);
|
||||||
let { title, description } = $state(meta);
|
let { title, description } = $state(meta);
|
||||||
let isOwned = $derived($user ? $user.id === sharedLink?.userId : false);
|
let isOwned = $derived(authManager.authenticated && authManager.user.id === sharedLink?.userId);
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
|
|
||||||
const handlePasswordSubmit = async () => {
|
const handlePasswordSubmit = async () => {
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||||
import Portal from '$lib/elements/Portal.svelte';
|
import Portal from '$lib/elements/Portal.svelte';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { mapSettings } from '$lib/stores/preferences.store';
|
import { mapSettings } from '$lib/stores/preferences.store';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import {
|
import {
|
||||||
updateStackedAssetInTimeline,
|
updateStackedAssetInTimeline,
|
||||||
updateUnstackedAssetInTimeline,
|
updateUnstackedAssetInTimeline,
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
unarchive={assetMultiSelectManager.isAllArchived}
|
unarchive={assetMultiSelectManager.isAllArchived}
|
||||||
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||||
/>
|
/>
|
||||||
{#if $preferences.tags.enabled}
|
{#if authManager.preferences.tags.enabled}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<DeleteAssets
|
<DeleteAssets
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { focusTrap } from '$lib/actions/focus-trap';
|
import { focusTrap } from '$lib/actions/focus-trap';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import AvatarEditModal from '$lib/modals/AvatarEditModal.svelte';
|
import AvatarEditModal from '$lib/modals/AvatarEditModal.svelte';
|
||||||
import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte';
|
import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk';
|
import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk';
|
||||||
import { Button, Icon, IconButton, modalManager } from '@immich/ui';
|
import { Button, Icon, IconButton, modalManager } from '@immich/ui';
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
class="mx-4 mt-4 flex flex-col items-center justify-center gap-4 rounded-t-3xl bg-white p-4 dark:bg-immich-dark-primary/10"
|
class="mx-4 mt-4 flex flex-col items-center justify-center gap-4 rounded-t-3xl bg-white p-4 dark:bg-immich-dark-primary/10"
|
||||||
>
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<UserAvatar user={$user} size="xl" />
|
<UserAvatar user={authManager.user} size="xl" />
|
||||||
<div class="absolute bottom-0 end-0 rounded-full w-6 h-6">
|
<div class="absolute bottom-0 end-0 rounded-full w-6 h-6">
|
||||||
<IconButton
|
<IconButton
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -56,9 +56,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-center text-lg font-medium text-primary">
|
<p class="text-center text-lg font-medium text-primary">
|
||||||
{$user.name}
|
{authManager.user.name}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-gray-500 dark:text-immich-dark-fg">{$user.email}</p>
|
<p class="text-sm text-gray-500 dark:text-immich-dark-fg">{authManager.user.email}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
{$t('account_settings')}
|
{$t('account_settings')}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
{#if $user.isAdmin}
|
{#if authManager.user.isAdmin}
|
||||||
<Button
|
<Button
|
||||||
href={Route.systemSettings()}
|
href={Route.systemSettings()}
|
||||||
onclick={onClose}
|
onclick={onClose}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
|
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
|
||||||
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
||||||
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { ActionButton, Button, IconButton, Logo } from '@immich/ui';
|
import { ActionButton, Button, IconButton, Logo } from '@immich/ui';
|
||||||
import { mdiBellBadge, mdiBellOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
|
import { mdiBellBadge, mdiBellOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
@ -171,10 +170,10 @@
|
||||||
type="button"
|
type="button"
|
||||||
class="flex ps-2"
|
class="flex ps-2"
|
||||||
onclick={() => (shouldShowAccountInfoPanel = !shouldShowAccountInfoPanel)}
|
onclick={() => (shouldShowAccountInfoPanel = !shouldShowAccountInfoPanel)}
|
||||||
title={`${$user.name} (${$user.email})`}
|
title="{authManager.user.name} ({authManager.user.email})"
|
||||||
>
|
>
|
||||||
{#key $user}
|
{#key authManager.user}
|
||||||
<UserAvatar user={$user} size="md" noTitle interactive />
|
<UserAvatar user={authManager.user} size="md" noTitle interactive />
|
||||||
{/key}
|
{/key}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { setSupportBadgeVisibility } from '$lib/utils/purchase-utils';
|
import { setSupportBadgeVisibility } from '$lib/utils/purchase-utils';
|
||||||
import { Button, Icon } from '@immich/ui';
|
import { Button, Icon } from '@immich/ui';
|
||||||
import { mdiPartyPopper } from '@mdi/js';
|
import { mdiPartyPopper } from '@mdi/js';
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<SettingSwitch
|
<SettingSwitch
|
||||||
title={$t('show_supporter_badge')}
|
title={$t('show_supporter_badge')}
|
||||||
subtitle={$t('show_supporter_badge_description')}
|
subtitle={$t('show_supporter_badge_description')}
|
||||||
bind:checked={$preferences.purchase.showSupportBadge}
|
bind:checked={authManager.preferences.purchase.showSupportBadge}
|
||||||
onToggle={setSupportBadgeVisibility}
|
onToggle={setSupportBadgeVisibility}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { getAllTags, type TagResponseDto } from '@immich/sdk';
|
import { getAllTags, type TagResponseDto } from '@immich/sdk';
|
||||||
import { Checkbox, Label, Text } from '@immich/ui';
|
import { Checkbox, Label, Text } from '@immich/ui';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $preferences?.tags?.enabled}
|
{#if authManager.authenticated && authManager.preferences.tags.enabled}
|
||||||
<div id="location-selection">
|
<div id="location-selection">
|
||||||
<form autocomplete="off" id="create-tag-form">
|
<form autocomplete="off" id="create-tag-form">
|
||||||
<div class="mb-4 flex flex-col">
|
<div class="mb-4 flex flex-col">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import PurchaseModal from '$lib/modals/PurchaseModal.svelte';
|
import PurchaseModal from '$lib/modals/PurchaseModal.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { getAccountAge } from '$lib/utils/auth';
|
import { getAccountAge } from '$lib/utils/auth';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getButtonVisibility } from '$lib/utils/purchase-utils';
|
import { getButtonVisibility } from '$lib/utils/purchase-utils';
|
||||||
|
|
@ -50,7 +49,8 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
preferences.set(response);
|
authManager.setPreferences(response);
|
||||||
|
|
||||||
showBuyButton = getButtonVisibility();
|
showBuyButton = getButtonVisibility();
|
||||||
showMessage = false;
|
showMessage = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="license-status ps-4 text-sm">
|
<div class="license-status ps-4 text-sm">
|
||||||
{#if authManager.isPurchased && $preferences.purchase.showSupportBadge}
|
{#if authManager.isPurchased && authManager.preferences.purchase.showSupportBadge}
|
||||||
<button
|
<button
|
||||||
onclick={() => goto(Route.userSettings({ isOpen: OpenQueryParam.PURCHASE_SETTINGS }))}
|
onclick={() => goto(Route.userSettings({ isOpen: OpenQueryParam.PURCHASE_SETTINGS }))}
|
||||||
class="w-full mt-2"
|
class="w-full mt-2"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { releaseManager } from '$lib/managers/release-manager.svelte';
|
import { releaseManager } from '$lib/managers/release-manager.svelte';
|
||||||
import ServerAboutModal from '$lib/modals/ServerAboutModal.svelte';
|
import ServerAboutModal from '$lib/modals/ServerAboutModal.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { websocketStore } from '$lib/stores/websocket';
|
import { websocketStore } from '$lib/stores/websocket';
|
||||||
import type { ReleaseEvent } from '$lib/types';
|
import type { ReleaseEvent } from '$lib/types';
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
const getReleaseInfo = (release?: ReleaseEvent) => {
|
const getReleaseInfo = (release?: ReleaseEvent) => {
|
||||||
if (!release || !release?.isAvailable || !$user.isAdmin) {
|
if (!release || !release?.isAvailable || !authManager.user.isAdmin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { requestServerInfo } from '$lib/utils/auth';
|
import { requestServerInfo } from '$lib/utils/auth';
|
||||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||||
|
|
@ -8,9 +8,17 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
let hasQuota = $derived($user?.quotaSizeInBytes !== null);
|
let hasQuota = $derived(authManager.user.quotaSizeInBytes !== null);
|
||||||
let availableBytes = $derived((hasQuota ? $user?.quotaSizeInBytes : userInteraction.serverInfo?.diskSizeRaw) || 0);
|
let availableBytes = $derived(
|
||||||
let usedBytes = $derived((hasQuota ? $user?.quotaUsageInBytes : userInteraction.serverInfo?.diskUseRaw) || 0);
|
(hasQuota && authManager.authenticated
|
||||||
|
? authManager.user.quotaSizeInBytes
|
||||||
|
: userInteraction.serverInfo?.diskSizeRaw) || 0,
|
||||||
|
);
|
||||||
|
let usedBytes = $derived(
|
||||||
|
(hasQuota && authManager.authenticated
|
||||||
|
? authManager.user.quotaUsageInBytes
|
||||||
|
: userInteraction.serverInfo?.diskUseRaw) || 0,
|
||||||
|
);
|
||||||
|
|
||||||
const thresholds = [
|
const thresholds = [
|
||||||
{ from: 0.8, className: 'bg-warning' },
|
{ from: 0.8, className: 'bg-warning' },
|
||||||
|
|
@ -18,7 +26,7 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (userInteraction.serverInfo && $user) {
|
if (userInteraction.serverInfo && authManager.authenticated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await requestServerInfo();
|
await requestServerInfo();
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte';
|
import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte';
|
||||||
import RecentAlbums from '$lib/components/shared-components/side-bar/recent-albums.svelte';
|
import RecentAlbums from '$lib/components/shared-components/side-bar/recent-albums.svelte';
|
||||||
import Sidebar from '$lib/components/sidebar/sidebar.svelte';
|
import Sidebar from '$lib/components/sidebar/sidebar.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { recentAlbumsDropdown } from '$lib/stores/preferences.store';
|
import { recentAlbumsDropdown } from '$lib/stores/preferences.store';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { NavbarGroup, NavbarItem } from '@immich/ui';
|
import { NavbarGroup, NavbarItem } from '@immich/ui';
|
||||||
import {
|
import {
|
||||||
mdiAccount,
|
mdiAccount,
|
||||||
|
|
@ -47,11 +47,11 @@
|
||||||
<NavbarItem title={$t('map')} href={Route.map()} icon={mdiMapOutline} activeIcon={mdiMap} />
|
<NavbarItem title={$t('map')} href={Route.map()} icon={mdiMapOutline} activeIcon={mdiMap} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $preferences.people.enabled && $preferences.people.sidebarWeb}
|
{#if authManager.preferences.people.enabled && authManager.preferences.people.sidebarWeb}
|
||||||
<NavbarItem title={$t('people')} href={Route.people()} icon={mdiAccountOutline} activeIcon={mdiAccount} />
|
<NavbarItem title={$t('people')} href={Route.people()} icon={mdiAccountOutline} activeIcon={mdiAccount} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $preferences.sharedLinks.enabled && $preferences.sharedLinks.sidebarWeb}
|
{#if authManager.preferences.sharedLinks.enabled && authManager.preferences.sharedLinks.sidebarWeb}
|
||||||
<NavbarItem title={$t('shared_links')} href={Route.sharedLinks()} icon={mdiLink} />
|
<NavbarItem title={$t('shared_links')} href={Route.sharedLinks()} icon={mdiLink} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
@ -79,11 +79,11 @@
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</NavbarItem>
|
</NavbarItem>
|
||||||
|
|
||||||
{#if $preferences.tags.enabled && $preferences.tags.sidebarWeb}
|
{#if authManager.preferences.tags.enabled && authManager.preferences.tags.sidebarWeb}
|
||||||
<NavbarItem title={$t('tags')} href={Route.tags()} icon={{ icon: mdiTagMultipleOutline, flipped: true }} />
|
<NavbarItem title={$t('tags')} href={Route.tags()} icon={{ icon: mdiTagMultipleOutline, flipped: true }} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $preferences.folders.enabled && $preferences.folders.sidebarWeb}
|
{#if authManager.preferences.folders.enabled && authManager.preferences.folders.sidebarWeb}
|
||||||
<NavbarItem title={$t('folders')} href={Route.folders()} icon={{ icon: mdiFolderOutline, flipped: true }} />
|
<NavbarItem title={$t('folders')} href={Route.folders()} icon={{ icon: mdiFolderOutline, flipped: true }} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import AssetUpdateDescriptionConfirmModal from '$lib/modals/AssetUpdateDescriptionConfirmModal.svelte';
|
import AssetUpdateDescriptionConfirmModal from '$lib/modals/AssetUpdateDescriptionConfirmModal.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAssets } from '@immich/sdk';
|
import { updateAssets } from '@immich/sdk';
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
const handleUpdateDescription = async () => {
|
const handleUpdateDescription = async () => {
|
||||||
const description = await modalManager.show(AssetUpdateDescriptionConfirmModal);
|
const description = await modalManager.show(AssetUpdateDescriptionConfirmModal);
|
||||||
if (description) {
|
if (description) {
|
||||||
const ids = getOwnedAssetsWithWarning(assetMultiSelectManager.assets, $user);
|
const ids = getOwnedAssetsWithWarning(assetMultiSelectManager.assets, authManager.user);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids, description } });
|
await updateAssets({ assetBulkUpdateDto: { ids, description } });
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import GeolocationPointPickerModal from '$lib/modals/GeolocationPointPickerModal.svelte';
|
import GeolocationPointPickerModal from '$lib/modals/GeolocationPointPickerModal.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAssets } from '@immich/sdk';
|
import { updateAssets } from '@immich/sdk';
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = getOwnedAssetsWithWarning(assetMultiSelectManager.assets, $user);
|
const ids = getOwnedAssetsWithWarning(assetMultiSelectManager.assets, authManager.user);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } });
|
await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } });
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte';
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { SettingInputFieldType } from '$lib/constants';
|
import { SettingInputFieldType } from '$lib/constants';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateMyPreferences } from '@immich/sdk';
|
import { updateMyPreferences } from '@immich/sdk';
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
let archiveSize = $state(convertFromBytes($preferences?.download?.archiveSize || 4, ByteUnit.GiB));
|
let archiveSize = $state(convertFromBytes(authManager.preferences.download.archiveSize || 4, ByteUnit.GiB));
|
||||||
let includeEmbeddedVideos = $state($preferences?.download?.includeEmbeddedVideos || false);
|
let includeEmbeddedVideos = $state(authManager.preferences.download.includeEmbeddedVideos || false);
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
const newPreferences = await updateMyPreferences({
|
const response = await updateMyPreferences({
|
||||||
userPreferencesUpdateDto: {
|
userPreferencesUpdateDto: {
|
||||||
download: {
|
download: {
|
||||||
archiveSize: Math.floor(convertToBytes(archiveSize, ByteUnit.GiB)),
|
archiveSize: Math.floor(convertToBytes(archiveSize, ByteUnit.GiB)),
|
||||||
|
|
@ -23,7 +23,8 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
$preferences = newPreferences;
|
|
||||||
|
authManager.setPreferences(response);
|
||||||
|
|
||||||
toastManager.primary($t('saved_settings'));
|
toastManager.primary($t('saved_settings'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { AssetOrder, updateMyPreferences } from '@immich/sdk';
|
import { AssetOrder, updateMyPreferences } from '@immich/sdk';
|
||||||
import { Button, Field, NumberInput, Select, Switch, toastManager } from '@immich/ui';
|
import { Button, Field, NumberInput, Select, Switch, toastManager } from '@immich/ui';
|
||||||
|
|
@ -8,37 +8,37 @@
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
// Albums
|
// Albums
|
||||||
let defaultAssetOrder = $state($preferences?.albums?.defaultAssetOrder ?? AssetOrder.Desc);
|
let defaultAssetOrder = $state(authManager.preferences.albums?.defaultAssetOrder ?? AssetOrder.Desc);
|
||||||
|
|
||||||
// Folders
|
// Folders
|
||||||
let foldersEnabled = $state($preferences?.folders?.enabled ?? false);
|
let foldersEnabled = $state(authManager.preferences.folders?.enabled ?? false);
|
||||||
let foldersSidebar = $state($preferences?.folders?.sidebarWeb ?? false);
|
let foldersSidebar = $state(authManager.preferences.folders?.sidebarWeb ?? false);
|
||||||
|
|
||||||
// Memories
|
// Memories
|
||||||
let memoriesEnabled = $state($preferences?.memories?.enabled ?? true);
|
let memoriesEnabled = $state(authManager.preferences.memories?.enabled ?? true);
|
||||||
let memoriesDuration = $state($preferences?.memories?.duration ?? 5);
|
let memoriesDuration = $state(authManager.preferences.memories?.duration ?? 5);
|
||||||
|
|
||||||
// People
|
// People
|
||||||
let peopleEnabled = $state($preferences?.people?.enabled ?? false);
|
let peopleEnabled = $state(authManager.preferences.people?.enabled ?? false);
|
||||||
let peopleSidebar = $state($preferences?.people?.sidebarWeb ?? false);
|
let peopleSidebar = $state(authManager.preferences.people?.sidebarWeb ?? false);
|
||||||
|
|
||||||
// Ratings
|
// Ratings
|
||||||
let ratingsEnabled = $state($preferences?.ratings?.enabled ?? false);
|
let ratingsEnabled = $state(authManager.preferences.ratings?.enabled ?? false);
|
||||||
|
|
||||||
// Shared links
|
// Shared links
|
||||||
let sharedLinksEnabled = $state($preferences?.sharedLinks?.enabled ?? true);
|
let sharedLinksEnabled = $state(authManager.preferences.sharedLinks?.enabled ?? true);
|
||||||
let sharedLinkSidebar = $state($preferences?.sharedLinks?.sidebarWeb ?? false);
|
let sharedLinkSidebar = $state(authManager.preferences.sharedLinks?.sidebarWeb ?? false);
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
let tagsEnabled = $state($preferences?.tags?.enabled ?? false);
|
let tagsEnabled = $state(authManager.preferences.tags?.enabled ?? false);
|
||||||
let tagsSidebar = $state($preferences?.tags?.sidebarWeb ?? false);
|
let tagsSidebar = $state(authManager.preferences.tags?.sidebarWeb ?? false);
|
||||||
|
|
||||||
// Cast
|
// Cast
|
||||||
let gCastEnabled = $state($preferences?.cast?.gCastEnabled ?? false);
|
let gCastEnabled = $state(authManager.preferences.cast?.gCastEnabled ?? false);
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await updateMyPreferences({
|
const response = await updateMyPreferences({
|
||||||
userPreferencesUpdateDto: {
|
userPreferencesUpdateDto: {
|
||||||
albums: { defaultAssetOrder },
|
albums: { defaultAssetOrder },
|
||||||
folders: { enabled: foldersEnabled, sidebarWeb: foldersSidebar },
|
folders: { enabled: foldersEnabled, sidebarWeb: foldersSidebar },
|
||||||
|
|
@ -51,8 +51,7 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
$preferences = { ...data };
|
authManager.setPreferences(response);
|
||||||
|
|
||||||
toastManager.primary($t('saved_settings'));
|
toastManager.primary($t('saved_settings'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_update_settings'));
|
handleError(error, $t('errors.unable_to_update_settings'));
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateMyPreferences } from '@immich/sdk';
|
import { updateMyPreferences } from '@immich/sdk';
|
||||||
import { Button, Field, Switch, toastManager } from '@immich/ui';
|
import { Button, Field, Switch, toastManager } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
let emailNotificationsEnabled = $state($preferences?.emailNotifications?.enabled ?? true);
|
let emailNotificationsEnabled = $state(authManager.preferences.emailNotifications?.enabled ?? true);
|
||||||
let albumInviteNotificationEnabled = $state($preferences?.emailNotifications?.albumInvite ?? true);
|
let albumInviteNotificationEnabled = $state(authManager.preferences.emailNotifications?.albumInvite ?? true);
|
||||||
let albumUpdateNotificationEnabled = $state($preferences?.emailNotifications?.albumUpdate ?? true);
|
let albumUpdateNotificationEnabled = $state(authManager.preferences.emailNotifications?.albumUpdate ?? true);
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await updateMyPreferences({
|
const response = await updateMyPreferences({
|
||||||
userPreferencesUpdateDto: {
|
userPreferencesUpdateDto: {
|
||||||
emailNotifications: {
|
emailNotifications: {
|
||||||
enabled: emailNotificationsEnabled,
|
enabled: emailNotificationsEnabled,
|
||||||
|
|
@ -22,10 +22,7 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
$preferences.emailNotifications.enabled = data.emailNotifications.enabled;
|
authManager.setPreferences(response);
|
||||||
$preferences.emailNotifications.albumInvite = data.emailNotifications.albumInvite;
|
|
||||||
$preferences.emailNotifications.albumUpdate = data.emailNotifications.albumUpdate;
|
|
||||||
|
|
||||||
toastManager.primary($t('saved_settings'));
|
toastManager.primary($t('saved_settings'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_update_settings'));
|
handleError(error, $t('errors.unable_to_update_settings'));
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,22 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { oauth } from '$lib/utils';
|
import { oauth } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { type UserAdminResponseDto } from '@immich/sdk';
|
|
||||||
import { Button, LoadingSpinner, toastManager } from '@immich/ui';
|
import { Button, LoadingSpinner, toastManager } from '@immich/ui';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: UserAdminResponseDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { user = $bindable() }: Props = $props();
|
|
||||||
|
|
||||||
let loading = $state(true);
|
let loading = $state(true);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (oauth.isCallback(globalThis.location)) {
|
if (oauth.isCallback(globalThis.location)) {
|
||||||
try {
|
try {
|
||||||
loading = true;
|
loading = true;
|
||||||
user = await oauth.link(globalThis.location);
|
const response = await oauth.link(globalThis.location);
|
||||||
|
authManager.setUser(response);
|
||||||
toastManager.primary($t('linked_oauth_account'));
|
toastManager.primary($t('linked_oauth_account'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_link_oauth_account'));
|
handleError(error, $t('errors.unable_to_link_oauth_account'));
|
||||||
|
|
@ -35,7 +30,8 @@
|
||||||
|
|
||||||
const handleUnlink = async () => {
|
const handleUnlink = async () => {
|
||||||
try {
|
try {
|
||||||
user = await oauth.unlink();
|
const response = await oauth.unlink();
|
||||||
|
authManager.setUser(response);
|
||||||
toastManager.primary($t('unlinked_oauth_account'));
|
toastManager.primary($t('unlinked_oauth_account'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_unlink_account'));
|
handleError(error, $t('errors.unable_to_unlink_account'));
|
||||||
|
|
@ -51,7 +47,7 @@
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
</div>
|
</div>
|
||||||
{:else if featureFlagsManager.value.oauth}
|
{:else if featureFlagsManager.value.oauth}
|
||||||
{#if user.oauthId}
|
{#if authManager.user.oauthId}
|
||||||
<Button shape="round" size="small" onclick={() => handleUnlink()}>{$t('unlink_oauth')}</Button>
|
<Button shape="round" size="small" onclick={() => handleUnlink()}>{$t('unlink_oauth')}</Button>
|
||||||
{:else}
|
{:else}
|
||||||
<Button shape="round" size="small" onclick={() => oauth.authorize(globalThis.location)}
|
<Button shape="round" size="small" onclick={() => oauth.authorize(globalThis.location)}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,6 @@
|
||||||
inTimeline: boolean;
|
inTimeline: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: UserResponseDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { user }: Props = $props();
|
|
||||||
|
|
||||||
let partners: Array<PartnerSharing> = $state([]);
|
let partners: Array<PartnerSharing> = $state([]);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
|
@ -95,7 +89,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreatePartners = async () => {
|
const handleCreatePartners = async () => {
|
||||||
const users = await modalManager.show(PartnerSelectionModal, { user });
|
const users = await modalManager.show(PartnerSelectionModal, {});
|
||||||
|
|
||||||
if (!users) {
|
if (!users) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateMyUser } from '@immich/sdk';
|
import { updateMyUser } from '@immich/sdk';
|
||||||
import { Button, Field, Input, toastManager } from '@immich/ui';
|
import { Button, Field, Input, toastManager } from '@immich/ui';
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
import { createBubbler, preventDefault } from 'svelte/legacy';
|
import { createBubbler, preventDefault } from 'svelte/legacy';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
let editedUser = $state(cloneDeep($user));
|
let editedUser = $state(cloneDeep(authManager.user));
|
||||||
const bubble = createBubbler();
|
const bubble = createBubbler();
|
||||||
|
|
||||||
const handleSaveProfile = async () => {
|
const handleSaveProfile = async () => {
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(editedUser, data);
|
Object.assign(editedUser, data);
|
||||||
$user = data;
|
authManager.setUser(data);
|
||||||
|
|
||||||
toastManager.primary($t('saved_profile'));
|
toastManager.primary($t('saved_profile'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
import { dateFormats } from '$lib/constants';
|
import { dateFormats } from '$lib/constants';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { preferences, user } from '$lib/stores/user.store';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { setSupportBadgeVisibility } from '$lib/utils/purchase-utils';
|
import { setSupportBadgeVisibility } from '$lib/utils/purchase-utils';
|
||||||
import {
|
import {
|
||||||
|
|
@ -30,12 +29,12 @@
|
||||||
const serverInfo = await getAboutInfo();
|
const serverInfo = await getAboutInfo();
|
||||||
isServerProduct = serverInfo.licensed;
|
isServerProduct = serverInfo.licensed;
|
||||||
|
|
||||||
const userInfo = await getMyUser();
|
const response = await getMyUser();
|
||||||
if (userInfo.license) {
|
if (response.license) {
|
||||||
$user = { ...$user, license: userInfo.license };
|
authManager.setUser(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isServerProduct && $user.isAdmin) {
|
if (isServerProduct && authManager.user.isAdmin) {
|
||||||
serverPurchaseInfo = await getServerPurchaseInfo();
|
serverPurchaseInfo = await getServerPurchaseInfo();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -111,7 +110,7 @@
|
||||||
<SettingSwitch
|
<SettingSwitch
|
||||||
title={$t('show_supporter_badge')}
|
title={$t('show_supporter_badge')}
|
||||||
subtitle={$t('show_supporter_badge_description')}
|
subtitle={$t('show_supporter_badge_description')}
|
||||||
bind:checked={$preferences.purchase.showSupportBadge}
|
bind:checked={authManager.preferences.purchase.showSupportBadge}
|
||||||
onToggle={setSupportBadgeVisibility}
|
onToggle={setSupportBadgeVisibility}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -128,7 +127,7 @@
|
||||||
{$t('purchase_server_title')}
|
{$t('purchase_server_title')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{#if $user.isAdmin && serverPurchaseInfo?.activatedAt}
|
{#if authManager.user.isAdmin && serverPurchaseInfo?.activatedAt}
|
||||||
<p class="dark:text-white text-sm mt-1 col-start-2">
|
<p class="dark:text-white text-sm mt-1 col-start-2">
|
||||||
{$t('purchase_activated_time', {
|
{$t('purchase_activated_time', {
|
||||||
values: {
|
values: {
|
||||||
|
|
@ -142,7 +141,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $user.isAdmin}
|
{#if authManager.user.isAdmin}
|
||||||
<div class="text-right mt-4">
|
<div class="text-right mt-4">
|
||||||
<Button shape="round" size="small" color="danger" onclick={removeServerProductKey}
|
<Button shape="round" size="small" color="danger" onclick={removeServerProductKey}
|
||||||
>{$t('purchase_button_remove_key')}</Button
|
>{$t('purchase_button_remove_key')}</Button
|
||||||
|
|
@ -159,11 +158,11 @@
|
||||||
<p class="text-primary font-semibold text-lg">
|
<p class="text-primary font-semibold text-lg">
|
||||||
{$t('purchase_individual_title')}
|
{$t('purchase_individual_title')}
|
||||||
</p>
|
</p>
|
||||||
{#if $user.license?.activatedAt}
|
{#if authManager.user.license?.activatedAt}
|
||||||
<p class="dark:text-white text-sm mt-1 col-start-2">
|
<p class="dark:text-white text-sm mt-1 col-start-2">
|
||||||
{$t('purchase_activated_time', {
|
{$t('purchase_activated_time', {
|
||||||
values: {
|
values: {
|
||||||
date: new Date($user.license?.activatedAt).toLocaleString($locale, dateFormats.settings),
|
date: new Date(authManager.user.license?.activatedAt).toLocaleString($locale, dateFormats.settings),
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
import UserUsageStatistic from '$lib/components/user-settings-page/user-usage-statistic.svelte';
|
import UserUsageStatistic from '$lib/components/user-settings-page/user-usage-statistic.svelte';
|
||||||
import { OpenQueryParam, QueryParameter } from '$lib/constants';
|
import { OpenQueryParam, QueryParameter } from '$lib/constants';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { oauth } from '$lib/utils';
|
import { oauth } from '$lib/utils';
|
||||||
import { type ApiKeyResponseDto, type SessionResponseDto } from '@immich/sdk';
|
import { type ApiKeyResponseDto, type SessionResponseDto } from '@immich/sdk';
|
||||||
import {
|
import {
|
||||||
|
|
@ -120,7 +119,7 @@
|
||||||
subtitle={$t('manage_your_oauth_connection')}
|
subtitle={$t('manage_your_oauth_connection')}
|
||||||
isOpen={oauthOpen || undefined}
|
isOpen={oauthOpen || undefined}
|
||||||
>
|
>
|
||||||
<OAuthSettings user={$user} />
|
<OAuthSettings />
|
||||||
</SettingAccordion>
|
</SettingAccordion>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
@ -139,7 +138,7 @@
|
||||||
title={$t('partner_sharing')}
|
title={$t('partner_sharing')}
|
||||||
subtitle={$t('manage_sharing_with_partners')}
|
subtitle={$t('manage_sharing_with_partners')}
|
||||||
>
|
>
|
||||||
<PartnerSettings user={$user} />
|
<PartnerSettings />
|
||||||
</SettingAccordion>
|
</SettingAccordion>
|
||||||
|
|
||||||
<SettingAccordion
|
<SettingAccordion
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import {
|
import {
|
||||||
|
|
@ -157,7 +157,7 @@ class ActivityManager {
|
||||||
const [liked] = await getActivities({
|
const [liked] = await getActivities({
|
||||||
albumId,
|
albumId,
|
||||||
assetId,
|
assetId,
|
||||||
userId: get(user).id,
|
userId: authManager.user.id,
|
||||||
$type: ReactionType.Like,
|
$type: ReactionType.Like,
|
||||||
level: assetId ? undefined : ReactionLevel.Album,
|
level: assetId ? undefined : ReactionLevel.Album,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { AssetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { AssetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
import { resetSavedUser, user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { AssetVisibility } from '@immich/sdk';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
import { timelineAssetFactory } from '@test-data/factories/asset-factory';
|
import { timelineAssetFactory } from '@test-data/factories/asset-factory';
|
||||||
|
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||||
import { userAdminFactory } from '@test-data/factories/user-factory';
|
import { userAdminFactory } from '@test-data/factories/user-factory';
|
||||||
|
|
||||||
describe('AssetMultiSelectManager', () => {
|
describe('AssetMultiSelectManager', () => {
|
||||||
|
|
@ -32,14 +33,15 @@ describe('AssetMultiSelectManager', () => {
|
||||||
const cleanup = $effect.root(() => {
|
const cleanup = $effect.root(() => {
|
||||||
expect(sut.isAllUserOwned).toBe(false);
|
expect(sut.isAllUserOwned).toBe(false);
|
||||||
|
|
||||||
user.set(user1);
|
authManager.setUser(user1);
|
||||||
|
authManager.setPreferences(preferencesFactory.build());
|
||||||
expect(sut.isAllUserOwned).toBe(true);
|
expect(sut.isAllUserOwned).toBe(true);
|
||||||
|
|
||||||
user.set(user2);
|
authManager.setUser(user2);
|
||||||
expect(sut.isAllUserOwned).toBe(false);
|
expect(sut.isAllUserOwned).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
resetSavedUser();
|
authManager.reset();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
import { AssetVisibility, type UserAdminResponseDto } from '@immich/sdk';
|
|
||||||
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||||
import { fromStore } from 'svelte/store';
|
|
||||||
|
|
||||||
export type AssetMultiSelectOptions = {
|
export type AssetMultiSelectOptions = {
|
||||||
resetOnNavigate?: boolean;
|
resetOnNavigate?: boolean;
|
||||||
};
|
};
|
||||||
export class AssetMultiSelectManager {
|
export class AssetMultiSelectManager {
|
||||||
#selectedMap = new SvelteMap<string, TimelineAsset>();
|
#selectedMap = new SvelteMap<string, TimelineAsset>();
|
||||||
#user = fromStore<UserAdminResponseDto | undefined>(user);
|
|
||||||
#userId = $derived(this.#user.current?.id);
|
|
||||||
|
|
||||||
selectAll = $state(false);
|
selectAll = $state(false);
|
||||||
startAsset = $state<TimelineAsset | null>(null);
|
startAsset = $state<TimelineAsset | null>(null);
|
||||||
|
|
@ -23,12 +20,16 @@ export class AssetMultiSelectManager {
|
||||||
selectionActive = $derived(this.#selectedMap.size > 0);
|
selectionActive = $derived(this.#selectedMap.size > 0);
|
||||||
|
|
||||||
assets = $derived(Array.from(this.#selectedMap.values()));
|
assets = $derived(Array.from(this.#selectedMap.values()));
|
||||||
ownedAssets = $derived(this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets);
|
ownedAssets = $derived(
|
||||||
|
authManager.authenticated ? this.assets.filter((asset) => asset.ownerId === authManager.user.id) : this.assets,
|
||||||
|
);
|
||||||
|
|
||||||
isAllTrashed = $derived(this.assets.every((asset) => asset.isTrashed));
|
isAllTrashed = $derived(this.assets.every((asset) => asset.isTrashed));
|
||||||
isAllArchived = $derived(this.assets.every((asset) => asset.visibility === AssetVisibility.Archive));
|
isAllArchived = $derived(this.assets.every((asset) => asset.visibility === AssetVisibility.Archive));
|
||||||
isAllFavorite = $derived(this.assets.every((asset) => asset.isFavorite));
|
isAllFavorite = $derived(this.assets.every((asset) => asset.isFavorite));
|
||||||
isAllUserOwned = $derived(this.assets.every((asset) => asset.ownerId === this.#userId));
|
isAllUserOwned = $derived(
|
||||||
|
authManager.authenticated && this.assets.every((asset) => asset.ownerId === authManager.user.id),
|
||||||
|
);
|
||||||
|
|
||||||
#unsubscribe?: () => void;
|
#unsubscribe?: () => void;
|
||||||
|
|
||||||
|
|
@ -44,7 +45,9 @@ export class AssetMultiSelectManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
getOwnedAssets() {
|
getOwnedAssets() {
|
||||||
return this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets;
|
return authManager.authenticated
|
||||||
|
? this.assets.filter((asset) => asset.ownerId === authManager.user.id)
|
||||||
|
: this.assets;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSelectedAsset(assetId: string) {
|
hasSelectedAsset(assetId: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,86 @@
|
||||||
|
import { browser } from '$app/environment';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { isSharedLinkRoute } from '$lib/utils/navigation';
|
import { isSharedLinkRoute } from '$lib/utils/navigation';
|
||||||
import { getAboutInfo, logout, type UserAdminResponseDto } from '@immich/sdk';
|
import {
|
||||||
|
getAboutInfo,
|
||||||
|
getMyPreferences,
|
||||||
|
getMyUser,
|
||||||
|
logout,
|
||||||
|
type UserAdminResponseDto,
|
||||||
|
type UserPreferencesResponseDto,
|
||||||
|
} from '@immich/sdk';
|
||||||
|
|
||||||
class AuthManager {
|
class AuthManager {
|
||||||
isPurchased = $state(false);
|
isPurchased = $state(false);
|
||||||
isSharedLink = $derived(isSharedLinkRoute(page.route?.id));
|
isSharedLink = $derived(isSharedLinkRoute(page.route?.id));
|
||||||
params = $derived(this.isSharedLink ? { key: page.params.key, slug: page.params.slug } : {});
|
params = $derived(this.isSharedLink ? { key: page.params.key, slug: page.params.slug } : {});
|
||||||
|
|
||||||
constructor() {
|
#user = $state<UserAdminResponseDto>();
|
||||||
eventManager.on({
|
#preferences = $state<UserPreferencesResponseDto>();
|
||||||
AuthUserLoaded: (user) => this.onAuthUserLoaded(user),
|
|
||||||
});
|
get authenticated() {
|
||||||
|
return !!(this.#user && this.#preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onAuthUserLoaded(user: UserAdminResponseDto) {
|
get user() {
|
||||||
if (user.license?.activatedAt) {
|
if (!this.#user) {
|
||||||
authManager.isPurchased = true;
|
throw new TypeError('AuthManager.user is undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#user;
|
||||||
|
}
|
||||||
|
|
||||||
|
get preferences() {
|
||||||
|
if (!this.#preferences) {
|
||||||
|
throw new TypeError('AuthManager.preferences is undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#preferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
if (authManager.authenticated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverInfo = await getAboutInfo().catch(() => undefined);
|
if (!this.#hasAuthCookie()) {
|
||||||
if (serverInfo?.licensed) {
|
return;
|
||||||
authManager.isPurchased = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
try {
|
||||||
|
const [user, preferences] = await Promise.all([getMyUser(), getMyPreferences()]);
|
||||||
|
this.#preferences = preferences;
|
||||||
|
this.#user = user;
|
||||||
|
|
||||||
|
if (user.license?.activatedAt) {
|
||||||
|
this.isPurchased = true;
|
||||||
|
} else {
|
||||||
|
// check server status
|
||||||
|
const serverInfo = await getAboutInfo().catch(() => {});
|
||||||
|
if (serverInfo?.licensed) {
|
||||||
|
this.isPurchased = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventManager.emit('AuthUserLoaded', user);
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(user: UserAdminResponseDto) {
|
||||||
|
this.#user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreferences(preferences: UserPreferencesResponseDto) {
|
||||||
|
this.#preferences = preferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
async logout() {
|
async logout() {
|
||||||
|
|
@ -50,9 +105,31 @@ class AuthManager {
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.isPurchased = false;
|
this.isPurchased = false;
|
||||||
|
|
||||||
|
this.reset();
|
||||||
eventManager.emit('AuthLogout');
|
eventManager.emit('AuthLogout');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.#user = undefined;
|
||||||
|
this.#preferences = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hasAuthCookie() {
|
||||||
|
if (!browser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cookie of document.cookie.split('; ')) {
|
||||||
|
const [name] = cookie.split('=');
|
||||||
|
if (name === 'immich_is_authenticated') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const authManager = new AuthManager();
|
export const authManager = new AuthManager();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { asLocalTimeISO } from '$lib/utils/date-time';
|
import { asLocalTimeISO } from '$lib/utils/date-time';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
import { deleteMemory, type MemoryResponseDto, removeMemoryAssets, searchMemories, updateMemory } from '@immich/sdk';
|
import { deleteMemory, type MemoryResponseDto, removeMemoryAssets, searchMemories, updateMemory } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
type MemoryIndex = {
|
type MemoryIndex = {
|
||||||
memoryIndex: number;
|
memoryIndex: number;
|
||||||
|
|
@ -31,7 +30,7 @@ class MemoryManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
// loaded event might have already happened
|
// loaded event might have already happened
|
||||||
if (get(user)) {
|
if (authManager.authenticated) {
|
||||||
void this.initialize();
|
void this.initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import { getAnimateMock } from '$lib/__mocks__/animate.mock';
|
||||||
import { getIntersectionObserverMock } from '$lib/__mocks__/intersection-observer.mock';
|
import { getIntersectionObserverMock } from '$lib/__mocks__/intersection-observer.mock';
|
||||||
import { sdkMock } from '$lib/__mocks__/sdk.mock';
|
import { sdkMock } from '$lib/__mocks__/sdk.mock';
|
||||||
import { getVisualViewportMock } from '$lib/__mocks__/visual-viewport.mock';
|
import { getVisualViewportMock } from '$lib/__mocks__/visual-viewport.mock';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { calcNewDate } from '$lib/modals/timezone-utils';
|
import { calcNewDate } from '$lib/modals/timezone-utils';
|
||||||
|
import { userAdminFactory } from '@test-data/factories/user-factory';
|
||||||
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
@ -25,6 +27,8 @@ describe('DateSelectionModal component', () => {
|
||||||
vi.stubGlobal('visualViewport', getVisualViewportMock());
|
vi.stubGlobal('visualViewport', getVisualViewportMock());
|
||||||
vi.resetAllMocks();
|
vi.resetAllMocks();
|
||||||
Element.prototype.animate = getAnimateMock();
|
Element.prototype.animate = getAnimateMock();
|
||||||
|
|
||||||
|
authManager.setUser(userAdminFactory.build());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
import Combobox from '$lib/components/shared-components/combobox.svelte';
|
import Combobox from '$lib/components/shared-components/combobox.svelte';
|
||||||
import DateInput from '$lib/elements/DateInput.svelte';
|
import DateInput from '$lib/elements/DateInput.svelte';
|
||||||
import DurationInput from '$lib/elements/DurationInput.svelte';
|
import DurationInput from '$lib/elements/DurationInput.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import { getPreferredTimeZone, getTimezones, toIsoDate, type ZoneOption } from '$lib/modals/timezone-utils';
|
import { getPreferredTimeZone, getTimezones, toIsoDate, type ZoneOption } from '$lib/modals/timezone-utils';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAssets } from '@immich/sdk';
|
import { updateAssets } from '@immich/sdk';
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
let selectedOption = $derived(getPreferredTimeZone(initialDate, initialTimeZone, timezones, lastSelectedTimezone));
|
let selectedOption = $derived(getPreferredTimeZone(initialDate, initialTimeZone, timezones, lastSelectedTimezone));
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
const ids = getOwnedAssetsWithWarning(assets, $user);
|
const ids = getOwnedAssetsWithWarning(assets, authManager.user);
|
||||||
try {
|
try {
|
||||||
if (showRelative && (selectedDuration || selectedOption)) {
|
if (showRelative && (selectedDuration || selectedOption)) {
|
||||||
await updateAssets({
|
await updateAssets({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { deleteProfileImage, updateMyUser, UserAvatarColor } from '@immich/sdk';
|
import { deleteProfileImage, updateMyUser, UserAvatarColor } from '@immich/sdk';
|
||||||
import { Modal, ModalBody, toastManager } from '@immich/ui';
|
import { Modal, ModalBody, toastManager } from '@immich/ui';
|
||||||
|
|
@ -16,13 +16,14 @@
|
||||||
|
|
||||||
const onSave = async (color: UserAvatarColor) => {
|
const onSave = async (color: UserAvatarColor) => {
|
||||||
try {
|
try {
|
||||||
if ($user.profileImagePath !== '') {
|
if (authManager.user.profileImagePath !== '') {
|
||||||
await deleteProfileImage();
|
await deleteProfileImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
toastManager.primary($t('saved_profile'));
|
toastManager.primary($t('saved_profile'));
|
||||||
|
|
||||||
$user = await updateMyUser({ userUpdateMeDto: { avatarColor: color } });
|
const response = await updateMyUser({ userUpdateMeDto: { avatarColor: color } });
|
||||||
|
authManager.setUser(response);
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_save_profile'));
|
handleError(error, $t('errors.unable_to_save_profile'));
|
||||||
|
|
@ -35,7 +36,11 @@
|
||||||
<div class="grid grid-cols-2 sm:grid-cols-5 gap-4 place-items-center">
|
<div class="grid grid-cols-2 sm:grid-cols-5 gap-4 place-items-center">
|
||||||
{#each colors as color (color)}
|
{#each colors as color (color)}
|
||||||
<button type="button" onclick={() => onSave(color)}>
|
<button type="button" onclick={() => onSave(color)}>
|
||||||
<UserAvatar label={color} user={{ ...$user, profileImagePath: '', avatarColor: color }} size="xl" />
|
<UserAvatar
|
||||||
|
label={color}
|
||||||
|
user={{ ...authManager.user, profileImagePath: '', avatarColor: color }}
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { getPartners, PartnerDirection, searchUsers, type UserResponseDto } from '@immich/sdk';
|
import { getPartners, PartnerDirection, searchUsers, type UserResponseDto } from '@immich/sdk';
|
||||||
import { Button, ListButton, LoadingSpinner, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
|
import { Button, ListButton, LoadingSpinner, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: UserResponseDto;
|
|
||||||
onClose: (users?: UserResponseDto[]) => void;
|
onClose: (users?: UserResponseDto[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { user, onClose }: Props = $props();
|
let { onClose }: Props = $props();
|
||||||
|
|
||||||
let availableUsers: UserResponseDto[] = $state([]);
|
let availableUsers: UserResponseDto[] = $state([]);
|
||||||
let selectedUsers: UserResponseDto[] = $state([]);
|
let selectedUsers: UserResponseDto[] = $state([]);
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
let users = await searchUsers();
|
let users = await searchUsers();
|
||||||
|
|
||||||
// remove current user
|
// remove current user
|
||||||
users = users.filter((_user) => _user.id !== user.id);
|
users = users.filter(({ id }) => id !== authManager.user.id);
|
||||||
|
|
||||||
// exclude partners from the list of users available for selection
|
// exclude partners from the list of users available for selection
|
||||||
const partners = await getPartners({ direction: PartnerDirection.SharedBy });
|
const partners = await getPartners({ direction: PartnerDirection.SharedBy });
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { user } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { createProfileImage, type AssetResponseDto } from '@immich/sdk';
|
import { createProfileImage, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { FormModal, toastManager } from '@immich/ui';
|
import { FormModal, toastManager } from '@immich/ui';
|
||||||
|
|
@ -68,10 +68,10 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const file = new File([blob], 'profile-picture.png', { type: 'image/png' });
|
const file = new File([blob], 'profile-picture.png', { type: 'image/png' });
|
||||||
const { profileImagePath, profileChangedAt } = await createProfileImage({ createProfileImageDto: { file } });
|
await createProfileImage({ createProfileImageDto: { file } });
|
||||||
toastManager.primary($t('profile_picture_set'));
|
toastManager.primary($t('profile_picture_set'));
|
||||||
$user.profileImagePath = profileImagePath;
|
|
||||||
$user.profileChangedAt = profileChangedAt;
|
await authManager.refresh();
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
import SearchTagsSection from '$lib/components/shared-components/search-bar/search-tags-section.svelte';
|
import SearchTagsSection from '$lib/components/shared-components/search-bar/search-tags-section.svelte';
|
||||||
import SearchTextSection from '$lib/components/shared-components/search-bar/search-text-section.svelte';
|
import SearchTextSection from '$lib/components/shared-components/search-bar/search-text-section.svelte';
|
||||||
import { MediaType, QueryType, validQueryTypes } from '$lib/constants';
|
import { MediaType, QueryType, validQueryTypes } from '$lib/constants';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import type { SearchFilter } from '$lib/types';
|
import type { SearchFilter } from '$lib/types';
|
||||||
import { parseUtcDate } from '$lib/utils/date-time';
|
import { parseUtcDate } from '$lib/utils/date-time';
|
||||||
import { generateId } from '$lib/utils/generate-id';
|
import { generateId } from '$lib/utils/generate-id';
|
||||||
|
|
@ -193,7 +193,7 @@
|
||||||
<SearchDateSection bind:filters={filter.date} />
|
<SearchDateSection bind:filters={filter.date} />
|
||||||
|
|
||||||
<!-- RATING -->
|
<!-- RATING -->
|
||||||
{#if $preferences?.ratings.enabled}
|
{#if authManager.authenticated && authManager.preferences.ratings.enabled}
|
||||||
<SearchRatingsSection bind:rating={filter.rating} />
|
<SearchRatingsSection bind:rating={filter.rating} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Icon, Modal, ModalBody } from '@immich/ui';
|
import { Icon, Modal, ModalBody } from '@immich/ui';
|
||||||
import { mdiInformationOutline } from '@mdi/js';
|
import { mdiInformationOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
{ key: ['⇧', 'd'], action: $t('download') },
|
{ key: ['⇧', 'd'], action: $t('download') },
|
||||||
{ key: ['Space'], action: $t('play_or_pause_video') },
|
{ key: ['Space'], action: $t('play_or_pause_video') },
|
||||||
{ key: ['Del'], action: $t('trash_delete_asset'), info: $t('shift_to_permanent_delete') },
|
{ key: ['Del'], action: $t('trash_delete_asset'), info: $t('shift_to_permanent_delete') },
|
||||||
...($preferences?.ratings.enabled
|
...(authManager.authenticated && authManager.preferences.ratings.enabled
|
||||||
? [{ key: ['1-5'], action: $t('rate_asset'), info: $t('zero_to_clear_rating') }]
|
? [{ key: ['1-5'], action: $t('rate_asset'), info: $t('zero_to_clear_rating') }]
|
||||||
: []),
|
: []),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,9 @@ import AlbumAddUsersModal from '$lib/modals/AlbumAddUsersModal.svelte';
|
||||||
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
||||||
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
|
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { createAlbumAndRedirect } from '$lib/utils/album-utils';
|
import { createAlbumAndRedirect } from '$lib/utils/album-utils';
|
||||||
import { downloadArchive } from '$lib/utils/asset-utils';
|
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||||
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
import {
|
import {
|
||||||
|
|
@ -32,7 +30,6 @@ import {
|
||||||
import { modalManager, toastManager, type ActionItem } from '@immich/ui';
|
import { modalManager, toastManager, type ActionItem } from '@immich/ui';
|
||||||
import { mdiLink, mdiPlus, mdiPlusBoxOutline, mdiShareVariantOutline, mdiUpload } from '@mdi/js';
|
import { mdiLink, mdiPlus, mdiPlusBoxOutline, mdiShareVariantOutline, mdiUpload } from '@mdi/js';
|
||||||
import { type MessageFormatter } from 'svelte-i18n';
|
import { type MessageFormatter } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export const getAlbumsActions = ($t: MessageFormatter) => {
|
export const getAlbumsActions = ($t: MessageFormatter) => {
|
||||||
const Create: ActionItem = {
|
const Create: ActionItem = {
|
||||||
|
|
@ -45,7 +42,7 @@ export const getAlbumsActions = ($t: MessageFormatter) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAlbumActions = ($t: MessageFormatter, album: AlbumResponseDto) => {
|
export const getAlbumActions = ($t: MessageFormatter, album: AlbumResponseDto) => {
|
||||||
const isOwned = get(user).id === album.ownerId;
|
const isOwned = authManager.user.id === album.ownerId;
|
||||||
|
|
||||||
const Share: ActionItem = {
|
const Share: ActionItem = {
|
||||||
title: $t('share'),
|
title: $t('share'),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { getAssetActions, handleDownloadAsset } from '$lib/services/asset.service';
|
import { getAssetActions, handleDownloadAsset } from '$lib/services/asset.service';
|
||||||
import { user as userStore } from '$lib/stores/user.store';
|
|
||||||
import { setSharedLink } from '$lib/utils';
|
import { setSharedLink } from '$lib/utils';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
import { getAssetInfo } from '@immich/sdk';
|
import { getAssetInfo } from '@immich/sdk';
|
||||||
import { toastManager } from '@immich/ui';
|
import { toastManager } from '@immich/ui';
|
||||||
import { assetFactory } from '@test-data/factories/asset-factory';
|
import { assetFactory } from '@test-data/factories/asset-factory';
|
||||||
|
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||||
import { sharedLinkFactory } from '@test-data/factories/shared-link-factory';
|
import { sharedLinkFactory } from '@test-data/factories/shared-link-factory';
|
||||||
import { userAdminFactory } from '@test-data/factories/user-factory';
|
import { userAdminFactory } from '@test-data/factories/user-factory';
|
||||||
import { vitest } from 'vitest';
|
import { vitest } from 'vitest';
|
||||||
|
|
@ -32,11 +33,15 @@ vitest.mock('$lib/utils', async () => {
|
||||||
|
|
||||||
describe('AssetService', () => {
|
describe('AssetService', () => {
|
||||||
describe('getAssetActions', () => {
|
describe('getAssetActions', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
authManager.setPreferences(preferencesFactory.build());
|
||||||
|
});
|
||||||
|
|
||||||
it('should allow shared link downloads if the user owns the asset and shared link downloads are disabled', () => {
|
it('should allow shared link downloads if the user owns the asset and shared link downloads are disabled', () => {
|
||||||
const ownerId = 'owner';
|
const ownerId = 'owner';
|
||||||
const user = userAdminFactory.build({ id: ownerId });
|
const user = userAdminFactory.build({ id: ownerId });
|
||||||
const asset = assetFactory.build({ ownerId });
|
const asset = assetFactory.build({ ownerId });
|
||||||
userStore.set(user);
|
authManager.setUser(user);
|
||||||
setSharedLink(sharedLinkFactory.build({ allowDownload: false }));
|
setSharedLink(sharedLinkFactory.build({ allowDownload: false }));
|
||||||
const assetActions = getAssetActions(() => '', asset);
|
const assetActions = getAssetActions(() => '', asset);
|
||||||
expect(assetActions.SharedLinkDownload.$if?.()).toStrictEqual(true);
|
expect(assetActions.SharedLinkDownload.$if?.()).toStrictEqual(true);
|
||||||
|
|
@ -46,7 +51,7 @@ describe('AssetService', () => {
|
||||||
const ownerId = 'owner';
|
const ownerId = 'owner';
|
||||||
const user = userAdminFactory.build({ id: 'non-owner' });
|
const user = userAdminFactory.build({ id: 'non-owner' });
|
||||||
const asset = assetFactory.build({ ownerId });
|
const asset = assetFactory.build({ ownerId });
|
||||||
userStore.set(user);
|
authManager.setUser(user);
|
||||||
setSharedLink(sharedLinkFactory.build({ allowDownload: false }));
|
setSharedLink(sharedLinkFactory.build({ allowDownload: false }));
|
||||||
const assetActions = getAssetActions(() => '', asset);
|
const assetActions = getAssetActions(() => '', asset);
|
||||||
expect(assetActions.SharedLinkDownload.$if?.()).toStrictEqual(false);
|
expect(assetActions.SharedLinkDownload.$if?.()).toStrictEqual(false);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import AssetAddToAlbumModal from '$lib/modals/AssetAddToAlbumModal.svelte';
|
import AssetAddToAlbumModal from '$lib/modals/AssetAddToAlbumModal.svelte';
|
||||||
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
|
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
|
||||||
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
|
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
|
||||||
import { user as authUser, preferences } from '$lib/stores/user.store';
|
|
||||||
import { getAssetMediaUrl, getSharedLink, sleep } from '$lib/utils';
|
import { getAssetMediaUrl, getSharedLink, sleep } from '$lib/utils';
|
||||||
import { downloadUrl } from '$lib/utils/asset-utils';
|
import { downloadUrl } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
|
@ -46,7 +45,6 @@ import {
|
||||||
mdiTune,
|
mdiTune,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import type { MessageFormatter } from 'svelte-i18n';
|
import type { MessageFormatter } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export const getAssetBulkActions = ($t: MessageFormatter) => {
|
export const getAssetBulkActions = ($t: MessageFormatter) => {
|
||||||
const ownedAssets = assetMultiSelectManager.ownedAssets;
|
const ownedAssets = assetMultiSelectManager.ownedAssets;
|
||||||
|
|
@ -95,15 +93,14 @@ export const getAssetBulkActions = ($t: MessageFormatter) => {
|
||||||
|
|
||||||
export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) => {
|
export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) => {
|
||||||
const sharedLink = getSharedLink();
|
const sharedLink = getSharedLink();
|
||||||
const currentAuthUser = get(authUser);
|
const authUser = authManager.authenticated ? authManager.user : undefined;
|
||||||
const userPreferences = get(preferences);
|
const isOwner = !!(authUser && authUser.id === asset.ownerId);
|
||||||
const isOwner = !!(currentAuthUser && currentAuthUser.id === asset.ownerId);
|
|
||||||
|
|
||||||
const Share: ActionItem = {
|
const Share: ActionItem = {
|
||||||
title: $t('share'),
|
title: $t('share'),
|
||||||
icon: mdiShareVariantOutline,
|
icon: mdiShareVariantOutline,
|
||||||
type: $t('assets'),
|
type: $t('assets'),
|
||||||
$if: () => !!(currentAuthUser && !asset.isTrashed && asset.visibility !== AssetVisibility.Locked),
|
$if: () => !!(authUser && !asset.isTrashed && asset.visibility !== AssetVisibility.Locked),
|
||||||
onAction: () => modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] }),
|
onAction: () => modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -112,7 +109,7 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
|
||||||
icon: mdiDownload,
|
icon: mdiDownload,
|
||||||
shortcuts: { key: 'd', shift: true },
|
shortcuts: { key: 'd', shift: true },
|
||||||
type: $t('assets'),
|
type: $t('assets'),
|
||||||
$if: () => !!currentAuthUser,
|
$if: () => !!authUser,
|
||||||
onAction: () => handleDownloadAsset(asset, { edited: true }),
|
onAction: () => handleDownloadAsset(asset, { edited: true }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -120,7 +117,7 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
|
||||||
title: $t('download_original'),
|
title: $t('download_original'),
|
||||||
icon: mdiDownloadBox,
|
icon: mdiDownloadBox,
|
||||||
type: $t('assets'),
|
type: $t('assets'),
|
||||||
$if: () => !!currentAuthUser && asset.isEdited,
|
$if: () => !!authUser && asset.isEdited,
|
||||||
onAction: () => handleDownloadAsset(asset, { edited: false }),
|
onAction: () => handleDownloadAsset(asset, { edited: false }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -218,7 +215,7 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
|
||||||
title: $t('add_tag'),
|
title: $t('add_tag'),
|
||||||
icon: mdiTagPlusOutline,
|
icon: mdiTagPlusOutline,
|
||||||
type: $t('assets'),
|
type: $t('assets'),
|
||||||
$if: () => userPreferences.tags.enabled,
|
$if: () => authManager.authenticated && authManager.preferences.tags.enabled,
|
||||||
onAction: () => modalManager.show(AssetTagModal, { assetIds: [asset.id] }),
|
onAction: () => modalManager.show(AssetTagModal, { assetIds: [asset.id] }),
|
||||||
shortcuts: { key: 't' },
|
shortcuts: { key: 't' },
|
||||||
};
|
};
|
||||||
|
|
@ -315,7 +312,10 @@ export const handleDownloadAsset = async (asset: AssetResponseDto, { edited }: {
|
||||||
|
|
||||||
if (asset.livePhotoVideoId) {
|
if (asset.livePhotoVideoId) {
|
||||||
const motionAsset = await getAssetInfo({ ...authManager.params, id: asset.livePhotoVideoId });
|
const motionAsset = await getAssetInfo({ ...authManager.params, id: asset.livePhotoVideoId });
|
||||||
if (!isAndroidMotionVideo(motionAsset) || get(preferences)?.download.includeEmbeddedVideos) {
|
if (
|
||||||
|
!isAndroidMotionVideo(motionAsset) ||
|
||||||
|
(authManager.authenticated && authManager.preferences.download.includeEmbeddedVideos)
|
||||||
|
) {
|
||||||
const motionFilename = motionAsset.originalFileName;
|
const motionFilename = motionAsset.originalFileName;
|
||||||
const lastDotIndex = motionFilename.lastIndexOf('.');
|
const lastDotIndex = motionFilename.lastIndexOf('.');
|
||||||
const motionDownloadFilename =
|
const motionDownloadFilename =
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
|
import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
|
||||||
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
|
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
|
||||||
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
|
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { user as authUser } from '$lib/stores/user.store';
|
|
||||||
import type { HeaderButtonActionItem } from '$lib/types';
|
import type { HeaderButtonActionItem } from '$lib/types';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
|
|
@ -32,7 +32,6 @@ import {
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { MessageFormatter } from 'svelte-i18n';
|
import type { MessageFormatter } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export const getUserAdminsActions = ($t: MessageFormatter) => {
|
export const getUserAdminsActions = ($t: MessageFormatter) => {
|
||||||
const Create: ActionItem = {
|
const Create: ActionItem = {
|
||||||
|
|
@ -64,7 +63,7 @@ export const getUserAdminActions = ($t: MessageFormatter, user: UserAdminRespons
|
||||||
title: $t('delete'),
|
title: $t('delete'),
|
||||||
type: $t('command'),
|
type: $t('command'),
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
$if: () => get(authUser).id !== user.id && !user.deletedAt,
|
$if: () => authManager.user.id !== user.id && !user.deletedAt,
|
||||||
onAction: () => modalManager.show(UserDeleteConfirmModal, { user }),
|
onAction: () => modalManager.show(UserDeleteConfirmModal, { user }),
|
||||||
shortcuts: { key: 'Backspace' },
|
shortcuts: { key: 'Backspace' },
|
||||||
shortcutOptions: { ignoreInputFields: true },
|
shortcutOptions: { ignoreInputFields: true },
|
||||||
|
|
@ -89,7 +88,7 @@ export const getUserAdminActions = ($t: MessageFormatter, user: UserAdminRespons
|
||||||
icon: mdiLockReset,
|
icon: mdiLockReset,
|
||||||
title: $t('reset_password'),
|
title: $t('reset_password'),
|
||||||
type: $t('command'),
|
type: $t('command'),
|
||||||
$if: () => get(authUser).id !== user.id,
|
$if: () => authManager.user.id !== user.id,
|
||||||
onAction: () => handleResetPasswordUserAdmin(user),
|
onAction: () => handleResetPasswordUserAdmin(user),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
|
||||||
import { type UserAdminResponseDto, type UserPreferencesResponseDto } from '@immich/sdk';
|
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
export const user = writable<UserAdminResponseDto>();
|
|
||||||
export const preferences = writable<UserPreferencesResponseDto>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the store to its initial undefined value. Make sure to
|
|
||||||
* only do this _after_ redirecting to an unauthenticated page.
|
|
||||||
*/
|
|
||||||
export const resetSavedUser = () => {
|
|
||||||
user.set(undefined as unknown as UserAdminResponseDto);
|
|
||||||
preferences.set(undefined as unknown as UserPreferencesResponseDto);
|
|
||||||
};
|
|
||||||
|
|
||||||
eventManager.on({
|
|
||||||
AuthLogout: () => resetSavedUser(),
|
|
||||||
});
|
|
||||||
|
|
@ -17,7 +17,6 @@ import {
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { io, type Socket } from 'socket.io-client';
|
import { io, type Socket } from 'socket.io-client';
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import { user } from './user.store';
|
|
||||||
|
|
||||||
interface AppRestartEvent {
|
interface AppRestartEvent {
|
||||||
isMaintenanceMode: boolean;
|
isMaintenanceMode: boolean;
|
||||||
|
|
@ -88,7 +87,11 @@ websocket
|
||||||
|
|
||||||
export const openWebsocketConnection = () => {
|
export const openWebsocketConnection = () => {
|
||||||
try {
|
try {
|
||||||
if (get(user) || get(websocketStore.serverRestarting) || page.url.pathname.startsWith(Route.maintenanceMode())) {
|
if (
|
||||||
|
authManager.authenticated ||
|
||||||
|
get(websocketStore.serverRestarting) ||
|
||||||
|
page.url.pathname.startsWith(Route.maintenanceMode())
|
||||||
|
) {
|
||||||
websocket.connect();
|
websocket.connect();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { downloadRequest, withError } from '$lib/utils';
|
import { downloadRequest, withError } from '$lib/utils';
|
||||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
|
|
@ -26,7 +25,6 @@ import {
|
||||||
type DownloadInfoDto,
|
type DownloadInfoDto,
|
||||||
type ExifResponseDto,
|
type ExifResponseDto,
|
||||||
type StackResponseDto,
|
type StackResponseDto,
|
||||||
type UserPreferencesResponseDto,
|
|
||||||
type UserResponseDto,
|
type UserResponseDto,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { toastManager } from '@immich/ui';
|
import { toastManager } from '@immich/ui';
|
||||||
|
|
@ -102,9 +100,8 @@ export const downloadUrl = (url: string, filename: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const downloadArchive = async (fileName: string, options: Omit<DownloadInfoDto, 'archiveSize'>) => {
|
export const downloadArchive = async (fileName: string, options: Omit<DownloadInfoDto, 'archiveSize'>) => {
|
||||||
const $preferences = get<UserPreferencesResponseDto | undefined>(preferences);
|
const archiveSize = authManager.authenticated ? authManager.preferences.download.archiveSize : undefined;
|
||||||
const dto = { ...options, archiveSize: $preferences?.download.archiveSize };
|
const dto = { ...options, archiveSize };
|
||||||
|
|
||||||
const [error, downloadInfo] = await withError(() => getDownloadInfo({ ...authManager.params, downloadInfoDto: dto }));
|
const [error, downloadInfo] = await withError(() => getDownloadInfo({ ...authManager.params, downloadInfoDto: dto }));
|
||||||
if (error) {
|
if (error) {
|
||||||
const $t = get(t);
|
const $t = get(t);
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,45 @@
|
||||||
import { browser } from '$app/environment';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { preferences as preferences$, user as user$ } from '$lib/stores/user.store';
|
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { getMyPreferences, getMyUser, getStorage } from '@immich/sdk';
|
import { getStorage } from '@immich/sdk';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export interface AuthOptions {
|
export interface AuthOptions {
|
||||||
admin?: true;
|
admin?: true;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadUser = async () => {
|
|
||||||
try {
|
|
||||||
let user = get(user$);
|
|
||||||
let preferences = get(preferences$);
|
|
||||||
|
|
||||||
if ((!user || !preferences) && hasAuthCookie()) {
|
|
||||||
[user, preferences] = await Promise.all([getMyUser(), getMyPreferences()]);
|
|
||||||
user$.set(user);
|
|
||||||
preferences$.set(preferences);
|
|
||||||
eventManager.emit('AuthUserLoaded', user);
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasAuthCookie = (): boolean => {
|
|
||||||
if (!browser) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const cookie of document.cookie.split('; ')) {
|
|
||||||
const [name] = cookie.split('=');
|
|
||||||
if (name === 'immich_is_authenticated') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const authenticate = async (url: URL, options?: AuthOptions) => {
|
export const authenticate = async (url: URL, options?: AuthOptions) => {
|
||||||
const { public: publicRoute, admin: adminRoute } = options || {};
|
const { public: publicRoute, admin: adminRoute } = options || {};
|
||||||
const user = await loadUser();
|
await authManager.load();
|
||||||
|
|
||||||
if (publicRoute) {
|
if (publicRoute) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!authManager.authenticated) {
|
||||||
redirect(307, Route.login({ continue: url.pathname + url.search }));
|
redirect(307, Route.login({ continue: url.pathname + url.search }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adminRoute && !user.isAdmin) {
|
if (adminRoute && !authManager.user.isAdmin) {
|
||||||
redirect(307, Route.photos());
|
redirect(307, Route.photos());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const requestServerInfo = async () => {
|
export const requestServerInfo = async () => {
|
||||||
if (get(user$)) {
|
if (authManager.authenticated) {
|
||||||
const data = await getStorage();
|
const data = await getStorage();
|
||||||
userInteraction.serverInfo = data;
|
userInteraction.serverInfo = data;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAccountAge = (): number => {
|
export const getAccountAge = (): number => {
|
||||||
const user = get(user$);
|
if (!authManager.authenticated) {
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdDate = DateTime.fromISO(user.createdAt);
|
const createdDate = DateTime.fromISO(authManager.user.createdAt);
|
||||||
const now = DateTime.now();
|
const now = DateTime.now();
|
||||||
const accountAge = now.diff(createdDate, 'days').days.toFixed(0);
|
const accountAge = now.diff(createdDate, 'days').days.toFixed(0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { CastDestinationType, CastState, type ICastDestination } from '$lib/managers/cast-manager.svelte';
|
import { CastDestinationType, CastState, type ICastDestination } from '$lib/managers/cast-manager.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import 'chromecast-caf-sender';
|
import 'chromecast-caf-sender';
|
||||||
import { Duration } from 'luxon';
|
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';
|
const FRAMEWORK_LINK = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1';
|
||||||
|
|
||||||
|
|
@ -26,8 +25,7 @@ export class GCastDestination implements ICastDestination {
|
||||||
private currentUrl: string | null = null;
|
private currentUrl: string | null = null;
|
||||||
|
|
||||||
async initialize(): Promise<boolean> {
|
async initialize(): Promise<boolean> {
|
||||||
const preferencesStore = get(preferences);
|
if (!authManager.authenticated || authManager.preferences.cast.gCastEnabled) {
|
||||||
if (!preferencesStore || !preferencesStore.cast.gCastEnabled) {
|
|
||||||
this.isAvailable = false;
|
this.isAvailable = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
||||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||||
import { resetSavedUser, user } from '$lib/stores/user.store';
|
|
||||||
import { UploadState } from '$lib/types';
|
import { UploadState } from '$lib/types';
|
||||||
import * as utils from '$lib/utils';
|
import * as utils from '$lib/utils';
|
||||||
import { AssetMediaStatus, type AssetMediaResponseDto, type UserAdminResponseDto } from '@immich/sdk';
|
import { AssetMediaStatus, type AssetMediaResponseDto, type UserAdminResponseDto } from '@immich/sdk';
|
||||||
|
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { fileUploadHandler } from './file-uploader';
|
import { fileUploadHandler } from './file-uploader';
|
||||||
|
|
@ -18,7 +19,7 @@ describe('fileUploader error handling', () => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
vi.spyOn(uploadManager, 'getExtensions').mockReturnValue(['.jpg']);
|
vi.spyOn(uploadManager, 'getExtensions').mockReturnValue(['.jpg']);
|
||||||
uploadAssetsStore.reset();
|
uploadAssetsStore.reset();
|
||||||
resetSavedUser();
|
authManager.reset();
|
||||||
|
|
||||||
// Stub out crypto to avoid that branch
|
// Stub out crypto to avoid that branch
|
||||||
vi.stubGlobal('crypto', undefined);
|
vi.stubGlobal('crypto', undefined);
|
||||||
|
|
@ -31,7 +32,7 @@ describe('fileUploader error handling', () => {
|
||||||
describe(`for ${name}`, () => {
|
describe(`for ${name}`, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
if (mockUser) {
|
if (mockUser) {
|
||||||
user.set(mockUserObject);
|
authManager.setUser(mockUserObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -58,9 +59,10 @@ describe('fileUploader error handling', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should suppress errors on logout', async () => {
|
it('should suppress errors on logout', async () => {
|
||||||
user.set(mockUserObject);
|
authManager.setUser(mockUserObject);
|
||||||
|
authManager.setPreferences(preferencesFactory.build());
|
||||||
vi.spyOn(utils, 'uploadRequest').mockImplementationOnce(() => {
|
vi.spyOn(utils, 'uploadRequest').mockImplementationOnce(() => {
|
||||||
resetSavedUser();
|
authManager.reset();
|
||||||
return Promise.reject(mockError);
|
return Promise.reject(mockError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
||||||
import { addAssetsToAlbums } from '$lib/services/album.service';
|
import { addAssetsToAlbums } from '$lib/services/album.service';
|
||||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { UploadState } from '$lib/types';
|
import { UploadState } from '$lib/types';
|
||||||
import { uploadRequest } from '$lib/utils';
|
import { uploadRequest } from '$lib/utils';
|
||||||
import { ExecutorQueue } from '$lib/utils/executor-queue';
|
import { ExecutorQueue } from '$lib/utils/executor-queue';
|
||||||
|
|
@ -145,7 +144,7 @@ async function fileUploader({
|
||||||
}: FileUploaderParams): Promise<string | undefined> {
|
}: FileUploaderParams): Promise<string | undefined> {
|
||||||
const fileCreatedAt = new Date(assetFile.lastModified).toISOString();
|
const fileCreatedAt = new Date(assetFile.lastModified).toISOString();
|
||||||
const $t = get(t);
|
const $t = get(t);
|
||||||
const wasInitiallyLoggedIn = !!get(user);
|
const wasInitiallyLoggedIn = !!authManager.authenticated;
|
||||||
|
|
||||||
uploadAssetsStore.markStarted(deviceAssetId);
|
uploadAssetsStore.markStarted(deviceAssetId);
|
||||||
|
|
||||||
|
|
@ -238,7 +237,7 @@ async function fileUploader({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If the user store no longer holds a user, it means they have logged out
|
// If the user store no longer holds a user, it means they have logged out
|
||||||
// In this case don't bother reporting any errors.
|
// In this case don't bother reporting any errors.
|
||||||
if (wasInitiallyLoggedIn && !get(user)) {
|
if (wasInitiallyLoggedIn && !authManager.authenticated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
import { PUBLIC_IMMICH_BUY_HOST, PUBLIC_IMMICH_PAY_HOST } from '$env/static/public';
|
import { PUBLIC_IMMICH_BUY_HOST, PUBLIC_IMMICH_PAY_HOST } from '$env/static/public';
|
||||||
import type { ImmichProduct } from '$lib/constants';
|
import type { ImmichProduct } from '$lib/constants';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { setServerLicense, setUserLicense, type LicenseResponseDto } from '@immich/sdk';
|
import { setServerLicense, setUserLicense, type LicenseResponseDto } from '@immich/sdk';
|
||||||
import { loadUser } from './auth';
|
|
||||||
|
|
||||||
export const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
export const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
||||||
// Send server key to user activation if user is not admin
|
// TODO is this needed?
|
||||||
const user = await loadUser();
|
await authManager.load();
|
||||||
const isServerActivation = user?.isAdmin && licenseKey.search('IMSV') !== -1;
|
|
||||||
|
const isServerActivation = authManager.user.isAdmin && licenseKey.search('IMSV') !== -1;
|
||||||
const licenseKeyDto = { licenseKey, activationKey };
|
const licenseKeyDto = { licenseKey, activationKey };
|
||||||
|
// Send server key to user activation if user is not admin
|
||||||
return isServerActivation ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });
|
return isServerActivation ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,19 @@
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { updateMyPreferences } from '@immich/sdk';
|
import { updateMyPreferences } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export const getButtonVisibility = (): boolean => {
|
export const getButtonVisibility = (): boolean => {
|
||||||
const myPreferences = get(preferences);
|
if (!authManager.authenticated) {
|
||||||
|
|
||||||
if (!myPreferences) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { purchase } = myPreferences;
|
|
||||||
|
|
||||||
const now = DateTime.now();
|
const now = DateTime.now();
|
||||||
const hideUntilDate = DateTime.fromISO(purchase.hideBuyButtonUntil);
|
const hideUntilDate = DateTime.fromISO(authManager.preferences.purchase.hideBuyButtonUntil);
|
||||||
const dayLeft = Number(now.diff(hideUntilDate, 'days').days.toFixed(0));
|
const dayLeft = Number(now.diff(hideUntilDate, 'days').days.toFixed(0));
|
||||||
|
|
||||||
return dayLeft > 0;
|
return dayLeft > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setSupportBadgeVisibility = async (value: boolean) => {
|
export const setSupportBadgeVisibility = async (value: boolean) => {
|
||||||
const response = await updateMyPreferences({
|
const response = await updateMyPreferences({ userPreferencesUpdateDto: { purchase: { showSupportBadge: value } } });
|
||||||
userPreferencesUpdateDto: {
|
authManager.setPreferences(response);
|
||||||
purchase: {
|
|
||||||
showSupportBadge: value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
preferences.set(response);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
||||||
import { assetMultiSelectManager, AssetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager, AssetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
|
|
@ -47,7 +48,6 @@
|
||||||
import { getGlobalActions } from '$lib/services/app.service';
|
import { getGlobalActions } from '$lib/services/app.service';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { preferences, user } from '$lib/stores/user.store';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { isAlbumsRoute, navigate, type AssetGridRouteSearchParams } from '$lib/utils/navigation';
|
import { isAlbumsRoute, navigate, type AssetGridRouteSearchParams } from '$lib/utils/navigation';
|
||||||
|
|
@ -243,7 +243,7 @@
|
||||||
|
|
||||||
onDestroy(() => activityManager.reset());
|
onDestroy(() => activityManager.reset());
|
||||||
|
|
||||||
let isOwned = $derived($user.id == album.ownerId);
|
let isOwned = $derived(authManager.user.id == album.ownerId);
|
||||||
|
|
||||||
let showActivityStatus = $derived(
|
let showActivityStatus = $derived(
|
||||||
album.albumUsers.length > 0 &&
|
album.albumUsers.length > 0 &&
|
||||||
|
|
@ -251,8 +251,8 @@
|
||||||
(album.isActivityEnabled || activityManager.commentCount > 0),
|
(album.isActivityEnabled || activityManager.commentCount > 0),
|
||||||
);
|
);
|
||||||
let isEditor = $derived(
|
let isEditor = $derived(
|
||||||
album.albumUsers.find(({ user: { id } }) => id === $user.id)?.role === AlbumUserRole.Editor ||
|
album.albumUsers.find(({ user: { id } }) => id === authManager.user.id)?.role === AlbumUserRole.Editor ||
|
||||||
album.ownerId === $user.id,
|
album.ownerId === authManager.user.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
let albumHasViewers = $derived(album.albumUsers.some(({ role }) => role === AlbumUserRole.Viewer));
|
let albumHasViewers = $derived(album.albumUsers.some(({ role }) => role === AlbumUserRole.Viewer));
|
||||||
|
|
@ -485,7 +485,7 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
{#if authManager.preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
@ -615,7 +615,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if album.albumUsers.length > 0 && album && assetViewerManager.isShowActivityPanel && $user && !assetViewerManager.isViewing}
|
{#if album.albumUsers.length > 0 && album && assetViewerManager.isShowActivityPanel && authManager.authenticated && !assetViewerManager.isViewing}
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div
|
<div
|
||||||
transition:fly={{ duration: 150 }}
|
transition:fly={{ duration: 150 }}
|
||||||
|
|
@ -623,12 +623,7 @@
|
||||||
class="z-2 w-90 md:w-115 overflow-y-auto transition-all dark:border-l dark:border-s-immich-dark-gray"
|
class="z-2 w-90 md:w-115 overflow-y-auto transition-all dark:border-l dark:border-s-immich-dark-gray"
|
||||||
translate="yes"
|
translate="yes"
|
||||||
>
|
>
|
||||||
<ActivityViewer
|
<ActivityViewer disabled={!album.isActivityEnabled} albumOwnerId={album.ownerId} albumId={album.id} />
|
||||||
user={$user}
|
|
||||||
disabled={!album.isActivityEnabled}
|
|
||||||
albumOwnerId={album.ownerId}
|
|
||||||
albumId={album.id}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { ActionButton, CommandPaletteDefaultProvider } from '@immich/ui';
|
import { ActionButton, CommandPaletteDefaultProvider } from '@immich/ui';
|
||||||
import { mdiDotsVertical } from '@mdi/js';
|
import { mdiDotsVertical } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
unarchive={assetMultiSelectManager.isAllArchived}
|
unarchive={assetMultiSelectManager.isAllArchived}
|
||||||
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||||
/>
|
/>
|
||||||
{#if $preferences.tags.enabled}
|
{#if authManager.preferences.tags.enabled}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import SkipLink from '$lib/elements/SkipLink.svelte';
|
import SkipLink from '$lib/elements/SkipLink.svelte';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import type { Viewport } from '$lib/managers/timeline-manager/types';
|
import type { Viewport } from '$lib/managers/timeline-manager/types';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { foldersStore } from '$lib/stores/folders.svelte';
|
import { foldersStore } from '$lib/stores/folders.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
import { joinPaths } from '$lib/utils/tree-utils';
|
import { joinPaths } from '$lib/utils/tree-utils';
|
||||||
import { ActionButton, CommandPaletteDefaultProvider, IconButton, Text } from '@immich/ui';
|
import { ActionButton, CommandPaletteDefaultProvider, IconButton, Text } from '@immich/ui';
|
||||||
|
|
@ -149,7 +149,7 @@
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} onArchive={triggerAssetUpdate} />
|
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} onArchive={triggerAssetUpdate} />
|
||||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||||
{#if $preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
{#if authManager.preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} onUndoDelete={triggerAssetUpdate} />
|
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} onUndoDelete={triggerAssetUpdate} />
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||||
import { PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
import { PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import PersonMergeSuggestionModal from '$lib/modals/PersonMergeSuggestionModal.svelte';
|
import PersonMergeSuggestionModal from '$lib/modals/PersonMergeSuggestionModal.svelte';
|
||||||
|
|
@ -34,7 +35,6 @@
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { getPersonActions } from '$lib/services/person.service';
|
import { getPersonActions } from '$lib/services/person.service';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { websocketEvents } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
|
@ -483,7 +483,7 @@
|
||||||
unarchive={assetMultiSelectManager.isAllArchived}
|
unarchive={assetMultiSelectManager.isAllArchived}
|
||||||
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||||
/>
|
/>
|
||||||
{#if $preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
{#if authManager.preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { memoryManager } from '$lib/managers/memory-manager.svelte';
|
import { memoryManager } from '$lib/managers/memory-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { getAssetMediaUrl, memoryLaneTitle } from '$lib/utils';
|
import { getAssetMediaUrl, memoryLaneTitle } from '$lib/utils';
|
||||||
import {
|
import {
|
||||||
updateStackedAssetInTimeline,
|
updateStackedAssetInTimeline,
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
withStacked
|
withStacked
|
||||||
>
|
>
|
||||||
{#if $preferences.memories.enabled}
|
{#if authManager.preferences.memories.enabled}
|
||||||
<ImageCarousel {items} />
|
<ImageCarousel {items} />
|
||||||
{/if}
|
{/if}
|
||||||
{#snippet empty()}
|
{#snippet empty()}
|
||||||
|
|
@ -150,7 +150,7 @@
|
||||||
menuItem
|
menuItem
|
||||||
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||||
/>
|
/>
|
||||||
{#if $preferences.tags.enabled}
|
{#if authManager.preferences.tags.enabled}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<DeleteAssets
|
<DeleteAssets
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { QueryParameter } from '$lib/constants';
|
import { QueryParameter } from '$lib/constants';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import type { Viewport } from '$lib/managers/timeline-manager/types';
|
import type { Viewport } from '$lib/managers/timeline-manager/types';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { lang, locale } from '$lib/stores/preferences.store';
|
import { lang, locale } from '$lib/stores/preferences.store';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { parseUtcDate } from '$lib/utils/date-time';
|
import { parseUtcDate } from '$lib/utils/date-time';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
|
@ -353,7 +353,7 @@
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} />
|
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} />
|
||||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||||
{#if $preferences.tags.enabled}
|
{#if authManager.preferences.tags.enabled}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<DeleteAssets menuItem {onAssetDelete} onUndoDelete={onSearchQueryUpdate} />
|
<DeleteAssets menuItem {onAssetDelete} onUndoDelete={onSearchQueryUpdate} />
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import SkipLink from '$lib/elements/SkipLink.svelte';
|
import SkipLink from '$lib/elements/SkipLink.svelte';
|
||||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||||
import { getTagActions } from '$lib/services/tag.service';
|
import { getTagActions } from '$lib/services/tag.service';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
|
||||||
import { joinPaths, TreeNode } from '$lib/utils/tree-utils';
|
import { joinPaths, TreeNode } from '$lib/utils/tree-utils';
|
||||||
import { getAllTags, type TagResponseDto } from '@immich/sdk';
|
import { getAllTags, type TagResponseDto } from '@immich/sdk';
|
||||||
import { ActionButton, CommandPaletteDefaultProvider, Text } from '@immich/ui';
|
import { ActionButton, CommandPaletteDefaultProvider, Text } from '@immich/ui';
|
||||||
|
|
@ -132,7 +132,7 @@
|
||||||
menuItem
|
menuItem
|
||||||
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||||
/>
|
/>
|
||||||
{#if $preferences.tags.enabled}
|
{#if authManager.preferences.tags.enabled}
|
||||||
<TagAction menuItem />
|
<TagAction menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
<DeleteAssets
|
<DeleteAssets
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
|
import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
|
||||||
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
|
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
|
||||||
import VersionAnnouncement from '$lib/components/VersionAnnouncement.svelte';
|
import VersionAnnouncement from '$lib/components/VersionAnnouncement.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { themeManager } from '$lib/managers/theme-manager.svelte';
|
import { themeManager } from '$lib/managers/theme-manager.svelte';
|
||||||
|
|
@ -15,7 +16,6 @@
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
import { sidebarStore } from '$lib/stores/sidebar.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { closeWebsocketConnection, openWebsocketConnection, websocketStore } from '$lib/stores/websocket';
|
import { closeWebsocketConnection, openWebsocketConnection, websocketStore } from '$lib/stores/websocket';
|
||||||
import { copyToClipboard } from '$lib/utils';
|
import { copyToClipboard } from '$lib/utils';
|
||||||
import { maintenanceShouldRedirect } from '$lib/utils/maintenance';
|
import { maintenanceShouldRedirect } from '$lib/utils/maintenance';
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
const { serverRestarting } = websocketStore;
|
const { serverRestarting } = websocketStore;
|
||||||
|
|
||||||
$effect.pre(() => {
|
$effect.pre(() => {
|
||||||
if ($user || $serverRestarting || page.url.pathname.startsWith(Route.maintenanceMode())) {
|
if (authManager.authenticated || $serverRestarting || page.url.pathname.startsWith(Route.maintenanceMode())) {
|
||||||
openWebsocketConnection();
|
openWebsocketConnection();
|
||||||
} else {
|
} else {
|
||||||
closeWebsocketConnection();
|
closeWebsocketConnection();
|
||||||
|
|
@ -182,7 +182,7 @@
|
||||||
icon: mdiServer,
|
icon: mdiServer,
|
||||||
onAction: () => goto(Route.systemStatistics()),
|
onAction: () => goto(Route.systemStatistics()),
|
||||||
},
|
},
|
||||||
].map((route) => ({ ...route, type: $t('page'), $if: () => $user?.isAdmin }));
|
].map((route) => ({ ...route, type: $t('page'), $if: () => authManager.user.isAdmin }));
|
||||||
|
|
||||||
const commands = $derived([...userCommands, ...adminCommands]);
|
const commands = $derived([...userCommands, ...adminCommands]);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
import { init } from '$lib/utils/server';
|
import { init } from '$lib/utils/server';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import { loadUser } from '../lib/utils/auth';
|
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const ssr = false;
|
export const ssr = false;
|
||||||
|
|
@ -17,8 +17,8 @@ export const load = (async ({ fetch }) => {
|
||||||
redirect(307, Route.maintenanceMode());
|
redirect(307, Route.maintenanceMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
const authenticated = await loadUser();
|
await authManager.load();
|
||||||
if (authenticated) {
|
if (authManager.authenticated) {
|
||||||
redirect(307, Route.photos());
|
redirect(307, Route.photos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { handleCreateLibrary } from '$lib/services/library.service';
|
import { handleCreateLibrary } from '$lib/services/library.service';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { Field, FormModal, HelperText, Select } from '@immich/ui';
|
import { Field, FormModal, HelperText, Select } from '@immich/ui';
|
||||||
import { mdiFolderSync } from '@mdi/js';
|
import { mdiFolderSync } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
const { data }: Props = $props();
|
const { data }: Props = $props();
|
||||||
|
|
||||||
let ownerId: string = $state($user.id);
|
let ownerId: string = $state(authManager.user.id);
|
||||||
const users = $state(data.allUsers);
|
const users = $state(data.allUsers);
|
||||||
|
|
||||||
const onClose = async () => {
|
const onClose = async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { handleUpdateUserAdmin } from '$lib/services/user-admin.service';
|
import { handleUpdateUserAdmin } from '$lib/services/user-admin.service';
|
||||||
import { user as authUser } from '$lib/stores/user.store';
|
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
import { ByteUnit, convertFromBytes, convertToBytes } from '$lib/utils/byte-units';
|
||||||
import { Field, FormModal, Input, Link, NumberInput, Switch, Text } from '@immich/ui';
|
import { Field, FormModal, Input, Link, NumberInput, Switch, Text } from '@immich/ui';
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
</Link>
|
</Link>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{#if user.id !== $authUser.id}
|
{#if user.id !== authManager.user.id}
|
||||||
<Field label={$t('admin.admin_user')}>
|
<Field label={$t('admin.admin_user')}>
|
||||||
<Switch bind:checked={isAdmin} class="mt-4" />
|
<Switch bind:checked={isAdmin} class="mt-4" />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { updateMyUser } from '@immich/sdk';
|
import { updateMyUser } from '@immich/sdk';
|
||||||
import { Alert, Button, Field, HelperText, PasswordInput, Stack, Text } from '@immich/ui';
|
import { Alert, Button, Field, HelperText, PasswordInput, Stack, Text } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -32,7 +31,7 @@
|
||||||
<form onsubmit={onSubmit} class="flex flex-col gap-4">
|
<form onsubmit={onSubmit} class="flex flex-col gap-4">
|
||||||
<Alert color="primary" size="small" class="mb-2">
|
<Alert color="primary" size="small" class="mb-2">
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
<Text>{$t('hi_user', { values: { name: $user.name, email: $user.email } })}</Text>
|
<Text>{$t('hi_user', { values: { name: authManager.user.name, email: authManager.user.email } })}</Text>
|
||||||
<Text>{$t('change_password_description')}</Text>
|
<Text>{$t('change_password_description')}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { authenticate } from '$lib/utils/auth';
|
import { authenticate } from '$lib/utils/auth';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const load = (async ({ url }) => {
|
export const load = (async ({ url }) => {
|
||||||
await authenticate(url);
|
await authenticate(url);
|
||||||
if (!get(user).shouldChangePassword) {
|
if (!authManager.user.shouldChangePassword) {
|
||||||
redirect(307, Route.photos());
|
redirect(307, Route.photos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@
|
||||||
import OnboardingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte';
|
import OnboardingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte';
|
||||||
import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte';
|
import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte';
|
||||||
import OnboardingUserPrivacy from '$lib/components/onboarding-page/onboarding-user-privacy.svelte';
|
import OnboardingUserPrivacy from '$lib/components/onboarding-page/onboarding-user-privacy.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { Route } from '$lib/route';
|
import { Route } from '$lib/route';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { OnboardingRole } from '$lib/types';
|
import { OnboardingRole } from '$lib/types';
|
||||||
import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk';
|
import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk';
|
||||||
import {
|
import {
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
return temporaryIndex === -1 ? 0 : temporaryIndex;
|
return temporaryIndex === -1 ? 0 : temporaryIndex;
|
||||||
});
|
});
|
||||||
let userRole = $derived(
|
let userRole = $derived(
|
||||||
$user.isAdmin && !serverConfigManager.value.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER,
|
authManager.user.isAdmin && !serverConfigManager.value.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER,
|
||||||
);
|
);
|
||||||
|
|
||||||
let onboardingStepCount = $derived(onboardingSteps.filter((step) => shouldRunStep(step.role, userRole)).length);
|
let onboardingStepCount = $derived(onboardingSteps.filter((step) => shouldRunStep(step.role, userRole)).length);
|
||||||
|
|
@ -128,7 +128,7 @@
|
||||||
|
|
||||||
const handleNextClicked = async () => {
|
const handleNextClicked = async () => {
|
||||||
if (nextStepIndex == -1) {
|
if (nextStepIndex == -1) {
|
||||||
if ($user.isAdmin) {
|
if (authManager.user.isAdmin) {
|
||||||
await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } });
|
await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } });
|
||||||
await serverConfigManager.loadServerConfig();
|
await serverConfigManager.loadServerConfig();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue