v1.0.8
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
import {
|
||||
findFirstAccessibleLinkInLayout,
|
||||
findFirstVisibleLinkInLayout,
|
||||
isLinkAccessible,
|
||||
type CanGet,
|
||||
type HasPermission,
|
||||
} from '@/lib/layout';
|
||||
import type { Layout, Schema } from '@/types/schema';
|
||||
|
||||
const STORAGE_KEY = 'stalwart-last-visited';
|
||||
|
||||
function readAll(): Record<string, unknown> {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
const parsed: unknown = raw ? JSON.parse(raw) : null;
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function rememberLastVisited(section: string, viewName: string): void {
|
||||
try {
|
||||
const all = readAll();
|
||||
if (all[section] === viewName) return;
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify({ ...all, [section]: viewName }));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export function sectionLandingLink(
|
||||
schema: Schema,
|
||||
layout: Layout,
|
||||
edition: string,
|
||||
canGet: CanGet,
|
||||
hasPerm?: HasPermission,
|
||||
): string | null {
|
||||
const last = readAll()[layout.name];
|
||||
if (typeof last === 'string' && isLinkAccessible(schema, last, edition, canGet, hasPerm)) {
|
||||
return last;
|
||||
}
|
||||
return (
|
||||
findFirstAccessibleLinkInLayout(schema, layout, edition, canGet, hasPerm) ??
|
||||
findFirstVisibleLinkInLayout(schema, layout, edition, canGet, hasPerm)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
import { getApiBaseUrl } from '@/services/api';
|
||||
|
||||
export type LogoState = { status: 'loading' } | { status: 'custom'; url: string } | { status: 'default' };
|
||||
|
||||
let state: LogoState = { status: 'loading' };
|
||||
let started = false;
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
export function getLogoState(): LogoState {
|
||||
return state;
|
||||
}
|
||||
|
||||
export function subscribeToLogo(listener: () => void): () => void {
|
||||
listeners.add(listener);
|
||||
return () => {
|
||||
listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
export function loadLogoOnce(): void {
|
||||
if (started) return;
|
||||
started = true;
|
||||
fetch(`${getApiBaseUrl()}/logo`)
|
||||
.then(async (response) => {
|
||||
const contentType = response.headers.get('content-type') ?? '';
|
||||
if (!response.ok || !contentType.startsWith('image/')) return null;
|
||||
return URL.createObjectURL(await response.blob());
|
||||
})
|
||||
.catch(() => null)
|
||||
.then((url) => {
|
||||
state = url ? { status: 'custom', url } : { status: 'default' };
|
||||
listeners.forEach((notify) => notify());
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user