Redesign mobile time clock, add shortcuts, and improve account management.
Add iOS Shortcuts/Siri intents, local send-reminder notifications, stable client picker with last-client defaults, account refresh/remove, and softer session handling on unauthorized API responses. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,7 +21,14 @@ import {
|
||||
type SavedAccount,
|
||||
} from "@/lib/accounts";
|
||||
import { setRuntimeApiUrl, getApiUrl, DEFAULT_API_URL } from "@/lib/config";
|
||||
import { clearAuthStorage, readStoredSessionUser } from "@/lib/auth-storage";
|
||||
import { normalizeInstanceUrl, saveStoredInstanceUrl } from "@/lib/instance-url";
|
||||
import { clearTimeClockPrefsForAccount } from "@/lib/time-clock-prefs";
|
||||
|
||||
export type RemoveAccountResult = {
|
||||
wasActive: boolean;
|
||||
remainingCount: number;
|
||||
};
|
||||
|
||||
type AccountsContextValue = {
|
||||
accounts: SavedAccount[];
|
||||
@@ -37,7 +44,8 @@ type AccountsContextValue = {
|
||||
email: string;
|
||||
name: string;
|
||||
}) => Promise<SavedAccount>;
|
||||
removeAccount: (accountId: string) => Promise<void>;
|
||||
removeAccount: (accountId: string) => Promise<RemoveAccountResult>;
|
||||
refreshAccounts: () => Promise<void>;
|
||||
clearActiveAccount: () => Promise<void>;
|
||||
};
|
||||
|
||||
@@ -148,12 +156,17 @@ export function AccountsProvider({ children }: { children: ReactNode }) {
|
||||
);
|
||||
|
||||
const removeAccount = useCallback(
|
||||
async (accountId: string) => {
|
||||
async (accountId: string): Promise<RemoveAccountResult> => {
|
||||
const wasActive = activeAccountId === accountId;
|
||||
|
||||
await clearAuthStorage(authStoragePrefix(accountId));
|
||||
await clearTimeClockPrefsForAccount(accountId);
|
||||
|
||||
const nextAccounts = accounts.filter((account) => account.id !== accountId);
|
||||
setAccounts(nextAccounts);
|
||||
await saveAccounts(nextAccounts);
|
||||
|
||||
if (activeAccountId === accountId) {
|
||||
if (wasActive) {
|
||||
const fallback = nextAccounts[0] ?? null;
|
||||
await saveActiveAccountId(fallback?.id ?? null);
|
||||
setActiveAccountId(fallback?.id ?? null);
|
||||
@@ -162,10 +175,30 @@ export function AccountsProvider({ children }: { children: ReactNode }) {
|
||||
setApiUrl(fallback.instanceUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return { wasActive, remainingCount: nextAccounts.length };
|
||||
},
|
||||
[accounts, activeAccountId],
|
||||
);
|
||||
|
||||
const refreshAccounts = useCallback(async () => {
|
||||
const stored = await loadAccounts();
|
||||
const refreshed = await Promise.all(
|
||||
stored.map(async (account) => {
|
||||
const user = await readStoredSessionUser(authStoragePrefix(account.id));
|
||||
if (!user?.name && !user?.email) return account;
|
||||
return {
|
||||
...account,
|
||||
name: user.name?.trim() || account.name,
|
||||
email: user.email?.trim() || account.email,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
setAccounts(refreshed);
|
||||
await saveAccounts(refreshed);
|
||||
}, []);
|
||||
|
||||
const clearActiveAccount = useCallback(async () => {
|
||||
await saveActiveAccountId(null);
|
||||
setActiveAccountId(null);
|
||||
@@ -184,6 +217,7 @@ export function AccountsProvider({ children }: { children: ReactNode }) {
|
||||
switchAccount,
|
||||
registerAccount,
|
||||
removeAccount,
|
||||
refreshAccounts,
|
||||
clearActiveAccount,
|
||||
}),
|
||||
[
|
||||
@@ -195,6 +229,7 @@ export function AccountsProvider({ children }: { children: ReactNode }) {
|
||||
switchAccount,
|
||||
registerAccount,
|
||||
removeAccount,
|
||||
refreshAccounts,
|
||||
clearActiveAccount,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -15,11 +15,13 @@ function createAppAuthClient(apiUrl: string, storagePrefix: string): AuthClient
|
||||
return createAuthClient({
|
||||
baseURL: apiUrl,
|
||||
plugins: [
|
||||
expoClient({
|
||||
scheme: "beenvoice",
|
||||
storagePrefix,
|
||||
storage: SecureStore,
|
||||
}),
|
||||
expoClient({
|
||||
scheme: "beenvoice",
|
||||
storagePrefix,
|
||||
storage: SecureStore,
|
||||
// Avoid showing a cached session when cookies have already expired.
|
||||
disableCache: true,
|
||||
}),
|
||||
genericOAuthClient(),
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user