Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(web): settings accordion open state #7504

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts" context="module">
export type AccordionState = Set<string>;

const { get: getAccordionState, set: setAccordionState } = createContext<Writable<AccordionState>>();
export { getAccordionState };
</script>

<script lang="ts">
import { writable, type Writable } from 'svelte/store';
import { createContext } from '$lib/utils/context';
import { page } from '$app/stores';
import { handlePromiseError } from '$lib/utils';
import { goto } from '$app/navigation';

const getParamValues = (param: string) => {
return new Set(($page.url.searchParams.get(param) || '').split(' ').filter((x) => x !== ''));
};

export let queryParam: string;
export let state: Writable<AccordionState> = writable(getParamValues(queryParam));
setAccordionState(state);

$: if (queryParam && $state) {
const searchParams = new URLSearchParams($page.url.searchParams);
if ($state.size > 0) {
searchParams.set(queryParam, [...$state].join(' '));
} else {
searchParams.delete(queryParam);
}

handlePromiseError(goto(`?${searchParams.toString()}`, { replaceState: true, noScroll: true, keepFocus: true }));
}
</script>

<slot />
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
<script lang="ts">
import { page } from '$app/stores';
import { QueryParameter } from '$lib/constants';
import { hasParamValue, handlePromiseError, updateParamList } from '$lib/utils';
import { slide } from 'svelte/transition';
import { getAccordionState } from './setting-accordion-state.svelte';

const accordionState = getAccordionState();

export let title: string;
export let subtitle = '';
export let key: string;
export let isOpen = false;

const syncFromUrl = () => (isOpen = hasParamValue(QueryParameter.IS_OPEN, key));
const syncToUrl = (isOpen: boolean) => updateParamList({ param: QueryParameter.IS_OPEN, value: key, add: isOpen });
export let isOpen = $accordionState.has(key);

isOpen ? handlePromiseError(syncToUrl(true)) : syncFromUrl();
$: $page.url && syncFromUrl();
$: setIsOpen(isOpen);

const toggle = async () => {
isOpen = !isOpen;
await syncToUrl(isOpen);
const setIsOpen = (isOpen: boolean) => {
if (isOpen) {
$accordionState = $accordionState.add(key);
} else {
$accordionState.delete(key);
$accordionState = $accordionState;
}
};
</script>

<div class="border-b-[1px] border-gray-200 py-4 dark:border-gray-700">
<button on:click={toggle} class="flex w-full place-items-center justify-between text-left">
<button on:click={() => (isOpen = !isOpen)} class="flex w-full place-items-center justify-between text-left">
<div>
<h2 class="font-medium text-immich-primary dark:text-immich-dark-primary">
{title}
Expand Down
81 changes: 38 additions & 43 deletions web/src/lib/components/user-settings-page/user-settings-list.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script lang="ts">
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { OpenSettingQueryParameterValue, QueryParameter } from '$lib/constants';
import { featureFlags } from '$lib/stores/server-config.store';
Expand All @@ -17,60 +16,56 @@
import TrashSettings from './trash-settings.svelte';
import UserAPIKeyList from './user-api-key-list.svelte';
import UserProfileSettings from './user-profile-settings.svelte';
import SettingAccordionState from '../shared-components/settings/setting-accordion-state.svelte';

export let keys: ApiKeyResponseDto[] = [];
export let devices: AuthDeviceResponseDto[] = [];

let oauthOpen = false;
if (browser) {
oauthOpen = oauth.isCallback(window.location);
}
let oauthOpen =
oauth.isCallback(window.location) ||
$page.url.searchParams.get(QueryParameter.OPEN_SETTING) === OpenSettingQueryParameterValue.OAUTH;
</script>

<SettingAccordion key="appearance" title="Appearance" subtitle="Manage the app appearance">
<AppearanceSettings />
</SettingAccordion>

<SettingAccordion key="account" title="Account" subtitle="Manage your account">
<UserProfileSettings />
</SettingAccordion>
<SettingAccordionState queryParam={QueryParameter.IS_OPEN}>
<SettingAccordion key="appearance" title="Appearance" subtitle="Manage the app appearance">
<AppearanceSettings />
</SettingAccordion>

<SettingAccordion key="api-keys" title="API Keys" subtitle="Manage your API keys">
<UserAPIKeyList bind:keys />
</SettingAccordion>
<SettingAccordion key="account" title="Account" subtitle="Manage your account">
<UserProfileSettings />
</SettingAccordion>

<SettingAccordion key="authorized-devices" title="Authorized Devices" subtitle="Manage your logged-in devices">
<DeviceList bind:devices />
</SettingAccordion>
<SettingAccordion key="api-keys" title="API Keys" subtitle="Manage your API keys">
<UserAPIKeyList bind:keys />
</SettingAccordion>

<SettingAccordion key="libraries" title="Libraries" subtitle="Manage your asset libraries">
<LibraryList />
</SettingAccordion>
<SettingAccordion key="authorized-devices" title="Authorized Devices" subtitle="Manage your logged-in devices">
<DeviceList bind:devices />
</SettingAccordion>

<SettingAccordion key="memories" title="Memories" subtitle="Manage what you see in your memories.">
<MemoriesSettings user={$user} />
</SettingAccordion>
<SettingAccordion key="libraries" title="Libraries" subtitle="Manage your asset libraries">
<LibraryList />
</SettingAccordion>

{#if $featureFlags.loaded && $featureFlags.oauth}
<SettingAccordion
key="oauth"
title="OAuth"
subtitle="Manage your OAuth connection"
isOpen={oauthOpen ||
$page.url.searchParams.get(QueryParameter.OPEN_SETTING) === OpenSettingQueryParameterValue.OAUTH}
>
<OAuthSettings user={$user} />
<SettingAccordion key="memories" title="Memories" subtitle="Manage what you see in your memories.">
<MemoriesSettings user={$user} />
</SettingAccordion>
{/if}

<SettingAccordion key="password" title="Password" subtitle="Change your password">
<ChangePasswordSettings />
</SettingAccordion>
{#if $featureFlags.loaded && $featureFlags.oauth}
<SettingAccordion key="oauth" title="OAuth" subtitle="Manage your OAuth connection" isOpen={oauthOpen || undefined}>
<OAuthSettings user={$user} />
</SettingAccordion>
{/if}

<SettingAccordion key="sharing" title="Sharing" subtitle="Manage sharing with partners">
<PartnerSettings user={$user} />
</SettingAccordion>
<SettingAccordion key="password" title="Password" subtitle="Change your password">
<ChangePasswordSettings />
</SettingAccordion>

<SettingAccordion key="trash" title="Trash" subtitle="Manage trash settings">
<TrashSettings />
</SettingAccordion>
<SettingAccordion key="sharing" title="Sharing" subtitle="Manage sharing with partners">
<PartnerSettings user={$user} />
</SettingAccordion>

<SettingAccordion key="trash" title="Trash" subtitle="Manage trash settings">
<TrashSettings />
</SettingAccordion>
</SettingAccordionState>
33 changes: 0 additions & 33 deletions web/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification';
import { locales } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
Expand All @@ -14,37 +12,6 @@ import {
unlinkOAuthAccount,
type UserResponseDto,
} from '@immich/sdk';
import { get } from 'svelte/store';

interface UpdateParamAction {
param: string;
value: string;
add: boolean;
}

const getParamValues = (param: string) =>
new Set((get(page).url.searchParams.get(param) || '').split(' ').filter((x) => x !== ''));

export const hasParamValue = (param: string, value: string) => getParamValues(param).has(value);

export const updateParamList = async ({ param, value, add }: UpdateParamAction) => {
const values = getParamValues(param);

if (add) {
values.add(value);
} else {
values.delete(value);
}

const searchParams = new URLSearchParams(get(page).url.searchParams);
searchParams.set(param, [...values.values()].join(' '));

if (values.size === 0) {
searchParams.delete(param);
}

await goto(`?${searchParams.toString()}`, { replaceState: true, noScroll: true, keepFocus: true });
};

export const getJobName = (jobName: JobName) => {
const names: Record<JobName, string> = {
Expand Down
30 changes: 17 additions & 13 deletions web/src/routes/admin/system-settings/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import { downloadBlob } from '$lib/utils/asset-utils';
import { mdiAlert, mdiContentCopy, mdiDownload } from '@mdi/js';
import type { PageData } from './$types';
import SettingAccordionState from '$lib/components/shared-components/settings/setting-accordion-state.svelte';
import { QueryParameter } from '$lib/constants';

export let data: PageData;

Expand Down Expand Up @@ -176,19 +178,21 @@
<AdminSettings bind:config let:handleReset let:handleSave let:savedConfig let:defaultConfig>
<section id="setting-content" class="flex place-content-center sm:mx-4">
<section class="w-full pb-28 sm:w-5/6 md:w-[850px]">
{#each settings as { item, title, subtitle, key }}
<SettingAccordion {title} {subtitle} {key}>
<svelte:component
this={item}
on:save={({ detail }) => handleSave(detail)}
on:reset={({ detail }) => handleReset(detail)}
disabled={$featureFlags.configFile}
{defaultConfig}
{config}
{savedConfig}
/>
</SettingAccordion>
{/each}
<SettingAccordionState queryParam={QueryParameter.IS_OPEN}>
{#each settings as { item, title, subtitle, key }}
<SettingAccordion {title} {subtitle} {key}>
<svelte:component
this={item}
on:save={({ detail }) => handleSave(detail)}
on:reset={({ detail }) => handleReset(detail)}
disabled={$featureFlags.configFile}
{defaultConfig}
{config}
{savedConfig}
/>
</SettingAccordion>
{/each}
</SettingAccordionState>
</section>
</section>
</AdminSettings>
Expand Down