refactor: server config and feature flags managers (#23894)
parent
f11bfb9581
commit
1200bfad13
|
|
@ -1,78 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { retrieveServerConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
|
||||||
import { getConfig, getConfigDefaults, updateConfig, type SystemConfigDto } from '@immich/sdk';
|
|
||||||
import { toastManager } from '@immich/ui';
|
|
||||||
import { cloneDeep, isEqual } from 'lodash-es';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
|
||||||
import type { SettingsResetOptions } from './admin-settings';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
config: SystemConfigDto;
|
|
||||||
children: import('svelte').Snippet<[{ savedConfig: SystemConfigDto; defaultConfig: SystemConfigDto }]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { config = $bindable(), children }: Props = $props();
|
|
||||||
|
|
||||||
let savedConfig: SystemConfigDto | undefined = $state();
|
|
||||||
let defaultConfig: SystemConfigDto | undefined = $state();
|
|
||||||
|
|
||||||
export const handleReset = async (options: SettingsResetOptions) => {
|
|
||||||
await (options.default ? resetToDefault(options.configKeys) : reset(options.configKeys));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleSave = async (update: Partial<SystemConfigDto>) => {
|
|
||||||
let systemConfigDto = {
|
|
||||||
...savedConfig,
|
|
||||||
...update,
|
|
||||||
} as SystemConfigDto;
|
|
||||||
|
|
||||||
if (isEqual(systemConfigDto, savedConfig)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const newConfig = await updateConfig({
|
|
||||||
systemConfigDto,
|
|
||||||
});
|
|
||||||
|
|
||||||
config = cloneDeep(newConfig);
|
|
||||||
savedConfig = cloneDeep(newConfig);
|
|
||||||
toastManager.success($t('settings_saved'));
|
|
||||||
|
|
||||||
await retrieveServerConfig();
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_save_settings'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const reset = async (configKeys: Array<keyof SystemConfigDto>) => {
|
|
||||||
const resetConfig = await getConfig();
|
|
||||||
|
|
||||||
for (const key of configKeys) {
|
|
||||||
config = { ...config, [key]: resetConfig[key] };
|
|
||||||
}
|
|
||||||
|
|
||||||
toastManager.info($t('admin.reset_settings_to_recent_saved'));
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetToDefault = (configKeys: Array<keyof SystemConfigDto>) => {
|
|
||||||
if (!defaultConfig) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of configKeys) {
|
|
||||||
config = { ...config, [key]: defaultConfig[key] };
|
|
||||||
}
|
|
||||||
|
|
||||||
toastManager.info($t('admin.reset_settings_to_default'));
|
|
||||||
};
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
[savedConfig, defaultConfig] = await Promise.all([getConfig(), getConfigDefaults()]);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if savedConfig && defaultConfig}
|
|
||||||
{@render children({ savedConfig, defaultConfig })}
|
|
||||||
{/if}
|
|
||||||
|
|
@ -6,8 +6,9 @@
|
||||||
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 FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import AuthDisableLoginConfirmModal from '$lib/modals/AuthDisableLoginConfirmModal.svelte';
|
import AuthDisableLoginConfirmModal from '$lib/modals/AuthDisableLoginConfirmModal.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { OAuthTokenEndpointAuthMethod, unlinkAllOAuthAccountsAdmin } from '@immich/sdk';
|
import { OAuthTokenEndpointAuthMethod, unlinkAllOAuthAccountsAdmin } from '@immich/sdk';
|
||||||
import { Button, modalManager, Text, toastManager } from '@immich/ui';
|
import { Button, modalManager, Text, toastManager } from '@immich/ui';
|
||||||
|
|
@ -15,7 +16,7 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@
|
||||||
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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
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 FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import {
|
import {
|
||||||
AudioCodec,
|
AudioCodec,
|
||||||
CQMode,
|
CQMode,
|
||||||
|
|
@ -23,7 +24,7 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@
|
||||||
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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
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 { SettingInputFieldType } from '$lib/constants';
|
import { SettingInputFieldType } from '$lib/constants';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { getJobName } from '$lib/utils';
|
import { getJobName } from '$lib/utils';
|
||||||
import { JobName, type SystemConfigJobDto } from '@immich/sdk';
|
import { JobName, type SystemConfigJobDto } from '@immich/sdk';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,12 @@
|
||||||
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 FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { LogLevel } from '@immich/sdk';
|
import { LogLevel } from '@immich/sdk';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,15 @@
|
||||||
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 FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { Button, IconButton } from '@immich/ui';
|
import { Button, IconButton } from '@immich/ui';
|
||||||
import { mdiPlus, mdiTrashCanOutline } from '@mdi/js';
|
import { mdiPlus, mdiTrashCanOutline } from '@mdi/js';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -167,7 +168,7 @@
|
||||||
min={0.001}
|
min={0.001}
|
||||||
max={0.1}
|
max={0.1}
|
||||||
description={$t('admin.machine_learning_max_detection_distance_description')}
|
description={$t('admin.machine_learning_max_detection_distance_description')}
|
||||||
disabled={disabled || !$featureFlags.duplicateDetection}
|
disabled={disabled || !featureFlagsManager.value.duplicateDetection}
|
||||||
isEdited={configToEdit.machineLearning.duplicateDetection.maxDistance !==
|
isEdited={configToEdit.machineLearning.duplicateDetection.maxDistance !==
|
||||||
config.machineLearning.duplicateDetection.maxDistance}
|
config.machineLearning.duplicateDetection.maxDistance}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@
|
||||||
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 FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@
|
||||||
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 { featureFlagsManager } from '$lib/managers/feature-flags-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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
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';
|
||||||
|
|
@ -14,7 +15,7 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
import { AppRoute, SettingInputFieldType } from '$lib/constants';
|
import { AppRoute, SettingInputFieldType } from '$lib/constants';
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { getStorageTemplateOptions, type SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
|
import { getStorageTemplateOptions, type SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
|
||||||
import { LoadingSpinner } from '@immich/ui';
|
import { LoadingSpinner } from '@immich/ui';
|
||||||
|
|
@ -27,7 +28,7 @@
|
||||||
|
|
||||||
const { minified = false, duration = 500, saveOnClose = false }: Props = $props();
|
const { minified = false, duration = 500, saveOnClose = false }: Props = $props();
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||||
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
|
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import EmailTemplatePreviewModal from '$lib/modals/EmailTemplatePreviewModal.svelte';
|
import EmailTemplatePreviewModal from '$lib/modals/EmailTemplatePreviewModal.svelte';
|
||||||
import { systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplateAdmin } from '@immich/sdk';
|
import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplateAdmin } from '@immich/sdk';
|
||||||
import { Button, Icon, LoadingSpinner, modalManager } from '@immich/ui';
|
import { Button, Icon, LoadingSpinner, modalManager } from '@immich/ui';
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
|
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
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 { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@
|
||||||
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
import SettingButtonsRow from '$lib/components/shared-components/settings/SystemConfigButtonRow.svelte';
|
||||||
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 { SettingInputFieldType } from '$lib/constants';
|
import { SettingInputFieldType } from '$lib/constants';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
const disabled = $featureFlags.configFile;
|
const disabled = $derived(featureFlagsManager.value.configFile);
|
||||||
const config = $derived(systemConfigManager.value);
|
const config = $derived(systemConfigManager.value);
|
||||||
let configToEdit = $state(systemConfigManager.cloneValue());
|
let configToEdit = $state(systemConfigManager.cloneValue());
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import type { ResetOptions } from '$lib/utils/dipatch';
|
|
||||||
import type { SystemConfigDto } from '@immich/sdk';
|
|
||||||
|
|
||||||
export type SettingsResetOptions = ResetOptions & { configKeys: Array<keyof SystemConfigDto> };
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte';
|
import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte';
|
||||||
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 { 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';
|
||||||
import { handleDownloadAlbum } from '$lib/services/album.service';
|
import { handleDownloadAlbum } from '$lib/services/album.service';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
|
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||||
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
|
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
icon={mdiDownload}
|
icon={mdiDownload}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if sharedLink.showMetadata && $featureFlags.loaded && $featureFlags.map}
|
{#if sharedLink.showMetadata && featureFlagsManager.value.map}
|
||||||
<AlbumMap {album} />
|
<AlbumMap {album} />
|
||||||
{/if}
|
{/if}
|
||||||
<ThemeButton />
|
<ThemeButton />
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
import DeleteAssetDialog from '$lib/components/photos-page/delete-asset-dialog.svelte';
|
import DeleteAssetDialog from '$lib/components/photos-page/delete-asset-dialog.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import Portal from '$lib/elements/Portal.svelte';
|
import Portal from '$lib/elements/Portal.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-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 { deleteAssets, type AssetResponseDto } from '@immich/sdk';
|
import { deleteAssets, type AssetResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
let showConfirmModal = $state(false);
|
let showConfirmModal = $state(false);
|
||||||
|
|
||||||
const trashOrDelete = async (force = false) => {
|
const trashOrDelete = async (force = false) => {
|
||||||
if (force || !$featureFlags.trash) {
|
if (force || !featureFlagsManager.value.trash) {
|
||||||
if ($showDeleteModal) {
|
if ($showDeleteModal) {
|
||||||
showConfirmModal = true;
|
showConfirmModal = true;
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ describe('AssetViewerNavBar component', () => {
|
||||||
'ResizeObserver',
|
'ResizeObserver',
|
||||||
vi.fn(() => ({ observe: vi.fn(), unobserve: vi.fn(), disconnect: vi.fn() })),
|
vi.fn(() => ({ observe: vi.fn(), unobserve: vi.fn(), disconnect: vi.fn() })),
|
||||||
);
|
);
|
||||||
|
vi.mock(import('$lib/managers/feature-flags-manager.svelte'), () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return { featureFlagsManager: { init: vi.fn(), loadFeatureFlags: vi.fn(), value: { smartSearch: true } } as any };
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@
|
||||||
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 { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
|
import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
||||||
import { getAssetJobName, getSharedLink } from '$lib/utils';
|
import { getAssetJobName, getSharedLink } from '$lib/utils';
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
let isOwner = $derived($user && asset.ownerId === $user?.id);
|
let isOwner = $derived($user && asset.ownerId === $user?.id);
|
||||||
let showDownloadButton = $derived(sharedLink ? sharedLink.allowDownload : !asset.isOffline);
|
let showDownloadButton = $derived(sharedLink ? sharedLink.allowDownload : !asset.isOffline);
|
||||||
let isLocked = $derived(asset.visibility === AssetVisibility.Locked);
|
let isLocked = $derived(asset.visibility === AssetVisibility.Locked);
|
||||||
let smartSearchEnabled = $derived($featureFlags.loaded && $featureFlags.smartSearch);
|
let smartSearchEnabled = $derived(featureFlagsManager.value.smartSearch);
|
||||||
|
|
||||||
// $: showEditorButton =
|
// $: showEditorButton =
|
||||||
// isOwner &&
|
// isOwner &&
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte';
|
import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte';
|
||||||
import { AppRoute, QueryParameter, timeToLoadTheMap } from '$lib/constants';
|
import { AppRoute, QueryParameter, timeToLoadTheMap } from '$lib/constants';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import AssetChangeDateModal from '$lib/modals/AssetChangeDateModal.svelte';
|
import AssetChangeDateModal from '$lib/modals/AssetChangeDateModal.svelte';
|
||||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||||
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 { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { preferences, user } from '$lib/stores/user.store';
|
import { preferences, user } from '$lib/stores/user.store';
|
||||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -438,7 +438,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#if latlng && $featureFlags.loaded && $featureFlags.map}
|
{#if latlng && featureFlagsManager.value.map}
|
||||||
<div class="h-90">
|
<div class="h-90">
|
||||||
{#await import('$lib/components/shared-components/map/map.svelte')}
|
{#await import('$lib/components/shared-components/map/map.svelte')}
|
||||||
{#await delay(timeToLoadTheMap) then}
|
{#await delay(timeToLoadTheMap) then}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { getJobName } from '$lib/utils';
|
import { getJobName } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { JobCommand, JobName, sendJobCommand, type AllJobStatusResponseDto, type JobCommandDto } from '@immich/sdk';
|
import { JobCommand, JobName, sendJobCommand, type AllJobStatusResponseDto, type JobCommandDto } from '@immich/sdk';
|
||||||
|
|
@ -27,8 +27,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let { jobs = $bindable() }: Props = $props();
|
let { jobs = $bindable() }: Props = $props();
|
||||||
|
const featureFlags = featureFlagsManager.value;
|
||||||
|
|
||||||
interface JobDetails {
|
type JobDetails = {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
description?: Component;
|
description?: Component;
|
||||||
|
|
@ -38,7 +39,7 @@
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
icon: string;
|
icon: string;
|
||||||
handleCommand?: (jobId: JobName, jobCommand: JobCommandDto) => Promise<void>;
|
handleCommand?: (jobId: JobName, jobCommand: JobCommandDto) => Promise<void>;
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleConfirmCommand = async (jobId: JobName, dto: JobCommandDto) => {
|
const handleConfirmCommand = async (jobId: JobName, dto: JobCommandDto) => {
|
||||||
if (dto.force) {
|
if (dto.force) {
|
||||||
|
|
@ -84,7 +85,7 @@
|
||||||
subtitle: $t('admin.sidecar_job_description'),
|
subtitle: $t('admin.sidecar_job_description'),
|
||||||
allText: $t('sync'),
|
allText: $t('sync'),
|
||||||
missingText: $t('discover'),
|
missingText: $t('discover'),
|
||||||
disabled: !$featureFlags.sidecar,
|
disabled: !featureFlags.sidecar,
|
||||||
},
|
},
|
||||||
[JobName.SmartSearch]: {
|
[JobName.SmartSearch]: {
|
||||||
icon: mdiImageSearch,
|
icon: mdiImageSearch,
|
||||||
|
|
@ -92,7 +93,7 @@
|
||||||
subtitle: $t('admin.smart_search_job_description'),
|
subtitle: $t('admin.smart_search_job_description'),
|
||||||
allText: $t('all'),
|
allText: $t('all'),
|
||||||
missingText: $t('missing'),
|
missingText: $t('missing'),
|
||||||
disabled: !$featureFlags.smartSearch,
|
disabled: !featureFlags.smartSearch,
|
||||||
},
|
},
|
||||||
[JobName.DuplicateDetection]: {
|
[JobName.DuplicateDetection]: {
|
||||||
icon: mdiContentDuplicate,
|
icon: mdiContentDuplicate,
|
||||||
|
|
@ -100,7 +101,7 @@
|
||||||
subtitle: $t('admin.duplicate_detection_job_description'),
|
subtitle: $t('admin.duplicate_detection_job_description'),
|
||||||
allText: $t('all'),
|
allText: $t('all'),
|
||||||
missingText: $t('missing'),
|
missingText: $t('missing'),
|
||||||
disabled: !$featureFlags.duplicateDetection,
|
disabled: !featureFlags.duplicateDetection,
|
||||||
},
|
},
|
||||||
[JobName.FaceDetection]: {
|
[JobName.FaceDetection]: {
|
||||||
icon: mdiFaceRecognition,
|
icon: mdiFaceRecognition,
|
||||||
|
|
@ -110,7 +111,7 @@
|
||||||
refreshText: $t('refresh'),
|
refreshText: $t('refresh'),
|
||||||
missingText: $t('missing'),
|
missingText: $t('missing'),
|
||||||
handleCommand: handleConfirmCommand,
|
handleCommand: handleConfirmCommand,
|
||||||
disabled: !$featureFlags.facialRecognition,
|
disabled: !featureFlags.facialRecognition,
|
||||||
},
|
},
|
||||||
[JobName.FacialRecognition]: {
|
[JobName.FacialRecognition]: {
|
||||||
icon: mdiTagFaces,
|
icon: mdiTagFaces,
|
||||||
|
|
@ -119,7 +120,7 @@
|
||||||
allText: $t('reset'),
|
allText: $t('reset'),
|
||||||
missingText: $t('missing'),
|
missingText: $t('missing'),
|
||||||
handleCommand: handleConfirmCommand,
|
handleCommand: handleConfirmCommand,
|
||||||
disabled: !$featureFlags.facialRecognition,
|
disabled: !featureFlags.facialRecognition,
|
||||||
},
|
},
|
||||||
[JobName.Ocr]: {
|
[JobName.Ocr]: {
|
||||||
icon: mdiOcr,
|
icon: mdiOcr,
|
||||||
|
|
@ -127,7 +128,7 @@
|
||||||
subtitle: $t('admin.ocr_job_description'),
|
subtitle: $t('admin.ocr_job_description'),
|
||||||
allText: $t('all'),
|
allText: $t('all'),
|
||||||
missingText: $t('missing'),
|
missingText: $t('missing'),
|
||||||
disabled: !$featureFlags.ocr,
|
disabled: !featureFlags.ocr,
|
||||||
},
|
},
|
||||||
[JobName.VideoConversion]: {
|
[JobName.VideoConversion]: {
|
||||||
icon: mdiVideo,
|
icon: mdiVideo,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { OnboardingRole } from '$lib/models/onboarding-role';
|
import { OnboardingRole } from '$lib/models/onboarding-role';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { Logo } from '@immich/ui';
|
import { Logo } from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
let userRole = $derived($user.isAdmin && !$serverConfig.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER);
|
let userRole = $derived(
|
||||||
|
$user.isAdmin && !serverConfigManager.value.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER,
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="gap-4">
|
<div class="gap-4">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<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 { 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 { systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
||||||
import { AppRoute, AssetAction } from '$lib/constants';
|
import { AppRoute, AssetAction } from '$lib/constants';
|
||||||
import Portal from '$lib/elements/Portal.svelte';
|
import Portal from '$lib/elements/Portal.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
||||||
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { deleteAssets } from '$lib/utils/actions';
|
import { deleteAssets } from '$lib/utils/actions';
|
||||||
import { archiveAssets, cancelMultiselect } from '$lib/utils/asset-utils';
|
import { archiveAssets, cancelMultiselect } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -405,7 +405,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
let isTrashEnabled = $derived(featureFlagsManager.value.trash);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!lastAssetMouseEvent) {
|
if (!lastAssetMouseEvent) {
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { afterNavigate } from '$app/navigation';
|
import { afterNavigate } from '$app/navigation';
|
||||||
import { Theme } from '$lib/constants';
|
import { Theme } from '$lib/constants';
|
||||||
|
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';
|
||||||
import MapSettingsModal from '$lib/modals/MapSettingsModal.svelte';
|
import MapSettingsModal from '$lib/modals/MapSettingsModal.svelte';
|
||||||
import { mapSettings } from '$lib/stores/preferences.store';
|
import { mapSettings } from '$lib/stores/preferences.store';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||||
import { getMapMarkers, type MapMarkerResponseDto } from '@immich/sdk';
|
import { getMapMarkers, type MapMarkerResponseDto } from '@immich/sdk';
|
||||||
import { Icon, modalManager } from '@immich/ui';
|
import { Icon, modalManager } from '@immich/ui';
|
||||||
|
|
@ -103,7 +103,9 @@
|
||||||
let abortController: AbortController;
|
let abortController: AbortController;
|
||||||
|
|
||||||
const theme = $derived($mapSettings.allowDarkMode ? themeManager.value : Theme.LIGHT);
|
const theme = $derived($mapSettings.allowDarkMode ? themeManager.value : Theme.LIGHT);
|
||||||
const styleUrl = $derived(theme === Theme.DARK ? $serverConfig.mapDarkStyleUrl : $serverConfig.mapLightStyleUrl);
|
const styleUrl = $derived(
|
||||||
|
theme === Theme.DARK ? serverConfigManager.value.mapDarkStyleUrl : serverConfigManager.value.mapLightStyleUrl,
|
||||||
|
);
|
||||||
|
|
||||||
export function addClipMapMarker(lng: number, lat: number) {
|
export function addClipMapMarker(lng: number, lat: number) {
|
||||||
if (map) {
|
if (map) {
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import SkipLink from '$lib/elements/SkipLink.svelte';
|
import SkipLink from '$lib/elements/SkipLink.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
import { mobileDevice } from '$lib/stores/mobile-device.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 { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { Button, IconButton, Logo } from '@immich/ui';
|
import { Button, IconButton, Logo } from '@immich/ui';
|
||||||
import { mdiBellBadge, mdiBellOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
|
import { mdiBellBadge, mdiBellOutline, mdiMagnify, mdiMenu, mdiTrayArrowUp } from '@mdi/js';
|
||||||
|
|
@ -82,13 +82,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between gap-4 lg:gap-8 pe-6">
|
<div class="flex justify-between gap-4 lg:gap-8 pe-6">
|
||||||
<div class="hidden w-full max-w-5xl flex-1 tall:ps-0 sm:block">
|
<div class="hidden w-full max-w-5xl flex-1 tall:ps-0 sm:block">
|
||||||
{#if $featureFlags.search}
|
{#if featureFlagsManager.value.search}
|
||||||
<SearchBar grayTheme={true} />
|
<SearchBar grayTheme={true} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="flex place-items-center justify-end gap-1 md:gap-2 w-full sm:w-auto">
|
<section class="flex place-items-center justify-end gap-1 md:gap-2 w-full sm:w-auto">
|
||||||
{#if $featureFlags.search}
|
{#if featureFlagsManager.value.search}
|
||||||
<IconButton
|
<IconButton
|
||||||
color="secondary"
|
color="secondary"
|
||||||
shape="round"
|
shape="round"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import RadioButton from '$lib/elements/RadioButton.svelte';
|
import RadioButton from '$lib/elements/RadioButton.svelte';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend class="immich-form-label">{$t('search_type')}</legend>
|
<legend class="immich-form-label">{$t('search_type')}</legend>
|
||||||
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1 mb-2">
|
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1 mb-2">
|
||||||
{#if $featureFlags.loaded && $featureFlags.smartSearch}
|
{#if featureFlagsManager.value.smartSearch}
|
||||||
<RadioButton name="query-type" id="context-radio" label={$t('context')} bind:group={queryType} value="smart" />
|
<RadioButton name="query-type" id="context-radio" label={$t('context')} bind:group={queryType} value="smart" />
|
||||||
{/if}
|
{/if}
|
||||||
<RadioButton
|
<RadioButton
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
bind:group={queryType}
|
bind:group={queryType}
|
||||||
value="description"
|
value="description"
|
||||||
/>
|
/>
|
||||||
{#if $featureFlags.loaded && $featureFlags.ocr}
|
{#if featureFlagsManager.value.ocr}
|
||||||
<RadioButton name="query-type" id="ocr-radio" label={$t('ocr')} bind:group={queryType} value="ocr" />
|
<RadioButton name="query-type" id="ocr-radio" label={$t('ocr')} bind:group={queryType} value="ocr" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
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 { systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import type { SystemConfigDto } from '@immich/sdk';
|
import type { SystemConfigDto } from '@immich/sdk';
|
||||||
import { Button, toastManager } from '@immich/ui';
|
import { Button, toastManager } from '@immich/ui';
|
||||||
import { isEqual, pick } from 'lodash-es';
|
import { isEqual, pick } from 'lodash-es';
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
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 { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { recentAlbumsDropdown } from '$lib/stores/preferences.store';
|
import { recentAlbumsDropdown } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { preferences } from '$lib/stores/user.store';
|
||||||
import {
|
import {
|
||||||
mdiAccount,
|
mdiAccount,
|
||||||
|
|
@ -54,11 +54,11 @@
|
||||||
icon={isPhotosSelected ? mdiImageMultiple : mdiImageMultipleOutline}
|
icon={isPhotosSelected ? mdiImageMultiple : mdiImageMultipleOutline}
|
||||||
></SideBarLink>
|
></SideBarLink>
|
||||||
|
|
||||||
{#if $featureFlags.search}
|
{#if featureFlagsManager.value.search}
|
||||||
<SideBarLink title={$t('explore')} href={resolve('/(user)/explore')} icon={mdiMagnify} />
|
<SideBarLink title={$t('explore')} href={resolve('/(user)/explore')} icon={mdiMagnify} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $featureFlags.map}
|
{#if featureFlagsManager.value.map}
|
||||||
<SideBarLink
|
<SideBarLink
|
||||||
title={$t('map')}
|
title={$t('map')}
|
||||||
href={resolve('/(user)/map')}
|
href={resolve('/(user)/map')}
|
||||||
|
|
@ -139,7 +139,7 @@
|
||||||
icon={isLockedFolderSelected ? mdiLock : mdiLockOutline}
|
icon={isLockedFolderSelected ? mdiLock : mdiLockOutline}
|
||||||
></SideBarLink>
|
></SideBarLink>
|
||||||
|
|
||||||
{#if $featureFlags.trash}
|
{#if featureFlagsManager.value.trash}
|
||||||
<SideBarLink
|
<SideBarLink
|
||||||
title={$t('trash')}
|
title={$t('trash')}
|
||||||
href={resolve('/(user)/trash')}
|
href={resolve('/(user)/trash')}
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,6 @@
|
||||||
// afterNavigate is only called after navigation to a new URL, {complete} will resolve
|
// afterNavigate is only called after navigation to a new URL, {complete} will resolve
|
||||||
// after successful navigation.
|
// after successful navigation.
|
||||||
afterNavigate(({ complete }) => {
|
afterNavigate(({ complete }) => {
|
||||||
console.log('after navigate');
|
|
||||||
void complete.finally(() => {
|
void complete.finally(() => {
|
||||||
const isAssetViewerPage = isAssetViewerRoute(page);
|
const isAssetViewerPage = isAssetViewerRoute(page);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import DeleteAssetDialog from '$lib/components/photos-page/delete-asset-dialog.svelte';
|
import DeleteAssetDialog from '$lib/components/photos-page/delete-asset-dialog.svelte';
|
||||||
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { type OnDelete, type OnUndoDelete, deleteAssets } from '$lib/utils/actions';
|
import { type OnDelete, type OnUndoDelete, deleteAssets } from '$lib/utils/actions';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiDeleteForeverOutline, mdiDeleteOutline, mdiTimerSand } from '@mdi/js';
|
import { mdiDeleteForeverOutline, mdiDeleteOutline, mdiTimerSand } from '@mdi/js';
|
||||||
|
|
@ -15,7 +15,12 @@
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { onAssetDelete, onUndoDelete = undefined, menuItem = false, force = !$featureFlags.trash }: Props = $props();
|
let {
|
||||||
|
onAssetDelete,
|
||||||
|
onUndoDelete = undefined,
|
||||||
|
menuItem = false,
|
||||||
|
force = !featureFlagsManager.value.trash,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
setFocusTo as setFocusToInit,
|
setFocusTo as setFocusToInit,
|
||||||
} from '$lib/components/timeline/actions/focus-actions';
|
} from '$lib/components/timeline/actions/focus-actions';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
|
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';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
|
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
|
||||||
|
|
@ -15,7 +16,6 @@
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
import { searchStore } from '$lib/stores/search.svelte';
|
import { searchStore } from '$lib/stores/search.svelte';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { deleteAssets, updateStackedAssetInTimeline } from '$lib/utils/actions';
|
import { deleteAssets, updateStackedAssetInTimeline } from '$lib/utils/actions';
|
||||||
import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils';
|
import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -121,7 +121,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
const isTrashEnabled = $derived(featureFlagsManager.value.trash);
|
||||||
const isEmpty = $derived(timelineManager.isInitialized && timelineManager.months.length === 0);
|
const isEmpty = $derived(timelineManager.isInitialized && timelineManager.months.length === 0);
|
||||||
const idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
const idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
||||||
let isShortcutModalOpen = false;
|
let isShortcutModalOpen = false;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { featureFlags } from '$lib/stores/system-config-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 { type UserAdminResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
<div class="flex place-content-center place-items-center">
|
<div class="flex place-content-center place-items-center">
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
</div>
|
</div>
|
||||||
{:else if $featureFlags.oauth}
|
{:else if featureFlagsManager.value.oauth}
|
||||||
{#if user.oauthId}
|
{#if 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}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
import UserPurchaseSettings from '$lib/components/user-settings-page/user-purchase-settings.svelte';
|
import UserPurchaseSettings from '$lib/components/user-settings-page/user-purchase-settings.svelte';
|
||||||
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 { OpenSettingQueryParameterValue, QueryParameter } from '$lib/constants';
|
import { OpenSettingQueryParameterValue, QueryParameter } from '$lib/constants';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
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';
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
<NotificationsSettings />
|
<NotificationsSettings />
|
||||||
</SettingAccordion>
|
</SettingAccordion>
|
||||||
|
|
||||||
{#if $featureFlags.loaded && $featureFlags.oauth}
|
{#if featureFlagsManager.value.oauth}
|
||||||
<SettingAccordion
|
<SettingAccordion
|
||||||
icon={mdiTwoFactorAuthentication}
|
icon={mdiTwoFactorAuthentication}
|
||||||
key="oauth"
|
key="oauth"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
|
import { getServerFeatures, type ServerFeaturesDto } from '@immich/sdk';
|
||||||
|
|
||||||
|
class FeatureFlagsManager {
|
||||||
|
#value?: ServerFeaturesDto = $state();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
eventManager.on('SystemConfigUpdate', () => void this.#loadFeatureFlags());
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.#loadFeatureFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
if (!this.#value) {
|
||||||
|
throw new Error('Feature flags manager must be initialized first');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #loadFeatureFlags() {
|
||||||
|
this.#value = await getServerFeatures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const featureFlagsManager = new FeatureFlagsManager();
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
|
import { getServerConfig, type ServerConfigDto } from '@immich/sdk';
|
||||||
|
|
||||||
|
class ServerConfigManager {
|
||||||
|
#value?: ServerConfigDto = $state();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
eventManager.on('SystemConfigUpdate', () => void this.loadServerConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.loadServerConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
if (!this.#value) {
|
||||||
|
throw new Error('Server config manager must be initialized first');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadServerConfig() {
|
||||||
|
this.#value = await getServerConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const serverConfigManager = new ServerConfigManager();
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
|
import { getConfig, getConfigDefaults, type SystemConfigDto } from '@immich/sdk';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
class SystemConfigManager {
|
||||||
|
#value?: SystemConfigDto = $state();
|
||||||
|
#defaultValue?: SystemConfigDto = $state();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
eventManager.on('SystemConfigUpdate', (config) => (this.#value = config));
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.#loadConfig();
|
||||||
|
await this.#loadDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
if (!this.#value) {
|
||||||
|
throw new Error('Server config manager must be initialized first');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(config: SystemConfigDto) {
|
||||||
|
this.#value = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultValue() {
|
||||||
|
if (!this.#defaultValue) {
|
||||||
|
throw new Error('Server config manager must be initialized first');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneValue() {
|
||||||
|
return cloneDeep(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneDefaultValue() {
|
||||||
|
return cloneDeep(this.defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #loadConfig() {
|
||||||
|
this.#value = await getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
async #loadDefault() {
|
||||||
|
this.#defaultValue = await getConfigDefaults();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const systemConfigManager = new SystemConfigManager();
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { resetPinCode } from '@immich/sdk';
|
import { resetPinCode } from '@immich/sdk';
|
||||||
import {
|
import {
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
let { onClose }: Props = $props();
|
let { onClose }: Props = $props();
|
||||||
|
|
||||||
let passwordLoginEnabled = $derived($featureFlags.passwordLogin);
|
let passwordLoginEnabled = $derived(featureFlagsManager.value.passwordLogin);
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
|
|
||||||
const handleReset = async () => {
|
const handleReset = async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
|
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
import { ByteUnit, convertToBytes } from '$lib/utils/byte-units';
|
import { ByteUnit, convertToBytes } from '$lib/utils/byte-units';
|
||||||
import {
|
import {
|
||||||
|
|
@ -85,17 +85,17 @@
|
||||||
<Input bind:value={email} type="email" />
|
<Input bind:value={email} type="email" />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
{#if $featureFlags.email}
|
{#if featureFlagsManager.value.email}
|
||||||
<Field label={$t('admin.send_welcome_email')}>
|
<Field label={$t('admin.send_welcome_email')}>
|
||||||
<Switch id="send-welcome-email" bind:checked={notify} class="text-sm" />
|
<Switch id="send-welcome-email" bind:checked={notify} class="text-sm" />
|
||||||
</Field>
|
</Field>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Field label={$t('password')} required={!$featureFlags.oauth}>
|
<Field label={$t('password')} required={!featureFlagsManager.value.oauth}>
|
||||||
<PasswordInput id="password" bind:value={password} autocomplete="new-password" />
|
<PasswordInput id="password" bind:value={password} autocomplete="new-password" />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field label={$t('confirm_password')} required={!$featureFlags.oauth}>
|
<Field label={$t('confirm_password')} required={!featureFlagsManager.value.oauth}>
|
||||||
<PasswordInput id="confirmPassword" bind:value={passwordConfirm} autocomplete="new-password" />
|
<PasswordInput id="confirmPassword" bind:value={passwordConfirm} autocomplete="new-password" />
|
||||||
<HelperText color="danger">{passwordMismatchMessage}</HelperText>
|
<HelperText color="danger">{passwordMismatchMessage}</HelperText>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
import FormatMessage from '$lib/elements/FormatMessage.svelte';
|
||||||
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { handleDeleteUserAdmin } from '$lib/services/user-admin.service';
|
import { handleDeleteUserAdmin } from '$lib/services/user-admin.service';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { type UserAdminResponseDto } from '@immich/sdk';
|
import { type UserAdminResponseDto } from '@immich/sdk';
|
||||||
import { Alert, Checkbox, ConfirmModal, Field, Input, Label, Text } from '@immich/ui';
|
import { Alert, Checkbox, ConfirmModal, Field, Input, Label, Text } from '@immich/ui';
|
||||||
import { mdiTrashCanOutline } from '@mdi/js';
|
import { mdiTrashCanOutline } from '@mdi/js';
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
{:else}
|
{:else}
|
||||||
<FormatMessage
|
<FormatMessage
|
||||||
key="admin.user_delete_delay"
|
key="admin.user_delete_delay"
|
||||||
values={{ user: user.name, delay: $serverConfig.userDeleteDelay }}
|
values={{ user: user.name, delay: serverConfigManager.value.userDeleteDelay }}
|
||||||
>
|
>
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<b>{message}</b>
|
<b>{message}</b>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { goto } from '$app/navigation';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { authManager } from '$lib/managers/auth-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 { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import QrCodeModal from '$lib/modals/QrCodeModal.svelte';
|
import QrCodeModal from '$lib/modals/QrCodeModal.svelte';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { copyToClipboard } from '$lib/utils';
|
import { copyToClipboard } from '$lib/utils';
|
||||||
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';
|
||||||
|
|
@ -19,7 +19,6 @@ import {
|
||||||
import { MenuItemType, menuManager, modalManager, toastManager, type MenuItem } from '@immich/ui';
|
import { MenuItemType, menuManager, modalManager, toastManager, type MenuItem } from '@immich/ui';
|
||||||
import { mdiCircleEditOutline, mdiContentCopy, mdiDelete, mdiDotsVertical, mdiQrcode } from '@mdi/js';
|
import { mdiCircleEditOutline, mdiContentCopy, mdiDelete, mdiDotsVertical, mdiQrcode } from '@mdi/js';
|
||||||
import type { MessageFormatter } from 'svelte-i18n';
|
import type { MessageFormatter } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export const getSharedLinkActions = ($t: MessageFormatter, sharedLink: SharedLinkResponseDto) => {
|
export const getSharedLinkActions = ($t: MessageFormatter, sharedLink: SharedLinkResponseDto) => {
|
||||||
const Edit: MenuItem = {
|
const Edit: MenuItem = {
|
||||||
|
|
@ -63,7 +62,7 @@ export const getSharedLinkActions = ($t: MessageFormatter, sharedLink: SharedLin
|
||||||
|
|
||||||
const asUrl = (sharedLink: SharedLinkResponseDto) => {
|
const asUrl = (sharedLink: SharedLinkResponseDto) => {
|
||||||
const path = sharedLink.slug ? `s/${sharedLink.slug}` : `share/${sharedLink.key}`;
|
const path = sharedLink.slug ? `s/${sharedLink.slug}` : `share/${sharedLink.key}`;
|
||||||
return new URL(path, get(serverConfig).externalDomain || globalThis.location.origin).href;
|
return new URL(path, serverConfigManager.value.externalDomain || globalThis.location.origin).href;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleCreateSharedLink = async (dto: SharedLinkCreateDto) => {
|
export const handleCreateSharedLink = async (dto: SharedLinkCreateDto) => {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { type FeatureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import type { ActionItem } from '$lib/types';
|
import type { ActionItem } from '$lib/types';
|
||||||
import { copyToClipboard } from '$lib/utils';
|
import { copyToClipboard } from '$lib/utils';
|
||||||
import { downloadBlob } from '$lib/utils/asset-utils';
|
import { downloadBlob } from '$lib/utils/asset-utils';
|
||||||
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 { getConfig, updateConfig, type SystemConfigDto } from '@immich/sdk';
|
import { getConfig, updateConfig, type ServerFeaturesDto, type SystemConfigDto } from '@immich/sdk';
|
||||||
import { toastManager } from '@immich/ui';
|
import { toastManager } from '@immich/ui';
|
||||||
import { mdiContentCopy, mdiDownload, mdiUpload } from '@mdi/js';
|
import { mdiContentCopy, mdiDownload, mdiUpload } from '@mdi/js';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import type { MessageFormatter } from 'svelte-i18n';
|
import type { MessageFormatter } from 'svelte-i18n';
|
||||||
|
|
||||||
export const getSystemConfigActions = ($t: MessageFormatter, $featureFlags: FeatureFlags, config: SystemConfigDto) => {
|
export const getSystemConfigActions = (
|
||||||
|
$t: MessageFormatter,
|
||||||
|
featureFlags: ServerFeaturesDto,
|
||||||
|
config: SystemConfigDto,
|
||||||
|
) => {
|
||||||
const CopyToClipboard: ActionItem = {
|
const CopyToClipboard: ActionItem = {
|
||||||
title: $t('copy_to_clipboard'),
|
title: $t('copy_to_clipboard'),
|
||||||
icon: mdiContentCopy,
|
icon: mdiContentCopy,
|
||||||
|
|
@ -28,7 +31,7 @@ export const getSystemConfigActions = ($t: MessageFormatter, $featureFlags: Feat
|
||||||
const Upload: ActionItem = {
|
const Upload: ActionItem = {
|
||||||
title: $t('import_from_json'),
|
title: $t('import_from_json'),
|
||||||
icon: mdiUpload,
|
icon: mdiUpload,
|
||||||
$if: () => !$featureFlags.configFile,
|
$if: () => !featureFlags.configFile,
|
||||||
onSelect: () => handleUploadConfig(),
|
onSelect: () => handleUploadConfig(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
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 PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
|
import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
|
||||||
import UserCreateModal from '$lib/modals/UserCreateModal.svelte';
|
import UserCreateModal from '$lib/modals/UserCreateModal.svelte';
|
||||||
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
|
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
|
||||||
import UserEditModal from '$lib/modals/UserEditModal.svelte';
|
import UserEditModal from '$lib/modals/UserEditModal.svelte';
|
||||||
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
|
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user as authUser } from '$lib/stores/user.store';
|
import { user as authUser } from '$lib/stores/user.store';
|
||||||
import type { ActionItem } from '$lib/types';
|
import type { ActionItem } from '$lib/types';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
|
@ -37,9 +37,7 @@ import type { MessageFormatter } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
const getDeleteDate = (deletedAt: string): Date =>
|
const getDeleteDate = (deletedAt: string): Date =>
|
||||||
DateTime.fromISO(deletedAt)
|
DateTime.fromISO(deletedAt).plus({ days: serverConfigManager.value.userDeleteDelay }).toJSDate();
|
||||||
.plus({ days: get(serverConfig).userDeleteDelay })
|
|
||||||
.toJSDate();
|
|
||||||
|
|
||||||
export const getUserAdminsActions = ($t: MessageFormatter) => {
|
export const getUserAdminsActions = ($t: MessageFormatter) => {
|
||||||
const Create: ActionItem = {
|
const Create: ActionItem = {
|
||||||
|
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
|
||||||
import {
|
|
||||||
getConfig,
|
|
||||||
getConfigDefaults,
|
|
||||||
getServerConfig,
|
|
||||||
getServerFeatures,
|
|
||||||
type ServerConfigDto,
|
|
||||||
type ServerFeaturesDto,
|
|
||||||
type SystemConfigDto,
|
|
||||||
} from '@immich/sdk';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
export type FeatureFlags = ServerFeaturesDto & { loaded: boolean };
|
|
||||||
|
|
||||||
export const featureFlags = writable<FeatureFlags>({
|
|
||||||
loaded: false,
|
|
||||||
smartSearch: true,
|
|
||||||
duplicateDetection: false,
|
|
||||||
facialRecognition: true,
|
|
||||||
importFaces: false,
|
|
||||||
sidecar: true,
|
|
||||||
map: true,
|
|
||||||
reverseGeocoding: true,
|
|
||||||
search: true,
|
|
||||||
oauth: false,
|
|
||||||
oauthAutoLaunch: false,
|
|
||||||
passwordLogin: true,
|
|
||||||
configFile: false,
|
|
||||||
trash: true,
|
|
||||||
email: false,
|
|
||||||
ocr: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type ServerConfig = ServerConfigDto & { loaded: boolean };
|
|
||||||
|
|
||||||
export const serverConfig = writable<ServerConfig>({
|
|
||||||
loaded: false,
|
|
||||||
oauthButtonText: '',
|
|
||||||
loginPageMessage: '',
|
|
||||||
trashDays: 30,
|
|
||||||
userDeleteDelay: 7,
|
|
||||||
isInitialized: false,
|
|
||||||
isOnboarded: false,
|
|
||||||
externalDomain: '',
|
|
||||||
mapDarkStyleUrl: '',
|
|
||||||
mapLightStyleUrl: '',
|
|
||||||
publicUsers: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
class SystemConfigManager {
|
|
||||||
#value?: SystemConfigDto = $state();
|
|
||||||
#defaultValue?: SystemConfigDto = $state();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
eventManager.on('SystemConfigUpdate', (config) => (this.#value = config));
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
if (!this.#value) {
|
|
||||||
throw new Error('System config dto must be initialized first');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.#value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(config: SystemConfigDto) {
|
|
||||||
this.#value = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
get defaultValue() {
|
|
||||||
if (!this.#defaultValue) {
|
|
||||||
throw new Error('System config dto must be initialized first');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.#defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
cloneValue() {
|
|
||||||
return cloneDeep(this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
cloneDefaultValue() {
|
|
||||||
return cloneDeep(this.defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
await this.#loadConfig();
|
|
||||||
await this.#loadDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
async #loadConfig() {
|
|
||||||
this.#value = await getConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
async #loadDefault() {
|
|
||||||
this.#defaultValue = await getConfigDefaults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const retrieveServerConfig = async () => {
|
|
||||||
const [flags, config] = await Promise.all([getServerFeatures(), getServerConfig()]);
|
|
||||||
|
|
||||||
featureFlags.update(() => ({ ...flags, loaded: true }));
|
|
||||||
serverConfig.update(() => ({ ...config, loaded: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const systemConfigManager = new SystemConfigManager();
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
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 { serverConfig } from '$lib/stores/system-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 { get } from 'svelte/store';
|
|
||||||
import { loadUser } from './auth';
|
import { loadUser } from './auth';
|
||||||
|
|
||||||
export const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
export const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
||||||
|
|
@ -24,6 +23,6 @@ export const getActivationKey = async (licenseKey: string): Promise<string> => {
|
||||||
export const getLicenseLink = (license: ImmichProduct) => {
|
export const getLicenseLink = (license: ImmichProduct) => {
|
||||||
const url = new URL('/', PUBLIC_IMMICH_BUY_HOST);
|
const url = new URL('/', PUBLIC_IMMICH_BUY_HOST);
|
||||||
url.searchParams.append('productId', license);
|
url.searchParams.append('productId', license);
|
||||||
url.searchParams.append('instanceUrl', get(serverConfig).externalDomain || globalThis.origin);
|
url.searchParams.append('instanceUrl', serverConfigManager.value.externalDomain || globalThis.origin);
|
||||||
return url.href;
|
return url.href;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { retrieveServerConfig } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { initLanguage } from '$lib/utils';
|
import { initLanguage } from '$lib/utils';
|
||||||
import { defaults } from '@immich/sdk';
|
import { defaults } from '@immich/sdk';
|
||||||
import { memoize } from 'lodash-es';
|
import { memoize } from 'lodash-es';
|
||||||
|
|
@ -11,7 +12,8 @@ async function _init(fetch: Fetch) {
|
||||||
// https://github.com/oazapfts/oazapfts/blob/main/README.md#fetch-options
|
// https://github.com/oazapfts/oazapfts/blob/main/README.md#fetch-options
|
||||||
defaults.fetch = fetch;
|
defaults.fetch = fetch;
|
||||||
await initLanguage();
|
await initLanguage();
|
||||||
await retrieveServerConfig();
|
await featureFlagsManager.init();
|
||||||
|
await serverConfigManager.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = memoize(_init, () => 'singlevalue');
|
export const init = memoize(_init, () => 'singlevalue');
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||||
import { AlbumPageViewMode, AppRoute } from '$lib/constants';
|
import { AlbumPageViewMode, AppRoute } from '$lib/constants';
|
||||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
import { activityManager } from '$lib/managers/activity-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';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
||||||
|
|
@ -40,7 +41,6 @@
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { preferences, user } from '$lib/stores/user.store';
|
import { preferences, user } from '$lib/stores/user.store';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -619,7 +619,7 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $featureFlags.loaded && $featureFlags.map}
|
{#if featureFlagsManager.value.map}
|
||||||
<AlbumMap {album} />
|
<AlbumMap {album} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||||
import { AppRoute, timeToLoadTheMap } from '$lib/constants';
|
import { timeToLoadTheMap } from '$lib/constants';
|
||||||
import Portal from '$lib/elements/Portal.svelte';
|
import Portal from '$lib/elements/Portal.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { delay } from '$lib/utils/asset-utils';
|
import { delay } from '$lib/utils/asset-utils';
|
||||||
import { navigate } from '$lib/utils/navigation';
|
import { navigate } from '$lib/utils/navigation';
|
||||||
import { LoadingSpinner } from '@immich/ui';
|
import { LoadingSpinner } from '@immich/ui';
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { run } from 'svelte/legacy';
|
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -28,12 +26,6 @@
|
||||||
assetViewingStore.showAssetViewer(false);
|
assetViewingStore.showAssetViewer(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
run(() => {
|
|
||||||
if (!$featureFlags.map) {
|
|
||||||
handlePromiseError(goto(AppRoute.PHOTOS));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function onViewAssets(assetIds: string[]) {
|
async function onViewAssets(assetIds: string[]) {
|
||||||
viewingAssets = assetIds;
|
viewingAssets = assetIds;
|
||||||
viewingAssetCursor = 0;
|
viewingAssetCursor = 0;
|
||||||
|
|
@ -69,7 +61,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $featureFlags.loaded && $featureFlags.map}
|
{#if featureFlagsManager.value.map}
|
||||||
<UserPageLayout title={data.meta.title}>
|
<UserPageLayout title={data.meta.title}>
|
||||||
<div class="isolate h-full w-full">
|
<div class="isolate h-full w-full">
|
||||||
{#await import('$lib/components/shared-components/map/map.svelte')}
|
{#await import('$lib/components/shared-components/map/map.svelte')}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { AppRoute } from '$lib/constants';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { handlePromiseError } from '$lib/utils';
|
||||||
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 { getAssetInfoFromParam } from '$lib/utils/navigation';
|
import { getAssetInfoFromParam } from '$lib/utils/navigation';
|
||||||
|
|
@ -8,6 +12,10 @@ export const load = (async ({ params, url }) => {
|
||||||
const asset = await getAssetInfoFromParam(params);
|
const asset = await getAssetInfoFromParam(params);
|
||||||
const $t = await getFormatter();
|
const $t = await getFormatter();
|
||||||
|
|
||||||
|
if (!featureFlagsManager.value.map) {
|
||||||
|
handlePromiseError(goto(AppRoute.PHOTOS));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
asset,
|
asset,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@
|
||||||
import TagAction from '$lib/components/timeline/actions/TagAction.svelte';
|
import TagAction from '$lib/components/timeline/actions/TagAction.svelte';
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { lang, locale } from '$lib/stores/preferences.store';
|
import { lang, locale } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { preferences } from '$lib/stores/user.store';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
|
|
||||||
type SearchTerms = MetadataSearchDto & Pick<SmartSearchDto, 'query' | 'queryAssetId'>;
|
type SearchTerms = MetadataSearchDto & Pick<SmartSearchDto, 'query' | 'queryAssetId'>;
|
||||||
let searchQuery = $derived(page.url.searchParams.get(QueryParameter.QUERY));
|
let searchQuery = $derived(page.url.searchParams.get(QueryParameter.QUERY));
|
||||||
let smartSearchEnabled = $derived($featureFlags.loaded && $featureFlags.smartSearch);
|
let smartSearchEnabled = $derived(featureFlagsManager.value.smartSearch);
|
||||||
let terms = $derived(searchQuery ? JSON.parse(searchQuery) : {});
|
let terms = $derived(searchQuery ? JSON.parse(searchQuery) : {});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import empty3Url from '$lib/assets/empty-3.svg';
|
import empty3Url from '$lib/assets/empty-3.svg';
|
||||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||||
|
|
@ -8,11 +7,10 @@
|
||||||
import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte';
|
import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte';
|
||||||
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 { AppRoute } from '$lib/constants';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { featureFlags, serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { handlePromiseError } from '$lib/utils';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { emptyTrash, restoreTrash } from '@immich/sdk';
|
import { emptyTrash, restoreTrash } from '@immich/sdk';
|
||||||
import { Button, HStack, modalManager, Text, toastManager } from '@immich/ui';
|
import { Button, HStack, modalManager, Text, toastManager } from '@immich/ui';
|
||||||
|
|
@ -26,10 +24,6 @@
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
if (!$featureFlags.trash) {
|
|
||||||
handlePromiseError(goto(AppRoute.PHOTOS));
|
|
||||||
}
|
|
||||||
|
|
||||||
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
const options = { isTrashed: true };
|
const options = { isTrashed: true };
|
||||||
|
|
||||||
|
|
@ -76,7 +70,7 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $featureFlags.loaded && $featureFlags.trash}
|
{#if featureFlagsManager.value.trash}
|
||||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||||
{#snippet buttons()}
|
{#snippet buttons()}
|
||||||
<HStack gap={0}>
|
<HStack gap={0}>
|
||||||
|
|
@ -105,7 +99,9 @@
|
||||||
|
|
||||||
<Timeline enableRouting={true} bind:timelineManager {options} {assetInteraction} onEscape={handleEscape}>
|
<Timeline enableRouting={true} bind:timelineManager {options} {assetInteraction} onEscape={handleEscape}>
|
||||||
<p class="font-medium text-gray-500/60 dark:text-gray-300/60 p-4">
|
<p class="font-medium text-gray-500/60 dark:text-gray-300/60 p-4">
|
||||||
{$t('trashed_items_will_be_permanently_deleted_after', { values: { days: $serverConfig.trashDays } })}
|
{$t('trashed_items_will_be_permanently_deleted_after', {
|
||||||
|
values: { days: serverConfigManager.value.trashDays },
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
{#snippet empty()}
|
{#snippet empty()}
|
||||||
<EmptyPlaceholder text={$t('trash_no_results_message')} src={empty3Url} />
|
<EmptyPlaceholder text={$t('trash_no_results_message')} src={empty3Url} />
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { AppRoute } from '$lib/constants';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { handlePromiseError } from '$lib/utils';
|
||||||
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 { getAssetInfoFromParam } from '$lib/utils/navigation';
|
import { getAssetInfoFromParam } from '$lib/utils/navigation';
|
||||||
|
|
@ -8,6 +12,10 @@ export const load = (async ({ params, url }) => {
|
||||||
const asset = await getAssetInfoFromParam(params);
|
const asset = await getAssetInfoFromParam(params);
|
||||||
const $t = await getFormatter();
|
const $t = await getFormatter();
|
||||||
|
|
||||||
|
if (!featureFlagsManager.value.trash) {
|
||||||
|
handlePromiseError(goto(AppRoute.PHOTOS));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
asset,
|
asset,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||||
import DuplicatesCompareControl from '$lib/components/utilities-page/duplicates/duplicates-compare-control.svelte';
|
import DuplicatesCompareControl from '$lib/components/utilities-page/duplicates/duplicates-compare-control.svelte';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import DuplicatesInformationModal from '$lib/modals/DuplicatesInformationModal.svelte';
|
import DuplicatesInformationModal from '$lib/modals/DuplicatesInformationModal.svelte';
|
||||||
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { stackAssets } from '$lib/utils/asset-utils';
|
import { stackAssets } from '$lib/utils/asset-utils';
|
||||||
import { suggestDuplicate } from '$lib/utils/duplicate-utils';
|
import { suggestDuplicate } from '$lib/utils/duplicate-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = $featureFlags.trash
|
const message = featureFlagsManager.value.trash
|
||||||
? $t('assets_moved_to_trash_count', { values: { count: trashedCount } })
|
? $t('assets_moved_to_trash_count', { values: { count: trashedCount } })
|
||||||
: $t('permanently_deleted_assets_count', { values: { count: trashedCount } });
|
: $t('permanently_deleted_assets_count', { values: { count: trashedCount } });
|
||||||
toastManager.success(message);
|
toastManager.success(message);
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
const handleResolve = async (duplicateId: string, duplicateAssetIds: string[], trashIds: string[]) => {
|
const handleResolve = async (duplicateId: string, duplicateAssetIds: string[], trashIds: string[]) => {
|
||||||
return withConfirmation(
|
return withConfirmation(
|
||||||
async () => {
|
async () => {
|
||||||
await deleteAssets({ assetBulkDeleteDto: { ids: trashIds, force: !$featureFlags.trash } });
|
await deleteAssets({ assetBulkDeleteDto: { ids: trashIds, force: !featureFlagsManager.value.trash } });
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids: duplicateAssetIds, duplicateId: null } });
|
await updateAssets({ assetBulkUpdateDto: { ids: duplicateAssetIds, duplicateId: null } });
|
||||||
|
|
||||||
duplicates = duplicates.filter((duplicate) => duplicate.duplicateId !== duplicateId);
|
duplicates = duplicates.filter((duplicate) => duplicate.duplicateId !== duplicateId);
|
||||||
|
|
@ -109,8 +109,8 @@
|
||||||
deletedNotification(trashIds.length);
|
deletedNotification(trashIds.length);
|
||||||
await correctDuplicatesIndexAndGo(duplicatesIndex);
|
await correctDuplicatesIndexAndGo(duplicatesIndex);
|
||||||
},
|
},
|
||||||
trashIds.length > 0 && !$featureFlags.trash ? $t('delete_duplicates_confirmation') : undefined,
|
trashIds.length > 0 && !featureFlagsManager.value.trash ? $t('delete_duplicates_confirmation') : undefined,
|
||||||
trashIds.length > 0 && !$featureFlags.trash ? $t('permanently_delete') : undefined,
|
trashIds.length > 0 && !featureFlagsManager.value.trash ? $t('permanently_delete') : undefined,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
let prompt, confirmText;
|
let prompt, confirmText;
|
||||||
if ($featureFlags.trash) {
|
if (featureFlagsManager.value.trash) {
|
||||||
prompt = $t('bulk_trash_duplicates_confirmation', { values: { count: idsToDelete.length } });
|
prompt = $t('bulk_trash_duplicates_confirmation', { values: { count: idsToDelete.length } });
|
||||||
confirmText = $t('confirm');
|
confirmText = $t('confirm');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -139,7 +139,7 @@
|
||||||
|
|
||||||
return withConfirmation(
|
return withConfirmation(
|
||||||
async () => {
|
async () => {
|
||||||
await deleteAssets({ assetBulkDeleteDto: { ids: idsToDelete, force: !$featureFlags.trash } });
|
await deleteAssets({ assetBulkDeleteDto: { ids: idsToDelete, force: !featureFlagsManager.value.trash } });
|
||||||
await updateAssets({
|
await updateAssets({
|
||||||
assetBulkUpdateDto: {
|
assetBulkUpdateDto: {
|
||||||
ids: [...idsToDelete, ...idsToKeep.filter((id): id is string => !!id)],
|
ids: [...idsToDelete, ...idsToKeep.filter((id): id is string => !!id)],
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
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 { 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 VersionAnnouncementModal from '$lib/modals/VersionAnnouncementModal.svelte';
|
import VersionAnnouncementModal from '$lib/modals/VersionAnnouncementModal.svelte';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import {
|
import {
|
||||||
closeWebsocketConnection,
|
closeWebsocketConnection,
|
||||||
|
|
@ -120,7 +120,10 @@
|
||||||
{#if page.data.meta.imageUrl}
|
{#if page.data.meta.imageUrl}
|
||||||
<meta
|
<meta
|
||||||
property="og:image"
|
property="og:image"
|
||||||
content={new URL(page.data.meta.imageUrl, $serverConfig.externalDomain || globalThis.location.origin).href}
|
content={new URL(
|
||||||
|
page.data.meta.imageUrl,
|
||||||
|
serverConfigManager.value.externalDomain || globalThis.location.origin,
|
||||||
|
).href}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
@ -131,7 +134,10 @@
|
||||||
{#if page.data.meta.imageUrl}
|
{#if page.data.meta.imageUrl}
|
||||||
<meta
|
<meta
|
||||||
name="twitter:image"
|
name="twitter:image"
|
||||||
content={new URL(page.data.meta.imageUrl, $serverConfig.externalDomain || globalThis.location.origin).href}
|
content={new URL(
|
||||||
|
page.data.meta.imageUrl,
|
||||||
|
serverConfigManager.value.externalDomain || globalThis.location.origin,
|
||||||
|
).href}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
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 { get } from 'svelte/store';
|
|
||||||
import { loadUser } from '../lib/utils/auth';
|
import { loadUser } from '../lib/utils/auth';
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
|
|
@ -19,8 +17,7 @@ export const load = (async ({ fetch }) => {
|
||||||
redirect(302, AppRoute.PHOTOS);
|
redirect(302, AppRoute.PHOTOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isInitialized } = get(serverConfig);
|
if (serverConfigManager.value.isInitialized) {
|
||||||
if (isInitialized) {
|
|
||||||
// Redirect to login page if there exists an admin account (i.e. server is initialized)
|
// Redirect to login page if there exists an admin account (i.e. server is initialized)
|
||||||
redirect(302, AppRoute.AUTH_LOGIN);
|
redirect(302, AppRoute.AUTH_LOGIN);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from './$types';
|
||||||
|
|
||||||
export const load = (async () => {
|
export const load = (async () => {
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,9 @@
|
||||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||||
import { QueryParameter } from '$lib/constants';
|
import { QueryParameter } from '$lib/constants';
|
||||||
import SearchBar from '$lib/elements/SearchBar.svelte';
|
import SearchBar from '$lib/elements/SearchBar.svelte';
|
||||||
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { getSystemConfigActions } from '$lib/services/system-config.service';
|
import { getSystemConfigActions } from '$lib/services/system-config.service';
|
||||||
import { featureFlags, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { Alert, HStack } from '@immich/ui';
|
import { Alert, HStack } from '@immich/ui';
|
||||||
import {
|
import {
|
||||||
mdiAccountOutline,
|
mdiAccountOutline,
|
||||||
|
|
@ -201,7 +202,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
const { CopyToClipboard, Upload, Download } = $derived(
|
const { CopyToClipboard, Upload, Download } = $derived(
|
||||||
getSystemConfigActions($t, $featureFlags, systemConfigManager.value),
|
getSystemConfigActions($t, featureFlagsManager.value, systemConfigManager.value),
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -219,7 +220,7 @@
|
||||||
|
|
||||||
<section id="setting-content" class="flex place-content-center sm:mx-4">
|
<section id="setting-content" class="flex place-content-center sm:mx-4">
|
||||||
<section class="w-full pb-28 sm:w-5/6 md:w-4xl">
|
<section class="w-full pb-28 sm:w-5/6 md:w-4xl">
|
||||||
{#if $featureFlags.configFile}
|
{#if featureFlagsManager.value.configFile}
|
||||||
<Alert color="warning" class="text-dark my-4" title={$t('admin.config_set_by_file')} />
|
<Alert color="warning" class="text-dark my-4" title={$t('admin.config_set_by_file')} />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="block lg:hidden">
|
<div class="block lg:hidden">
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { featureFlags, serverConfig } from '$lib/stores/system-config-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { oauth } from '$lib/utils';
|
import { oauth } from '$lib/utils';
|
||||||
import { getServerErrorMessage, handleError } from '$lib/utils/handle-error';
|
import { getServerErrorMessage, handleError } from '$lib/utils/handle-error';
|
||||||
import { login, type LoginResponseDto } from '@immich/sdk';
|
import { login, type LoginResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -25,6 +26,8 @@
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
let oauthLoading = $state(true);
|
let oauthLoading = $state(true);
|
||||||
|
|
||||||
|
const serverConfig = $derived(serverConfigManager.value);
|
||||||
|
|
||||||
const onSuccess = async (user: LoginResponseDto) => {
|
const onSuccess = async (user: LoginResponseDto) => {
|
||||||
await goto(data.continueUrl, { invalidateAll: true });
|
await goto(data.continueUrl, { invalidateAll: true });
|
||||||
eventManager.emit('AuthLogin', user);
|
eventManager.emit('AuthLogin', user);
|
||||||
|
|
@ -34,7 +37,7 @@
|
||||||
const onOnboarding = () => goto(AppRoute.AUTH_ONBOARDING);
|
const onOnboarding = () => goto(AppRoute.AUTH_ONBOARDING);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!$featureFlags.oauth) {
|
if (!featureFlagsManager.value.oauth) {
|
||||||
oauthLoading = false;
|
oauthLoading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +63,7 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
($featureFlags.oauthAutoLaunch && !oauth.isAutoLaunchDisabled(globalThis.location)) ||
|
(featureFlagsManager.value.oauthAutoLaunch && !oauth.isAutoLaunchDisabled(globalThis.location)) ||
|
||||||
oauth.isAutoLaunchEnabled(globalThis.location)
|
oauth.isAutoLaunchEnabled(globalThis.location)
|
||||||
) {
|
) {
|
||||||
await goto(`${AppRoute.AUTH_LOGIN}?autoLaunch=0`, { replaceState: true });
|
await goto(`${AppRoute.AUTH_LOGIN}?autoLaunch=0`, { replaceState: true });
|
||||||
|
|
@ -80,7 +83,7 @@
|
||||||
loading = true;
|
loading = true;
|
||||||
const user = await login({ loginCredentialDto: { email, password } });
|
const user = await login({ loginCredentialDto: { email, password } });
|
||||||
|
|
||||||
if (user.isAdmin && !$serverConfig.isOnboarded) {
|
if (user.isAdmin && !serverConfig.isOnboarded) {
|
||||||
await onOnboarding();
|
await onOnboarding();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -123,64 +126,62 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $featureFlags.loaded}
|
<AuthPageLayout title={data.meta.title}>
|
||||||
<AuthPageLayout title={data.meta.title}>
|
<Stack gap={4}>
|
||||||
<Stack gap={4}>
|
{#if serverConfig.loginPageMessage}
|
||||||
{#if $serverConfig.loginPageMessage}
|
<Alert color="primary" class="mb-6">
|
||||||
<Alert color="primary" class="mb-6">
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
{@html serverConfig.loginPageMessage}
|
||||||
{@html $serverConfig.loginPageMessage}
|
</Alert>
|
||||||
</Alert>
|
{/if}
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if !oauthLoading && $featureFlags.passwordLogin}
|
{#if !oauthLoading && featureFlagsManager.value.passwordLogin}
|
||||||
<form {onsubmit} class="flex flex-col gap-4">
|
<form {onsubmit} class="flex flex-col gap-4">
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
<Alert color="danger" title={errorMessage} closable />
|
<Alert color="danger" title={errorMessage} closable />
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Field label={$t('email')}>
|
|
||||||
<Input id="email" name="email" type="email" autocomplete="email" bind:value={email} />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label={$t('password')}>
|
|
||||||
<PasswordInput id="password" bind:value={password} autocomplete="current-password" />
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Button type="submit" size="large" shape="round" fullWidth {loading} class="mt-6">{$t('to_login')}</Button>
|
|
||||||
</form>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $featureFlags.oauth}
|
|
||||||
{#if $featureFlags.passwordLogin}
|
|
||||||
<div class="inline-flex w-full items-center justify-center my-4">
|
|
||||||
<hr class="my-4 h-px w-3/4 border-0 bg-gray-200 dark:bg-gray-600" />
|
|
||||||
<span
|
|
||||||
class="absolute start-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white uppercase"
|
|
||||||
>
|
|
||||||
{$t('or')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if oauthError}
|
|
||||||
<Alert color="danger" title={oauthError} closable />
|
|
||||||
{/if}
|
|
||||||
<Button
|
|
||||||
shape="round"
|
|
||||||
loading={loading || oauthLoading}
|
|
||||||
disabled={loading || oauthLoading}
|
|
||||||
size="large"
|
|
||||||
fullWidth
|
|
||||||
color={$featureFlags.passwordLogin ? 'secondary' : 'primary'}
|
|
||||||
onclick={handleOAuthLogin}
|
|
||||||
>
|
|
||||||
{$serverConfig.oauthButtonText}
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if !$featureFlags.passwordLogin && !$featureFlags.oauth}
|
<Field label={$t('email')}>
|
||||||
<Alert color="warning" title={$t('login_has_been_disabled')} />
|
<Input id="email" name="email" type="email" autocomplete="email" bind:value={email} />
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label={$t('password')}>
|
||||||
|
<PasswordInput id="password" bind:value={password} autocomplete="current-password" />
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Button type="submit" size="large" shape="round" fullWidth {loading} class="mt-6">{$t('to_login')}</Button>
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if featureFlagsManager.value.oauth}
|
||||||
|
{#if featureFlagsManager.value.passwordLogin}
|
||||||
|
<div class="inline-flex w-full items-center justify-center my-4">
|
||||||
|
<hr class="my-4 h-px w-3/4 border-0 bg-gray-200 dark:bg-gray-600" />
|
||||||
|
<span
|
||||||
|
class="absolute start-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white uppercase"
|
||||||
|
>
|
||||||
|
{$t('or')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</Stack>
|
{#if oauthError}
|
||||||
</AuthPageLayout>
|
<Alert color="danger" title={oauthError} closable />
|
||||||
{/if}
|
{/if}
|
||||||
|
<Button
|
||||||
|
shape="round"
|
||||||
|
loading={loading || oauthLoading}
|
||||||
|
disabled={loading || oauthLoading}
|
||||||
|
size="large"
|
||||||
|
fullWidth
|
||||||
|
color={featureFlagsManager.value.passwordLogin ? 'secondary' : 'primary'}
|
||||||
|
onclick={handleOAuthLogin}
|
||||||
|
>
|
||||||
|
{serverConfig.oauthButtonText}
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if !featureFlagsManager.value.passwordLogin && !featureFlagsManager.value.oauth}
|
||||||
|
<Alert color="warning" title={$t('login_has_been_disabled')} />
|
||||||
|
{/if}
|
||||||
|
</Stack>
|
||||||
|
</AuthPageLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
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 ({ parent, url }) => {
|
export const load = (async ({ parent, url }) => {
|
||||||
await parent();
|
await parent();
|
||||||
const { isInitialized } = get(serverConfig);
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
if (!serverConfigManager.value.isInitialized) {
|
||||||
// Admin not registered
|
// Admin not registered
|
||||||
redirect(302, AppRoute.AUTH_REGISTER);
|
redirect(302, AppRoute.AUTH_REGISTER);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@
|
||||||
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 { AppRoute, QueryParameter } from '$lib/constants';
|
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||||
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
|
import { systemConfigManager } from '$lib/managers/system-config-manager.svelte';
|
||||||
import { OnboardingRole } from '$lib/models/onboarding-role';
|
import { OnboardingRole } from '$lib/models/onboarding-role';
|
||||||
import { retrieveServerConfig, serverConfig, systemConfigManager } from '$lib/stores/system-config-manager.svelte';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk';
|
import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk';
|
||||||
import {
|
import {
|
||||||
|
|
@ -95,7 +96,9 @@
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let index = $state(0);
|
let index = $state(0);
|
||||||
let userRole = $derived($user.isAdmin && !$serverConfig.isOnboarded ? OnboardingRole.SERVER : OnboardingRole.USER);
|
let userRole = $derived(
|
||||||
|
$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);
|
||||||
let onboardingProgress = $derived(
|
let onboardingProgress = $derived(
|
||||||
|
|
@ -105,7 +108,9 @@
|
||||||
const shouldRunStep = (stepRole: OnboardingRole, userRole: OnboardingRole) => {
|
const shouldRunStep = (stepRole: OnboardingRole, userRole: OnboardingRole) => {
|
||||||
return (
|
return (
|
||||||
stepRole === OnboardingRole.USER ||
|
stepRole === OnboardingRole.USER ||
|
||||||
(stepRole === OnboardingRole.SERVER && userRole === OnboardingRole.SERVER && !$serverConfig.isOnboarded)
|
(stepRole === OnboardingRole.SERVER &&
|
||||||
|
userRole === OnboardingRole.SERVER &&
|
||||||
|
!serverConfigManager.value.isOnboarded)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -127,7 +132,7 @@
|
||||||
if (nextStepIndex == -1) {
|
if (nextStepIndex == -1) {
|
||||||
if ($user.isAdmin) {
|
if ($user.isAdmin) {
|
||||||
await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } });
|
await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } });
|
||||||
await retrieveServerConfig();
|
await serverConfigManager.loadServerConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
await setUserOnboarding({
|
await setUserOnboarding({
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { retrieveServerConfig } from '$lib/stores/system-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { signUpAdmin } from '@immich/sdk';
|
import { signUpAdmin } from '@immich/sdk';
|
||||||
import { Alert, Button, Field, Input, PasswordInput, Text } from '@immich/ui';
|
import { Alert, Button, Field, Input, PasswordInput, Text } from '@immich/ui';
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await signUpAdmin({ signUpDto: { email, password, name } });
|
await signUpAdmin({ signUpDto: { email, password, name } });
|
||||||
await retrieveServerConfig();
|
await serverConfigManager.loadServerConfig();
|
||||||
await goto(AppRoute.AUTH_LOGIN);
|
await goto(AppRoute.AUTH_LOGIN);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_create_admin_account'));
|
handleError(error, $t('errors.unable_to_create_admin_account'));
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { serverConfig } from '$lib/stores/system-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
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 ({ parent }) => {
|
export const load = (async ({ parent }) => {
|
||||||
await parent();
|
await parent();
|
||||||
const { isInitialized } = get(serverConfig);
|
if (serverConfigManager.value.isInitialized) {
|
||||||
if (isInitialized) {
|
|
||||||
// Admin has been registered, redirect to login
|
// Admin has been registered, redirect to login
|
||||||
redirect(302, AppRoute.AUTH_LOGIN);
|
redirect(302, AppRoute.AUTH_LOGIN);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue