feat(assets): add chat typing indicator lottie animation
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* UserStorage 完整实现(KV / localStorage)
|
||||
*
|
||||
* 对齐 Dart 端 `UserStorage`(lib/data/services/storage/user_storage.dart):
|
||||
* - 三个独立字段:User 对象 / userId / avatarUrl
|
||||
* - `clearUserData()` 批量清空
|
||||
*
|
||||
* 设计沿用 AuthStorage 模式:组合 `LocalStorage`(非继承)+ 单例 + Zod 校验。
|
||||
*
|
||||
* 边界:
|
||||
* - `getUser` 走 `UserSchema` 解析:缺字段时 Zod `.default()` 自动兜底
|
||||
* - `setUser` 走 `UserSchema` 校验写入:保证存储的总是完整 UserData
|
||||
* - 单例挂在 class 静态字段,HMR 复用安全
|
||||
*/
|
||||
|
||||
import { UserSchema, type UserData } from "@/data/models";
|
||||
import { LocalStorage } from "../local_storage";
|
||||
import { type Result as ResultT } from "../result";
|
||||
import { StorageKeys } from "../storage_keys";
|
||||
import type { IUserStorage } from "./iuser_storage";
|
||||
|
||||
export class UserStorage implements IUserStorage {
|
||||
private readonly ls: LocalStorage;
|
||||
private static _instance: UserStorage | null = null;
|
||||
|
||||
constructor(localStorage?: LocalStorage) {
|
||||
this.ls = localStorage ?? LocalStorage.getInstance();
|
||||
}
|
||||
|
||||
static getInstance(): UserStorage {
|
||||
if (!UserStorage._instance) UserStorage._instance = new UserStorage();
|
||||
return UserStorage._instance;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
static _resetForTests(): void {
|
||||
UserStorage._instance = null;
|
||||
}
|
||||
|
||||
// ---- user object ----
|
||||
|
||||
getUser(): Promise<ResultT<UserData | null>> {
|
||||
return this.ls.getJson(StorageKeys.user, UserSchema);
|
||||
}
|
||||
|
||||
setUser(user: UserData): Promise<ResultT<void>> {
|
||||
return this.ls.setJson(StorageKeys.user, user, UserSchema);
|
||||
}
|
||||
|
||||
clearUser(): Promise<ResultT<void>> {
|
||||
return this.ls.remove(StorageKeys.user);
|
||||
}
|
||||
|
||||
// ---- userId ----
|
||||
|
||||
getUserId(): Promise<ResultT<string | null>> {
|
||||
return this.ls.getString(StorageKeys.userId);
|
||||
}
|
||||
|
||||
setUserId(id: string): Promise<ResultT<void>> {
|
||||
return this.ls.setString(StorageKeys.userId, id);
|
||||
}
|
||||
|
||||
clearUserId(): Promise<ResultT<void>> {
|
||||
return this.ls.remove(StorageKeys.userId);
|
||||
}
|
||||
|
||||
// ---- avatarUrl ----
|
||||
|
||||
getAvatarUrl(): Promise<ResultT<string | null>> {
|
||||
return this.ls.getString(StorageKeys.userAvatar);
|
||||
}
|
||||
|
||||
setAvatarUrl(url: string): Promise<ResultT<void>> {
|
||||
return this.ls.setString(StorageKeys.userAvatar, url);
|
||||
}
|
||||
|
||||
clearAvatarUrl(): Promise<ResultT<void>> {
|
||||
return this.ls.remove(StorageKeys.userAvatar);
|
||||
}
|
||||
|
||||
// ---- bulk clear ----
|
||||
|
||||
async clearUserData(): Promise<ResultT<void>> {
|
||||
const r1 = await this.clearUser();
|
||||
if (r1.kind === "failure") return r1;
|
||||
const r2 = await this.clearUserId();
|
||||
if (r2.kind === "failure") return r2;
|
||||
return this.clearAvatarUrl();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user