mirror of
https://gitee.com/wanwujie/sub2api-mobile
synced 2026-04-20 23:04:46 +08:00
feat: refine expo admin mobile flows
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import * as SecureStore from 'expo-secure-store';
|
||||
import { Platform } from 'react-native';
|
||||
const { proxy } = require('valtio');
|
||||
|
||||
const BASE_URL_KEY = 'sub2api_base_url';
|
||||
@@ -67,39 +68,51 @@ export function getDefaultAdminConfig() {
|
||||
}
|
||||
|
||||
async function getItem(key: string) {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
try {
|
||||
if (Platform.OS === 'web') {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
return localStorage.getItem(key);
|
||||
return await SecureStore.getItemAsync(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
return SecureStore.getItemAsync(key);
|
||||
}
|
||||
|
||||
async function setItem(key: string, value: string) {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem(key, value);
|
||||
try {
|
||||
if (Platform.OS === 'web') {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await SecureStore.setItemAsync(key, value);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
await SecureStore.setItemAsync(key, value);
|
||||
}
|
||||
|
||||
async function deleteItem(key: string) {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem(key);
|
||||
try {
|
||||
if (Platform.OS === 'web') {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await SecureStore.deleteItemAsync(key);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
await SecureStore.deleteItemAsync(key);
|
||||
}
|
||||
|
||||
export const adminConfigState = proxy({
|
||||
@@ -112,98 +125,104 @@ export const adminConfigState = proxy({
|
||||
|
||||
export async function hydrateAdminConfig() {
|
||||
const defaults = getDefaultAdminConfig();
|
||||
const [baseUrl, adminApiKey, rawAccounts, activeAccountId] = await Promise.all([
|
||||
getItem(BASE_URL_KEY),
|
||||
getItem(ADMIN_KEY_KEY),
|
||||
getItem(ACCOUNTS_KEY),
|
||||
getItem(ACTIVE_ACCOUNT_ID_KEY),
|
||||
]);
|
||||
|
||||
let accounts: AdminAccountProfile[] = [];
|
||||
try {
|
||||
const [baseUrl, adminApiKey, rawAccounts, activeAccountId] = await Promise.all([
|
||||
getItem(BASE_URL_KEY),
|
||||
getItem(ADMIN_KEY_KEY),
|
||||
getItem(ACCOUNTS_KEY),
|
||||
getItem(ACTIVE_ACCOUNT_ID_KEY),
|
||||
]);
|
||||
|
||||
if (rawAccounts) {
|
||||
try {
|
||||
const parsed = JSON.parse(rawAccounts) as AdminAccountProfile[];
|
||||
accounts = Array.isArray(parsed) ? parsed.map((account) => normalizeAccount(account)) : [];
|
||||
} catch {
|
||||
accounts = [];
|
||||
let accounts: AdminAccountProfile[] = [];
|
||||
|
||||
if (rawAccounts) {
|
||||
try {
|
||||
const parsed = JSON.parse(rawAccounts) as AdminAccountProfile[];
|
||||
accounts = Array.isArray(parsed) ? parsed.map((account) => normalizeAccount(account)) : [];
|
||||
} catch {
|
||||
accounts = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (accounts.length === 0 && baseUrl) {
|
||||
const legacyConfig = normalizeConfig({
|
||||
baseUrl,
|
||||
adminApiKey: adminApiKey ?? defaults.adminApiKey,
|
||||
});
|
||||
|
||||
accounts = [
|
||||
{
|
||||
id: createAccountId(),
|
||||
label: getAccountLabel(legacyConfig.baseUrl),
|
||||
...legacyConfig,
|
||||
updatedAt: new Date().toISOString(),
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const sortedAccounts = sortAccounts(accounts);
|
||||
const activeAccount = getNextActiveAccount(sortedAccounts, activeAccountId ?? undefined);
|
||||
const nextActiveAccountId = activeAccount?.id || '';
|
||||
|
||||
adminConfigState.accounts = sortedAccounts;
|
||||
adminConfigState.activeAccountId = nextActiveAccountId;
|
||||
adminConfigState.baseUrl = activeAccount?.baseUrl ?? defaults.baseUrl;
|
||||
adminConfigState.adminApiKey = activeAccount?.adminApiKey ?? defaults.adminApiKey;
|
||||
|
||||
await Promise.all([
|
||||
setItem(ACCOUNTS_KEY, JSON.stringify(sortedAccounts)),
|
||||
nextActiveAccountId ? setItem(ACTIVE_ACCOUNT_ID_KEY, nextActiveAccountId) : deleteItem(ACTIVE_ACCOUNT_ID_KEY),
|
||||
setItem(BASE_URL_KEY, activeAccount?.baseUrl ?? defaults.baseUrl),
|
||||
setItem(ADMIN_KEY_KEY, activeAccount?.adminApiKey ?? defaults.adminApiKey),
|
||||
]);
|
||||
} finally {
|
||||
adminConfigState.hydrated = true;
|
||||
}
|
||||
|
||||
if (accounts.length === 0 && baseUrl) {
|
||||
const legacyConfig = normalizeConfig({
|
||||
baseUrl,
|
||||
adminApiKey: adminApiKey ?? defaults.adminApiKey,
|
||||
});
|
||||
|
||||
accounts = [
|
||||
{
|
||||
id: createAccountId(),
|
||||
label: getAccountLabel(legacyConfig.baseUrl),
|
||||
...legacyConfig,
|
||||
updatedAt: new Date().toISOString(),
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const sortedAccounts = sortAccounts(accounts);
|
||||
const activeAccount = getNextActiveAccount(sortedAccounts, activeAccountId ?? undefined);
|
||||
const nextActiveAccountId = activeAccount?.id || '';
|
||||
|
||||
adminConfigState.accounts = sortedAccounts;
|
||||
adminConfigState.activeAccountId = nextActiveAccountId;
|
||||
adminConfigState.baseUrl = activeAccount?.baseUrl ?? defaults.baseUrl;
|
||||
adminConfigState.adminApiKey = activeAccount?.adminApiKey ?? defaults.adminApiKey;
|
||||
|
||||
adminConfigState.hydrated = true;
|
||||
|
||||
await Promise.all([
|
||||
setItem(ACCOUNTS_KEY, JSON.stringify(sortedAccounts)),
|
||||
nextActiveAccountId ? setItem(ACTIVE_ACCOUNT_ID_KEY, nextActiveAccountId) : deleteItem(ACTIVE_ACCOUNT_ID_KEY),
|
||||
setItem(BASE_URL_KEY, activeAccount?.baseUrl ?? defaults.baseUrl),
|
||||
setItem(ADMIN_KEY_KEY, activeAccount?.adminApiKey ?? defaults.adminApiKey),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function saveAdminConfig(input: { baseUrl: string; adminApiKey: string }) {
|
||||
adminConfigState.saving = true;
|
||||
|
||||
const normalized = normalizeConfig(input);
|
||||
const nextUpdatedAt = new Date().toISOString();
|
||||
const existingAccount = adminConfigState.accounts.find(
|
||||
(account: AdminAccountProfile) => account.baseUrl === normalized.baseUrl && account.adminApiKey === normalized.adminApiKey
|
||||
);
|
||||
const nextAccount: AdminAccountProfile = existingAccount
|
||||
? {
|
||||
...existingAccount,
|
||||
label: getAccountLabel(normalized.baseUrl),
|
||||
updatedAt: nextUpdatedAt,
|
||||
}
|
||||
: {
|
||||
id: createAccountId(),
|
||||
label: getAccountLabel(normalized.baseUrl),
|
||||
...normalized,
|
||||
updatedAt: nextUpdatedAt,
|
||||
enabled: true,
|
||||
};
|
||||
const nextAccounts = sortAccounts([
|
||||
nextAccount,
|
||||
...adminConfigState.accounts.filter((account: AdminAccountProfile) => account.id !== nextAccount.id),
|
||||
]);
|
||||
try {
|
||||
const normalized = normalizeConfig(input);
|
||||
const nextUpdatedAt = new Date().toISOString();
|
||||
const existingAccount = adminConfigState.accounts.find(
|
||||
(account: AdminAccountProfile) => account.baseUrl === normalized.baseUrl && account.adminApiKey === normalized.adminApiKey
|
||||
);
|
||||
const nextAccount: AdminAccountProfile = existingAccount
|
||||
? {
|
||||
...existingAccount,
|
||||
label: getAccountLabel(normalized.baseUrl),
|
||||
updatedAt: nextUpdatedAt,
|
||||
}
|
||||
: {
|
||||
id: createAccountId(),
|
||||
label: getAccountLabel(normalized.baseUrl),
|
||||
...normalized,
|
||||
updatedAt: nextUpdatedAt,
|
||||
enabled: true,
|
||||
};
|
||||
const nextAccounts = sortAccounts([
|
||||
nextAccount,
|
||||
...adminConfigState.accounts.filter((account: AdminAccountProfile) => account.id !== nextAccount.id),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
setItem(BASE_URL_KEY, normalized.baseUrl),
|
||||
setItem(ADMIN_KEY_KEY, normalized.adminApiKey),
|
||||
setItem(ACCOUNTS_KEY, JSON.stringify(nextAccounts)),
|
||||
setItem(ACTIVE_ACCOUNT_ID_KEY, nextAccount.id),
|
||||
]);
|
||||
await Promise.all([
|
||||
setItem(BASE_URL_KEY, normalized.baseUrl),
|
||||
setItem(ADMIN_KEY_KEY, normalized.adminApiKey),
|
||||
setItem(ACCOUNTS_KEY, JSON.stringify(nextAccounts)),
|
||||
setItem(ACTIVE_ACCOUNT_ID_KEY, nextAccount.id),
|
||||
]);
|
||||
|
||||
adminConfigState.accounts = nextAccounts;
|
||||
adminConfigState.activeAccountId = nextAccount.id;
|
||||
adminConfigState.baseUrl = normalized.baseUrl;
|
||||
adminConfigState.adminApiKey = normalized.adminApiKey;
|
||||
adminConfigState.saving = false;
|
||||
adminConfigState.accounts = nextAccounts;
|
||||
adminConfigState.activeAccountId = nextAccount.id;
|
||||
adminConfigState.baseUrl = normalized.baseUrl;
|
||||
adminConfigState.adminApiKey = normalized.adminApiKey;
|
||||
} finally {
|
||||
adminConfigState.saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function switchAdminAccount(accountId: string) {
|
||||
|
||||
Reference in New Issue
Block a user