mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-09 14:42:59 +03:00
feat: add support for dark mode oidc client icons (#1039)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
@@ -12,11 +12,13 @@
|
||||
let {
|
||||
label,
|
||||
accept,
|
||||
onchange
|
||||
onchange,
|
||||
id = 'file-input'
|
||||
}: {
|
||||
label: string;
|
||||
accept?: string;
|
||||
onchange: (file: File | string | null) => void;
|
||||
id?: string;
|
||||
} = $props();
|
||||
|
||||
let url = $state('');
|
||||
@@ -47,7 +49,7 @@
|
||||
|
||||
<div class="flex">
|
||||
<FileInput
|
||||
id="logo"
|
||||
{id}
|
||||
variant="secondary"
|
||||
{accept}
|
||||
onchange={handleFileChange}
|
||||
@@ -64,9 +66,9 @@
|
||||
<LucideChevronDown class="size-4" /></Popover.Trigger
|
||||
>
|
||||
<Popover.Content class="w-80">
|
||||
<Label for="file-url" class="text-xs">URL</Label>
|
||||
<Label for="{id}-url" class="text-xs">URL</Label>
|
||||
<Input
|
||||
id="file-url"
|
||||
id="{id}-url"
|
||||
placeholder=""
|
||||
value={url}
|
||||
oninput={(e) => (url = e.currentTarget.value)}
|
||||
|
||||
@@ -68,25 +68,31 @@ class OidcService extends APIService {
|
||||
updateClient = async (id: string, client: OidcClientUpdate) =>
|
||||
(await this.api.put(`/oidc/clients/${id}`, client)).data as OidcClient;
|
||||
|
||||
updateClientLogo = async (client: OidcClient, image: File | null) => {
|
||||
if (client.hasLogo && !image) {
|
||||
await this.removeClientLogo(client.id);
|
||||
updateClientLogo = async (client: OidcClient, image: File | null, light: boolean = true) => {
|
||||
const hasLogo = light ? client.hasLogo : client.hasDarkLogo;
|
||||
|
||||
if (hasLogo && !image) {
|
||||
await this.removeClientLogo(client.id, light);
|
||||
return;
|
||||
}
|
||||
if (!client.hasLogo && !image) {
|
||||
if (!hasLogo && !image) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', image!);
|
||||
|
||||
await this.api.post(`/oidc/clients/${client.id}/logo`, formData);
|
||||
cachedOidcClientLogo.bustCache(client.id);
|
||||
await this.api.post(`/oidc/clients/${client.id}/logo`, formData, {
|
||||
params: { light }
|
||||
});
|
||||
cachedOidcClientLogo.bustCache(client.id, light);
|
||||
};
|
||||
|
||||
removeClientLogo = async (id: string) => {
|
||||
await this.api.delete(`/oidc/clients/${id}/logo`);
|
||||
cachedOidcClientLogo.bustCache(id);
|
||||
removeClientLogo = async (id: string, light: boolean = true) => {
|
||||
await this.api.delete(`/oidc/clients/${id}/logo`, {
|
||||
params: { light }
|
||||
});
|
||||
cachedOidcClientLogo.bustCache(id, light);
|
||||
};
|
||||
|
||||
createClientSecret = async (id: string) =>
|
||||
|
||||
@@ -4,6 +4,7 @@ export type OidcClientMetaData = {
|
||||
id: string;
|
||||
name: string;
|
||||
hasLogo: boolean;
|
||||
hasDarkLogo: boolean;
|
||||
requiresReauthentication: boolean;
|
||||
launchURL?: string;
|
||||
};
|
||||
@@ -37,17 +38,20 @@ export type OidcClientWithAllowedUserGroupsCount = OidcClient & {
|
||||
allowedUserGroupsCount: number;
|
||||
};
|
||||
|
||||
export type OidcClientUpdate = Omit<OidcClient, 'id' | 'logoURL' | 'hasLogo'>;
|
||||
export type OidcClientUpdate = Omit<OidcClient, 'id' | 'logoURL' | 'hasLogo' | 'hasDarkLogo'>;
|
||||
export type OidcClientCreate = OidcClientUpdate & {
|
||||
id?: string;
|
||||
};
|
||||
export type OidcClientUpdateWithLogo = OidcClientUpdate & {
|
||||
logo: File | null | undefined;
|
||||
darkLogo: File | null | undefined;
|
||||
};
|
||||
|
||||
export type OidcClientCreateWithLogo = OidcClientCreate & {
|
||||
logo?: File | null;
|
||||
logoUrl?: string;
|
||||
darkLogo?: File | null;
|
||||
darkLogoUrl?: string;
|
||||
};
|
||||
|
||||
export type OidcDeviceCodeInfo = {
|
||||
|
||||
@@ -9,73 +9,85 @@ type CachableImage = {
|
||||
|
||||
export const cachedApplicationLogo: CachableImage = {
|
||||
getUrl: (light = true) => {
|
||||
let url = '/api/application-images/logo';
|
||||
if (!light) {
|
||||
url += '?light=false';
|
||||
}
|
||||
const url = new URL('/api/application-images/logo', window.location.origin);
|
||||
if (!light) url.searchParams.set('light', 'false');
|
||||
return getCachedImageUrl(url);
|
||||
},
|
||||
bustCache: (light = true) => {
|
||||
let url = '/api/application-images/logo';
|
||||
if (!light) {
|
||||
url += '?light=false';
|
||||
}
|
||||
const url = new URL('/api/application-images/logo', window.location.origin);
|
||||
if (!light) url.searchParams.set('light', 'false');
|
||||
bustImageCache(url);
|
||||
}
|
||||
};
|
||||
|
||||
export const cachedBackgroundImage: CachableImage = {
|
||||
getUrl: () => getCachedImageUrl('/api/application-images/background'),
|
||||
bustCache: () => bustImageCache('/api/application-images/background')
|
||||
getUrl: () =>
|
||||
getCachedImageUrl(new URL('/api/application-images/background', window.location.origin)),
|
||||
bustCache: () =>
|
||||
bustImageCache(new URL('/api/application-images/background', window.location.origin))
|
||||
};
|
||||
|
||||
export const cachedProfilePicture: CachableImage = {
|
||||
getUrl: (userId: string) => {
|
||||
const url = `/api/users/${userId}/profile-picture.png`;
|
||||
const url = new URL(`/api/users/${userId}/profile-picture.png`, window.location.origin);
|
||||
return getCachedImageUrl(url);
|
||||
},
|
||||
bustCache: (userId: string) => {
|
||||
const url = `/api/users/${userId}/profile-picture.png`;
|
||||
const url = new URL(`/api/users/${userId}/profile-picture.png`, window.location.origin);
|
||||
bustImageCache(url);
|
||||
}
|
||||
};
|
||||
|
||||
export const cachedOidcClientLogo: CachableImage = {
|
||||
getUrl: (clientId: string) => {
|
||||
const url = `/api/oidc/clients/${clientId}/logo`;
|
||||
getUrl: (clientId: string, light = true) => {
|
||||
const url = new URL(`/api/oidc/clients/${clientId}/logo`, window.location.origin);
|
||||
if (!light) url.searchParams.set('light', 'false');
|
||||
return getCachedImageUrl(url);
|
||||
},
|
||||
bustCache: (clientId: string) => {
|
||||
const url = `/api/oidc/clients/${clientId}/logo`;
|
||||
bustCache: (clientId: string, light = true) => {
|
||||
const url = new URL(`/api/oidc/clients/${clientId}/logo`, window.location.origin);
|
||||
if (!light) url.searchParams.set('light', 'false');
|
||||
bustImageCache(url);
|
||||
}
|
||||
};
|
||||
|
||||
function getCachedImageUrl(url: string) {
|
||||
const skipCacheUntil = getSkipCacheUntil(url);
|
||||
function getCachedImageUrl(url: URL) {
|
||||
const baseKey = normalizeUrlForKey(url);
|
||||
const skipCacheUntil = getSkipCacheUntil(baseKey);
|
||||
const skipCache = skipCacheUntil > Date.now();
|
||||
|
||||
const finalUrl = new URL(url.toString());
|
||||
if (skipCache) {
|
||||
const skipCacheParam = new URLSearchParams();
|
||||
skipCacheParam.append('skip-cache', skipCacheUntil.toString());
|
||||
url += '?' + skipCacheParam.toString();
|
||||
finalUrl.searchParams.set('skip-cache', skipCacheUntil.toString());
|
||||
}
|
||||
|
||||
return url.toString();
|
||||
return finalUrl.pathname + (finalUrl.search ? `?${finalUrl.searchParams.toString()}` : '');
|
||||
}
|
||||
|
||||
function bustImageCache(url: string) {
|
||||
const skipCacheUntil: SkipCacheUntil = JSON.parse(
|
||||
localStorage.getItem('skip-cache-until') ?? '{}'
|
||||
);
|
||||
skipCacheUntil[hashKey(url)] = Date.now() + 1000 * 60 * 15; // 15 minutes
|
||||
localStorage.setItem('skip-cache-until', JSON.stringify(skipCacheUntil));
|
||||
function bustImageCache(url: URL) {
|
||||
const key = normalizeUrlForKey(url);
|
||||
const expiresAt = Date.now() + 1000 * 60 * 15;
|
||||
|
||||
const store: SkipCacheUntil = JSON.parse(localStorage.getItem('skip-cache-until') ?? '{}');
|
||||
store[key] = expiresAt;
|
||||
localStorage.setItem('skip-cache-until', JSON.stringify(store));
|
||||
}
|
||||
|
||||
function getSkipCacheUntil(url: string) {
|
||||
const skipCacheUntil: SkipCacheUntil = JSON.parse(
|
||||
localStorage.getItem('skip-cache-until') ?? '{}'
|
||||
function getSkipCacheUntil(key: string): number {
|
||||
const store: SkipCacheUntil = JSON.parse(localStorage.getItem('skip-cache-until') ?? '{}');
|
||||
return store[key] ?? 0;
|
||||
}
|
||||
|
||||
// Removes transient params and normalizes query order before hashing
|
||||
function normalizeUrlForKey(url: URL) {
|
||||
const u = new URL(url.toString());
|
||||
u.searchParams.delete('skip-cache');
|
||||
|
||||
const sortedParams = new URLSearchParams(
|
||||
[...u.searchParams.entries()].sort(([a], [b]) => a.localeCompare(b))
|
||||
);
|
||||
return skipCacheUntil[hashKey(url)] ?? 0;
|
||||
const normalized = u.pathname + (sortedParams.toString() ? `?${sortedParams.toString()}` : '');
|
||||
return hashKey(normalized);
|
||||
}
|
||||
|
||||
function hashKey(key: string): string {
|
||||
@@ -83,7 +95,7 @@ function hashKey(key: string): string {
|
||||
for (let i = 0; i < key.length; i++) {
|
||||
const char = key.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash;
|
||||
hash |= 0;
|
||||
}
|
||||
return Math.abs(hash).toString(36);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
<div
|
||||
class={{
|
||||
'group relative flex items-center rounded': true,
|
||||
'bg-[#F1F1F5]': forceColorScheme === 'light',
|
||||
'bg-[#27272A]': forceColorScheme === 'dark',
|
||||
'bg-[#F5F5F5]': forceColorScheme === 'light',
|
||||
'bg-[#262626]': forceColorScheme === 'dark',
|
||||
'bg-muted': !forceColorScheme
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -47,7 +47,12 @@
|
||||
const dataPromise = oidcService.updateClient(client.id, updatedClient);
|
||||
const imagePromise =
|
||||
updatedClient.logo !== undefined
|
||||
? oidcService.updateClientLogo(client, updatedClient.logo)
|
||||
? oidcService.updateClientLogo(client, updatedClient.logo, true)
|
||||
: Promise.resolve();
|
||||
|
||||
const darkImagePromise =
|
||||
updatedClient.darkLogo !== undefined
|
||||
? oidcService.updateClientLogo(client, updatedClient.darkLogo, false)
|
||||
: Promise.resolve();
|
||||
|
||||
client.isPublic = updatedClient.isPublic;
|
||||
@@ -56,8 +61,15 @@
|
||||
? m.enabled()
|
||||
: m.disabled();
|
||||
|
||||
await Promise.all([dataPromise, imagePromise])
|
||||
await Promise.all([dataPromise, imagePromise, darkImagePromise])
|
||||
.then(() => {
|
||||
// Update the hasLogo and hasDarkLogo flags after successful upload
|
||||
if (updatedClient.logo !== undefined) {
|
||||
client.hasLogo = updatedClient.logo !== null || !!updatedClient.logoUrl;
|
||||
}
|
||||
if (updatedClient.darkLogo !== undefined) {
|
||||
client.hasDarkLogo = updatedClient.darkLogo !== null || !!updatedClient.darkLogoUrl;
|
||||
}
|
||||
toast.success(m.oidc_client_updated_successfully());
|
||||
})
|
||||
.catch((e) => {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import FormInput from '$lib/components/form/form-input.svelte';
|
||||
import SwitchWithLabel from '$lib/components/form/switch-with-label.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import type {
|
||||
OidcClient,
|
||||
@@ -13,7 +14,7 @@
|
||||
import { createForm } from '$lib/utils/form-util';
|
||||
import { cn } from '$lib/utils/style';
|
||||
import { callbackUrlSchema, emptyToUndefined, optionalUrl } from '$lib/utils/zod-util';
|
||||
import { LucideChevronDown } from '@lucide/svelte';
|
||||
import { LucideChevronDown, LucideMoon, LucideSun } from '@lucide/svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { z } from 'zod/v4';
|
||||
import FederatedIdentitiesInput from './federated-identities-input.svelte';
|
||||
@@ -32,9 +33,13 @@
|
||||
let isLoading = $state(false);
|
||||
let showAdvancedOptions = $state(false);
|
||||
let logo = $state<File | null | undefined>();
|
||||
let darkLogo = $state<File | null | undefined>();
|
||||
let logoDataURL: string | null = $state(
|
||||
existingClient?.hasLogo ? cachedOidcClientLogo.getUrl(existingClient!.id) : null
|
||||
);
|
||||
let darkLogoDataURL: string | null = $state(
|
||||
existingClient?.hasDarkLogo ? cachedOidcClientLogo.getUrl(existingClient!.id, false) : null
|
||||
);
|
||||
|
||||
const client = {
|
||||
id: '',
|
||||
@@ -48,7 +53,8 @@
|
||||
credentials: {
|
||||
federatedIdentities: existingClient?.credentials?.federatedIdentities || []
|
||||
},
|
||||
logoUrl: ''
|
||||
logoUrl: '',
|
||||
darkLogoUrl: ''
|
||||
};
|
||||
|
||||
const formSchema = z.object({
|
||||
@@ -70,6 +76,7 @@
|
||||
requiresReauthentication: z.boolean(),
|
||||
launchURL: optionalUrl,
|
||||
logoUrl: optionalUrl,
|
||||
darkLogoUrl: optionalUrl,
|
||||
credentials: z.object({
|
||||
federatedIdentities: z.array(
|
||||
z.object({
|
||||
@@ -92,37 +99,63 @@
|
||||
|
||||
const success = await callback({
|
||||
...data,
|
||||
logo: $inputs.logoUrl?.value ? null : logo,
|
||||
logoUrl: $inputs.logoUrl?.value
|
||||
logo: $inputs.logoUrl?.value ? undefined : logo,
|
||||
logoUrl: $inputs.logoUrl?.value,
|
||||
darkLogo: $inputs.darkLogoUrl?.value ? undefined : darkLogo,
|
||||
darkLogoUrl: $inputs.darkLogoUrl?.value
|
||||
});
|
||||
|
||||
const hasLogo = logo != null || !!$inputs.logoUrl?.value;
|
||||
if (success && existingClient && hasLogo) {
|
||||
logoDataURL = cachedOidcClientLogo.getUrl(existingClient.id);
|
||||
const hasDarkLogo = darkLogo != null || !!$inputs.darkLogoUrl?.value;
|
||||
if (success && existingClient) {
|
||||
if (hasLogo) {
|
||||
logoDataURL = cachedOidcClientLogo.getUrl(existingClient.id);
|
||||
}
|
||||
if (hasDarkLogo) {
|
||||
darkLogoDataURL = cachedOidcClientLogo.getUrl(existingClient.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (success && !existingClient) form.reset();
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
function onLogoChange(input: File | string | null) {
|
||||
function onLogoChange(input: File | string | null, light: boolean = true) {
|
||||
if (input == null) return;
|
||||
|
||||
const logoUrlInput = light ? $inputs.logoUrl : $inputs.darkLogoUrl;
|
||||
|
||||
if (typeof input === 'string') {
|
||||
logo = null;
|
||||
logoDataURL = input || null;
|
||||
$inputs.logoUrl!.value = input;
|
||||
if (light) {
|
||||
logo = null;
|
||||
logoDataURL = input || null;
|
||||
} else {
|
||||
darkLogo = null;
|
||||
darkLogoDataURL = input || null;
|
||||
}
|
||||
logoUrlInput!.value = input;
|
||||
} else {
|
||||
logo = input;
|
||||
$inputs.logoUrl && ($inputs.logoUrl.value = '');
|
||||
logoDataURL = URL.createObjectURL(input);
|
||||
if (light) {
|
||||
logo = input;
|
||||
logoDataURL = URL.createObjectURL(input);
|
||||
} else {
|
||||
darkLogo = input;
|
||||
darkLogoDataURL = URL.createObjectURL(input);
|
||||
}
|
||||
logoUrlInput && (logoUrlInput.value = '');
|
||||
}
|
||||
}
|
||||
|
||||
function resetLogo() {
|
||||
logo = null;
|
||||
logoDataURL = null;
|
||||
$inputs.logoUrl && ($inputs.logoUrl.value = '');
|
||||
function resetLogo(light: boolean = true) {
|
||||
if (light) {
|
||||
logo = null;
|
||||
logoDataURL = null;
|
||||
$inputs.logoUrl && ($inputs.logoUrl.value = '');
|
||||
} else {
|
||||
darkLogo = null;
|
||||
darkLogoDataURL = null;
|
||||
$inputs.darkLogoUrl && ($inputs.darkLogoUrl.value = '');
|
||||
}
|
||||
}
|
||||
|
||||
function getFederatedIdentityErrors(errors: z.ZodError<any> | undefined) {
|
||||
@@ -182,13 +215,49 @@
|
||||
bind:checked={$inputs.requiresReauthentication.value}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
<OidcClientImageInput
|
||||
{logoDataURL}
|
||||
{resetLogo}
|
||||
clientName={$inputs.name.value}
|
||||
{onLogoChange}
|
||||
/>
|
||||
<div class="mt-7 w-full md:w-1/2">
|
||||
<Tabs.Root value="light-logo">
|
||||
<Tabs.Content value="light-logo">
|
||||
<OidcClientImageInput
|
||||
{logoDataURL}
|
||||
resetLogo={() => resetLogo(true)}
|
||||
clientName={$inputs.name.value}
|
||||
light={true}
|
||||
onLogoChange={(input) => onLogoChange(input, true)}
|
||||
>
|
||||
{#snippet tabTriggers()}
|
||||
<Tabs.List class="grid h-9 w-full grid-cols-2">
|
||||
<Tabs.Trigger value="light-logo" class="px-3">
|
||||
<LucideSun class="size-4" />
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="dark-logo" class="px-3">
|
||||
<LucideMoon class="size-4" />
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
{/snippet}
|
||||
</OidcClientImageInput>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="dark-logo">
|
||||
<OidcClientImageInput
|
||||
light={false}
|
||||
logoDataURL={darkLogoDataURL}
|
||||
resetLogo={() => resetLogo(false)}
|
||||
clientName={$inputs.name.value}
|
||||
onLogoChange={(input) => onLogoChange(input, false)}
|
||||
>
|
||||
{#snippet tabTriggers()}
|
||||
<Tabs.List class="grid h-9 w-full grid-cols-2">
|
||||
<Tabs.Trigger value="light-logo" class="px-3">
|
||||
<LucideSun class="size-4" />
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="dark-logo" class="px-3">
|
||||
<LucideMoon class="size-4" />
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
{/snippet}
|
||||
</OidcClientImageInput>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
|
||||
{#if showAdvancedOptions}
|
||||
|
||||
@@ -5,40 +5,53 @@
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { LucideX } from '@lucide/svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
logoDataURL,
|
||||
clientName,
|
||||
resetLogo,
|
||||
onLogoChange
|
||||
onLogoChange,
|
||||
light,
|
||||
tabTriggers
|
||||
}: {
|
||||
logoDataURL: string | null;
|
||||
clientName: string;
|
||||
resetLogo: () => void;
|
||||
onLogoChange: (file: File | string | null) => void;
|
||||
tabTriggers?: Snippet;
|
||||
light: boolean;
|
||||
} = $props();
|
||||
|
||||
let id = `oidc-client-logo-${light ? 'light' : 'dark'}`;
|
||||
</script>
|
||||
|
||||
<Label for="logo">{m.logo()}</Label>
|
||||
<div class="flex items-end gap-4">
|
||||
<Label for={id}>{m.logo()}</Label>
|
||||
<div class="flex h-24 items-end gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if tabTriggers}
|
||||
{@render tabTriggers()}
|
||||
{/if}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UrlFileInput {id} label={m.upload_logo()} accept="image/*" onchange={onLogoChange} />
|
||||
</div>
|
||||
</div>
|
||||
{#if logoDataURL}
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="relative shrink-0">
|
||||
<ImageBox class="size-24" src={logoDataURL} alt={m.name_logo({ name: clientName })} />
|
||||
<ImageBox
|
||||
class="size-24 {light ? 'bg-[#F5F5F5]' : 'bg-[#262626]'}"
|
||||
src={logoDataURL}
|
||||
alt={m.name_logo({ name: clientName })}
|
||||
/>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="icon"
|
||||
onclick={resetLogo}
|
||||
class="absolute -top-2 -right-2 size-6 rounded-full shadow-md"
|
||||
class="absolute -top-2 -right-2 size-6 rounded-full shadow-md "
|
||||
>
|
||||
<LucideX class="size-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UrlFileInput label={m.upload_logo()} accept="image/*" onchange={onLogoChange} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { cachedOidcClientLogo } from '$lib/utils/cached-image-util';
|
||||
import { axiosErrorToast } from '$lib/utils/error-util';
|
||||
import { LucidePencil, LucideTrash } from '@lucide/svelte';
|
||||
import { mode } from 'mode-watcher';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const oidcService = new OIDCService();
|
||||
@@ -22,6 +23,8 @@
|
||||
return tableRef?.refresh();
|
||||
}
|
||||
|
||||
const isLightMode = $derived(mode.current === 'light');
|
||||
|
||||
const booleanFilterValues = [
|
||||
{ label: m.enabled(), value: true },
|
||||
{ label: m.disabled(), value: false }
|
||||
@@ -103,7 +106,7 @@
|
||||
{#if item.hasLogo}
|
||||
<ImageBox
|
||||
class="size-12 rounded-lg"
|
||||
src={cachedOidcClientLogo.getUrl(item.id)}
|
||||
src={cachedOidcClientLogo.getUrl(item.id, isLightMode)}
|
||||
alt={m.name_logo({ name: item.name })}
|
||||
/>
|
||||
{:else}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<ImageBox
|
||||
class="size-14"
|
||||
src={client.hasLogo
|
||||
? cachedOidcClientLogo.getUrl(client.id)
|
||||
? cachedOidcClientLogo.getUrl(client.id, isLightMode)
|
||||
: cachedApplicationLogo.getUrl(isLightMode)}
|
||||
alt={m.name_logo({ name: client.name })}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user