fix(mobile): address PR review comments and lint CI failure
- Exclude packages/mobile/** from root eslint config (mobile uses Expo/CJS toolchain)
- Align TERMINAL_STATUSES and NON_RESTORABLE_STATUSES with packages/core/src/types.ts
- Fix isRestorable() to use isTerminal() check matching core logic
- Change notification trigger from null to { seconds: 1 } for Android background task compat
- Add isPRRateLimited guard to merge button to prevent merge when API rate limited
- Await scheduleNotification in background task to ensure notifications are delivered
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5a22a85e72
commit
f3944b5f15
|
|
@ -15,6 +15,7 @@ export default tseslint.config(
|
|||
"packages/web/postcss.config.mjs",
|
||||
"test-clipboard*.mjs",
|
||||
"test-clipboard*.sh",
|
||||
"packages/mobile/**",
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -47,10 +47,10 @@ TaskManager.defineTask(TASK_ID, async () => {
|
|||
const cooldownExpired = now - lastNotified > COOLDOWN_MS;
|
||||
|
||||
if (level === "respond" && (prev !== "respond" || cooldownExpired)) {
|
||||
scheduleNotification(session, "respond");
|
||||
await scheduleNotification(session, "respond");
|
||||
timestamps[session.id] = now;
|
||||
} else if (level === "merge" && (prev !== "merge" || cooldownExpired)) {
|
||||
scheduleNotification(session, "merge");
|
||||
await scheduleNotification(session, "merge");
|
||||
timestamps[session.id] = now;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ export async function setupNotifications(): Promise<boolean> {
|
|||
}
|
||||
|
||||
/** Fire an immediate local notification for a session attention transition. */
|
||||
export function scheduleNotification(
|
||||
export async function scheduleNotification(
|
||||
session: DashboardSession,
|
||||
level: "respond" | "merge",
|
||||
): void {
|
||||
): Promise<void> {
|
||||
const sessionLabel =
|
||||
session.issueLabel ??
|
||||
session.id;
|
||||
|
|
@ -70,6 +70,9 @@ export function scheduleNotification(
|
|||
...(Platform.OS === "android" && { channelId: ANDROID_CHANNEL_ID }),
|
||||
};
|
||||
|
||||
// Fire immediately (trigger: null = instant)
|
||||
void Notifications.scheduleNotificationAsync({ content, trigger: null });
|
||||
// Use { seconds: 1 } instead of null — trigger: null fails silently on Android in background tasks
|
||||
await Notifications.scheduleNotificationAsync({
|
||||
content,
|
||||
trigger: { seconds: 1, channelId: Platform.OS === "android" ? ANDROID_CHANNEL_ID : undefined },
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
relativeTime,
|
||||
isRestorable,
|
||||
isTerminal,
|
||||
isPRRateLimited,
|
||||
ATTENTION_COLORS,
|
||||
type DashboardCICheck,
|
||||
type DashboardUnresolvedComment,
|
||||
|
|
@ -155,7 +156,7 @@ export default function SessionDetailScreen({ route, navigation }: Props) {
|
|||
const canRestore = isRestorable(session);
|
||||
const isDone = isTerminal(session);
|
||||
const pr = session.pr;
|
||||
const isReadyToMerge = pr?.mergeability.mergeable && pr.state === "open";
|
||||
const isReadyToMerge = pr?.mergeability.mergeable && pr.state === "open" && !isPRRateLimited(pr);
|
||||
const failedChecks = pr?.ciChecks.filter((c) => c.status === "failed") ?? [];
|
||||
const unresolvedComments = pr?.unresolvedComments ?? [];
|
||||
|
||||
|
|
|
|||
|
|
@ -119,26 +119,27 @@ export const ATTENTION_COLORS: Record<AttentionLevel, string> = {
|
|||
done: "#8b949e",
|
||||
};
|
||||
|
||||
/** Terminal states that cannot be restored */
|
||||
const TERMINAL_STATUSES: SessionStatus[] = ["merged", "killed", "cleanup", "done", "terminated"];
|
||||
/** Statuses that indicate the session is in a terminal (dead) state.
|
||||
* Must stay in sync with packages/core/src/types.ts TERMINAL_STATUSES. */
|
||||
const TERMINAL_STATUSES: SessionStatus[] = ["killed", "terminated", "done", "cleanup", "errored", "merged"];
|
||||
const TERMINAL_ACTIVITIES: ActivityState[] = ["exited"];
|
||||
const NON_RESTORABLE_STATUSES: SessionStatus[] = ["merged", "done", "terminated"];
|
||||
|
||||
/** Statuses that must never be restored (e.g. already merged).
|
||||
* Must stay in sync with packages/core/src/types.ts NON_RESTORABLE_STATUSES. */
|
||||
const NON_RESTORABLE_STATUSES: SessionStatus[] = ["merged"];
|
||||
|
||||
export function isTerminal(session: DashboardSession): boolean {
|
||||
return (
|
||||
TERMINAL_STATUSES.includes(session.status) ||
|
||||
TERMINAL_ACTIVITIES.includes(session.activity as ActivityState)
|
||||
(session.activity !== null && TERMINAL_ACTIVITIES.includes(session.activity))
|
||||
);
|
||||
}
|
||||
|
||||
export function isRestorable(session: DashboardSession): boolean {
|
||||
return (
|
||||
session.activity === "exited" &&
|
||||
!NON_RESTORABLE_STATUSES.includes(session.status)
|
||||
);
|
||||
return isTerminal(session) && !NON_RESTORABLE_STATUSES.includes(session.status);
|
||||
}
|
||||
|
||||
function isPRRateLimited(pr: DashboardPR): boolean {
|
||||
export function isPRRateLimited(pr: DashboardPR): boolean {
|
||||
return pr.mergeability.blockers.includes("API rate limited or unavailable");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue