Move production to beenvoice.app with migrated accounts, refreshed auth and timer UX, and expanded invoice flows.
Official URL migration preserves sessions, shortcuts prefs, and last clock-in client; auth screens match web with legal links; time clock and invoice editor/send flows are updated for the new domain and UI patterns. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,16 +13,15 @@ import {
|
||||
authStoragePrefix,
|
||||
buildAccountId,
|
||||
loadAccounts,
|
||||
loadActiveAccountId,
|
||||
loadDraftInstanceUrl,
|
||||
saveAccounts,
|
||||
saveActiveAccountId,
|
||||
saveDraftInstanceUrl,
|
||||
type SavedAccount,
|
||||
} from "@/lib/accounts";
|
||||
import { setRuntimeApiUrl, getApiUrl, DEFAULT_API_URL } from "@/lib/config";
|
||||
import { setRuntimeApiUrl, getApiUrl, DEFAULT_API_URL, invalidServerUrlMessage } from "@/lib/config";
|
||||
import { clearAuthStorage, readStoredSessionUser } from "@/lib/auth-storage";
|
||||
import { normalizeInstanceUrl, saveStoredInstanceUrl } from "@/lib/instance-url";
|
||||
import { migrateStoredOfficialUrls } from "@/lib/official-url-migration";
|
||||
import { clearTimeClockPrefsForAccount } from "@/lib/time-clock-prefs";
|
||||
|
||||
export type RemoveAccountResult = {
|
||||
@@ -58,8 +57,8 @@ export function AccountsProvider({ children }: { children: ReactNode }) {
|
||||
const [apiUrl, setApiUrl] = useState(getApiUrl);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([loadAccounts(), loadActiveAccountId(), loadDraftInstanceUrl()])
|
||||
.then(([storedAccounts, activeId, draftUrl]) => {
|
||||
migrateStoredOfficialUrls()
|
||||
.then(({ accounts: storedAccounts, activeAccountId: activeId, draftUrl }) => {
|
||||
setAccounts(storedAccounts);
|
||||
|
||||
const active = storedAccounts.find((account) => account.id === activeId) ?? null;
|
||||
@@ -86,7 +85,7 @@ export function AccountsProvider({ children }: { children: ReactNode }) {
|
||||
async (url: string) => {
|
||||
const normalized = normalizeInstanceUrl(url);
|
||||
if (!normalized) {
|
||||
throw new Error("Enter a valid server URL (e.g. beenvoice.app or localhost:3000)");
|
||||
throw new Error(invalidServerUrlMessage());
|
||||
}
|
||||
|
||||
if (activeAccount) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
setBiometricEnabled,
|
||||
setStoredPin,
|
||||
} from "@/lib/app-lock";
|
||||
import { hasPendingShortcut } from "@/lib/shortcut-queue";
|
||||
import { hasPendingShortcut, subscribeShortcutQueue } from "@/lib/shortcut-queue";
|
||||
|
||||
type AppLockContextValue = {
|
||||
enabled: boolean;
|
||||
@@ -120,13 +120,29 @@ export function AppLockProvider({ children }: { children: ReactNode }) {
|
||||
!biometricUnlockInProgress.current
|
||||
) {
|
||||
wasBackgrounded.current = false;
|
||||
setIsLocked(true);
|
||||
void hasPendingShortcut().then((shortcutPending) => {
|
||||
if (!shortcutPending) {
|
||||
setIsLocked(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => subscription.remove();
|
||||
}, [enabled, activeAccountId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
|
||||
return subscribeShortcutQueue(() => {
|
||||
void hasPendingShortcut().then((shortcutPending) => {
|
||||
if (shortcutPending) {
|
||||
setIsLocked(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [enabled]);
|
||||
|
||||
const unlockWithPin = useCallback(
|
||||
async (pin: string) => {
|
||||
if (!activeAccountId) return false;
|
||||
|
||||
Reference in New Issue
Block a user