Maximus upgradus
CodexBar Mobile Build / build-android (push) Failing after 1m38s
CodexBar Mobile Build / release (push) Has been skipped

This commit is contained in:
2026-06-24 20:16:34 +02:00
parent d5b0f9c833
commit ba7d957155
20 changed files with 1192 additions and 642 deletions
+42 -33
View File
@@ -4,15 +4,18 @@ import {
loadClaudeSessionKey,
saveClaudeSessionKey,
clearClaudeSessionKey,
loadClaudeLastActiveOrg,
saveClaudeLastActiveOrg,
clearClaudeLastActiveOrg,
} from "@/lib/storage";
import type { ClaudeUsageResponse } from "@/types/claude";
import type { ClaudeAuth, ClaudeUsageResponse } from "@/types/claude";
type Status = "idle" | "loading" | "success" | "error";
async function doFetch(
sessionKey: string,
auth: ClaudeAuth,
set: {
sessionKey: (v: string | null) => void;
auth: (v: ClaudeAuth | null) => void;
usage: (v: ClaudeUsageResponse | null) => void;
status: (v: Status) => void;
error: (v: string | null) => void;
@@ -21,10 +24,13 @@ async function doFetch(
set.status("loading");
set.error(null);
try {
const orgs = await fetchClaudeOrgs(sessionKey);
if (!orgs.length) throw new Error("NO_ORGS_FOUND");
const orgUuid = orgs[0].uuid;
const data = await fetchClaudeUsage(sessionKey, orgUuid);
let orgUuid = auth.lastActiveOrg;
if (!orgUuid) {
const orgs = await fetchClaudeOrgs(auth);
if (!orgs.length) throw new Error("NO_ORGS_FOUND");
orgUuid = orgs[0].uuid;
}
const data = await fetchClaudeUsage(auth, orgUuid);
set.usage(data);
set.status("success");
} catch (e: unknown) {
@@ -33,29 +39,32 @@ async function doFetch(
set.status("error");
if (msg === "TOKEN_EXPIRED") {
await clearClaudeSessionKey();
set.sessionKey(null);
set.auth(null);
}
}
}
export function useClaudeUsage() {
const [sessionKey, setSessionKey] = useState<string | null>(null);
const [auth, setAuth] = useState<ClaudeAuth | null>(null);
const [pendingKey, setPendingKey] = useState("");
const [pendingOrg, setPendingOrg] = useState("");
const [usage, setUsage] = useState<ClaudeUsageResponse | null>(null);
const [status, setStatus] = useState<Status>("idle");
const [error, setError] = useState<string | null>(null);
const setters = { sessionKey: setSessionKey, usage: setUsage, status: setStatus, error: setError };
const setters = { auth: setAuth, usage: setUsage, status: setStatus, error: setError };
useEffect(() => {
loadClaudeSessionKey().then((stored) => {
if (stored) setSessionKey(stored);
});
Promise.all([loadClaudeSessionKey(), loadClaudeLastActiveOrg()]).then(
([key, org]) => {
if (key) setAuth({ sessionKey: key, lastActiveOrg: org ?? undefined });
}
);
}, []);
useEffect(() => {
if (sessionKey) void doFetch(sessionKey, setters);
}, [sessionKey]);
if (auth) void doFetch(auth, setters);
}, [auth]);
const saveKey = useCallback(async () => {
const trimmed = pendingKey.trim();
@@ -63,47 +72,47 @@ export function useClaudeUsage() {
setError("Session key must start with sk-ant-");
return;
}
const orgTrimmed = pendingOrg.trim() || undefined;
await saveClaudeSessionKey(trimmed);
setSessionKey(trimmed);
if (orgTrimmed) await saveClaudeLastActiveOrg(orgTrimmed);
else await clearClaudeLastActiveOrg();
setAuth({ sessionKey: trimmed, lastActiveOrg: orgTrimmed });
setPendingKey("");
setPendingOrg("");
setError(null);
}, [pendingKey]);
}, [pendingKey, pendingOrg]);
const refresh = useCallback(async () => {
if (!sessionKey) return;
await doFetch(sessionKey, setters);
}, [sessionKey]);
if (!auth) return;
await doFetch(auth, setters);
}, [auth]);
// Re-reads the session key from storage and fetches fresh data. Call from
// useFocusEffect so the dashboard stays current when the key is saved from
// another tab. If the string value is unchanged, React bails out on setState
// so we trigger the fetch directly.
const reload = useCallback(async () => {
const stored = await loadClaudeSessionKey();
if (!stored) {
setSessionKey(null);
const [key, org] = await Promise.all([loadClaudeSessionKey(), loadClaudeLastActiveOrg()]);
if (!key) {
setAuth(null);
setUsage(null);
setStatus("idle");
return;
}
// If already set to the same string, setState bails out and [sessionKey]
// effect won't fire, so call the fetch directly.
setSessionKey(stored);
void doFetch(stored, setters);
// Creating a new object always triggers the [auth] useEffect even if values are identical
setAuth({ sessionKey: key, lastActiveOrg: org ?? undefined });
}, []);
const clearKey = useCallback(async () => {
await clearClaudeSessionKey();
setSessionKey(null);
setAuth(null);
setUsage(null);
setStatus("idle");
setError(null);
}, []);
return {
sessionKey,
auth,
pendingKey,
setPendingKey,
pendingOrg,
setPendingOrg,
usage,
status,
error,
+64
View File
@@ -0,0 +1,64 @@
import { useState, useEffect, useCallback } from "react";
import {
loadNotifSettings,
saveNotifSettings,
type NotifSettings,
} from "@/lib/storage";
import {
requestPermissions,
getPermissionStatus,
scheduleDailyDigest,
cancelDailyDigest,
} from "@/lib/notifications";
export function useNotificationSettings() {
const [settings, setSettings] = useState<NotifSettings>({
dailyEnabled: false,
dailyHour: 9,
dailyMinute: 0,
thresholdEnabled: false,
thresholdPct: 20,
});
const [permissionStatus, setPermissionStatus] = useState("undetermined");
const [loaded, setLoaded] = useState(false);
useEffect(() => {
Promise.all([loadNotifSettings(), getPermissionStatus()]).then(
([stored, status]) => {
setSettings(stored);
setPermissionStatus(status);
setLoaded(true);
}
);
}, []);
const askPermissions = useCallback(async (): Promise<boolean> => {
const granted = await requestPermissions();
setPermissionStatus(granted ? "granted" : "denied");
return granted;
}, []);
const update = useCallback(
async (patch: Partial<NotifSettings>) => {
const next = { ...settings, ...patch };
setSettings(next);
await saveNotifSettings(next);
// Keep scheduled notification in sync when toggling or changing time
const dailyChanged =
"dailyEnabled" in patch ||
"dailyHour" in patch ||
"dailyMinute" in patch;
if (dailyChanged) {
if (next.dailyEnabled) {
await scheduleDailyDigest(next.dailyHour, next.dailyMinute, null, null);
} else {
await cancelDailyDigest();
}
}
},
[settings]
);
return { settings, permissionStatus, loaded, askPermissions, update };
}