Files
riwa-ionic/src/api/index.ts

141 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { App } from "@riwa/api-types";
import type { WatchSource } from "vue";
import { treaty } from "@elysiajs/eden";
import { toastController } from "@ionic/vue";
import { i18n } from "@/locales";
import { useMocks } from "@/mocks";
const baseURL = import.meta.env.DEV ? window.location.origin : import.meta.env.VITE_API_URL;
const client = treaty<App>(baseURL, {
fetch: {
credentials: "include",
},
headers() {
const token = localStorage.getItem("user-token") || "";
const headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", token ? `Bearer ${token}` : "");
return headers;
},
});
export interface SafeClientOptions {
silent?: boolean;
immediate?: boolean;
watchSource?: WatchSource; // 用于监听的响应式数据源
}
export interface SafeClientReturn<T, E> {
data: Ref<T | null>;
error: Ref<E | null>;
isPending: Ref<boolean>;
execute: () => Promise<void>;
onFetchResponse: (callback: (data: T, error: E) => void) => void;
stopWatching?: () => void;
}
export type RequestPromise<T, E> = Promise<{ data: T; error: E; status?: number; response?: Response }>;
export function safeClient<T, E>(
requestPromise: (() => RequestPromise<T, E>) | RequestPromise<T, E>,
options: SafeClientOptions = {},
): SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>> {
const { immediate = true, watchSource } = options;
const data = ref<T | null>(null);
const error = ref<E | null>(null);
const isPending = ref(false);
let responseCallback: ((data: T, error: E) => void) | null = null;
let stopWatcher: (() => void) | undefined;
const execute = async () => {
isPending.value = true;
let request: () => RequestPromise<T, E>;
if (typeof requestPromise !== "function") {
request = () => Promise.resolve(requestPromise);
}
else {
request = requestPromise;
}
const res = await request().finally(() => {
isPending.value = false;
});
if (res.error && res.status === 418) {
if (!options.silent) {
const toast = await toastController.create({
message: i18n.global.t((res.error as any).value.code, {
...(res.error as any).value.context,
}),
duration: 3000,
position: "bottom",
color: "danger",
});
await toast.present();
}
throw res.error;
}
data.value = res.data;
error.value = res.error;
// 调用注册的回调函数
if (responseCallback) {
responseCallback(res.data, res.error);
}
};
function onFetchResponse(callback: (data: T, error: E) => void) {
responseCallback = callback;
}
function stopWatching() {
if (stopWatcher) {
stopWatcher();
stopWatcher = undefined;
}
}
// 如果提供了 watchSource则监听其变化
if (watchSource) {
stopWatcher = watch(
watchSource,
() => {
execute();
},
{ immediate: false }, // 不立即执行,避免与 immediate 选项冲突
);
}
const result: SafeClientReturn<T, E> = {
data: data as Ref<T | null>,
error: error as Ref<E | null>,
isPending,
execute,
onFetchResponse,
stopWatching,
};
const promise = immediate ? execute().then(() => result) : Promise.resolve(result);
Object.assign(promise, result);
return promise as SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>>;
}
export function mockClient<T = any, E = any>(key: string): SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>> {
const requestPromise = async () => {
const mocks = useMocks();
const mockFn = mocks.get(key);
if (!mockFn) {
throw new Error(`Mock with key "${key}" not found.`);
}
const data = await mockFn();
return { data: data as T, error: null as E };
};
return safeClient<T, E>(requestPromise, { immediate: true });
}
export { client };