style: 优化个人中心和偏好设置弹窗样式,重构工作入口为精致分屏布局并移除首字徽标
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
import { getDesktopServerUrl } from "./desktopServerConfig";
|
||||
import { isTauriRuntime } from "./platform";
|
||||
|
||||
const SAVED_LOGIN_CREDENTIAL_VERSION = 1;
|
||||
|
||||
export interface SavedLoginCredential {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
type StoredLoginCredential = {
|
||||
version?: unknown;
|
||||
email?: unknown;
|
||||
password?: unknown;
|
||||
savedAt?: unknown;
|
||||
};
|
||||
|
||||
const invokeLoginCredential = async <T>(
|
||||
command: "login_credential_get" | "login_credential_set" | "login_credential_delete",
|
||||
args: Record<string, string>,
|
||||
): Promise<T> => {
|
||||
const { invoke } = await import("@tauri-apps/api/core");
|
||||
return invoke<T>(command, args);
|
||||
};
|
||||
|
||||
const normalizeEmail = (email: string): string => email.trim().toLowerCase();
|
||||
|
||||
const serializeDesktopCredential = (credential: SavedLoginCredential): string =>
|
||||
JSON.stringify({
|
||||
version: SAVED_LOGIN_CREDENTIAL_VERSION,
|
||||
email: normalizeEmail(credential.email),
|
||||
password: credential.password,
|
||||
savedAt: Date.now(),
|
||||
});
|
||||
|
||||
const parseDesktopCredential = (stored: string | null): SavedLoginCredential | null => {
|
||||
if (!stored) return null;
|
||||
try {
|
||||
const payload = JSON.parse(stored) as StoredLoginCredential;
|
||||
if (
|
||||
payload.version !== SAVED_LOGIN_CREDENTIAL_VERSION ||
|
||||
typeof payload.email !== "string" ||
|
||||
typeof payload.password !== "string" ||
|
||||
typeof payload.savedAt !== "number"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const email = normalizeEmail(payload.email);
|
||||
if (!email || !payload.password) return null;
|
||||
return { email, password: payload.password };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getDesktopSavedLoginCredential = async (): Promise<SavedLoginCredential | null> => {
|
||||
const serverOrigin = getDesktopServerUrl();
|
||||
if (!serverOrigin) return null;
|
||||
const stored = await invokeLoginCredential<string | null>("login_credential_get", { serverOrigin });
|
||||
const parsed = parseDesktopCredential(stored);
|
||||
if (!parsed && stored) {
|
||||
await invokeLoginCredential<void>("login_credential_delete", { serverOrigin });
|
||||
}
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const saveDesktopLoginCredential = async (credential: SavedLoginCredential): Promise<boolean> => {
|
||||
const serverOrigin = getDesktopServerUrl();
|
||||
if (!serverOrigin) return false;
|
||||
await invokeLoginCredential<void>("login_credential_set", {
|
||||
serverOrigin,
|
||||
credential: serializeDesktopCredential(credential),
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
const clearDesktopLoginCredential = async (): Promise<boolean> => {
|
||||
const serverOrigin = getDesktopServerUrl();
|
||||
if (!serverOrigin) return false;
|
||||
await invokeLoginCredential<void>("login_credential_delete", { serverOrigin });
|
||||
return true;
|
||||
};
|
||||
|
||||
const getPasswordCredentialConstructor = (): (new (data: { id: string; name?: string; password: string }) => unknown) | null => {
|
||||
const ctor = (window as unknown as { PasswordCredential?: unknown }).PasswordCredential;
|
||||
return typeof ctor === "function"
|
||||
? (ctor as new (data: { id: string; name?: string; password: string }) => unknown)
|
||||
: null;
|
||||
};
|
||||
|
||||
const getBrowserCredentialContainer = ():
|
||||
| {
|
||||
get?: (options: Record<string, unknown>) => Promise<unknown>;
|
||||
store?: (credential: unknown) => Promise<unknown>;
|
||||
preventSilentAccess?: () => Promise<void>;
|
||||
}
|
||||
| null => {
|
||||
const container = (navigator as unknown as { credentials?: unknown }).credentials;
|
||||
return container && typeof container === "object" ? (container as any) : null;
|
||||
};
|
||||
|
||||
const getBrowserSavedLoginCredential = async (): Promise<SavedLoginCredential | null> => {
|
||||
const credentials = getBrowserCredentialContainer();
|
||||
if (!credentials?.get) return null;
|
||||
try {
|
||||
const credential = await credentials.get({
|
||||
password: true,
|
||||
mediation: "optional",
|
||||
});
|
||||
const passwordCredential = credential as { id?: unknown; password?: unknown } | null;
|
||||
if (typeof passwordCredential?.id !== "string" || typeof passwordCredential.password !== "string") {
|
||||
return null;
|
||||
}
|
||||
const email = normalizeEmail(passwordCredential.id);
|
||||
if (!email || !passwordCredential.password) return null;
|
||||
return { email, password: passwordCredential.password };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const saveBrowserLoginCredential = async (credential: SavedLoginCredential): Promise<boolean> => {
|
||||
const credentials = getBrowserCredentialContainer();
|
||||
const PasswordCredential = getPasswordCredentialConstructor();
|
||||
if (!credentials?.store || !PasswordCredential) return false;
|
||||
const email = normalizeEmail(credential.email);
|
||||
if (!email || !credential.password) return false;
|
||||
await credentials.store(
|
||||
new PasswordCredential({
|
||||
id: email,
|
||||
name: email,
|
||||
password: credential.password,
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
};
|
||||
|
||||
const clearBrowserLoginCredential = async (): Promise<boolean> => {
|
||||
const credentials = getBrowserCredentialContainer();
|
||||
if (!credentials?.preventSilentAccess) return false;
|
||||
await credentials.preventSilentAccess();
|
||||
return true;
|
||||
};
|
||||
|
||||
export const getSavedLoginCredential = async (): Promise<SavedLoginCredential | null> => {
|
||||
if (isTauriRuntime()) return getDesktopSavedLoginCredential();
|
||||
return getBrowserSavedLoginCredential();
|
||||
};
|
||||
|
||||
export const saveLoginCredential = async (email: string, password: string): Promise<boolean> => {
|
||||
const credential = { email: normalizeEmail(email), password };
|
||||
if (!credential.email || !credential.password) return false;
|
||||
if (isTauriRuntime()) return saveDesktopLoginCredential(credential);
|
||||
return saveBrowserLoginCredential(credential);
|
||||
};
|
||||
|
||||
export const clearLoginCredential = async (): Promise<boolean> => {
|
||||
if (isTauriRuntime()) return clearDesktopLoginCredential();
|
||||
return clearBrowserLoginCredential();
|
||||
};
|
||||
Reference in New Issue
Block a user