Fix desktop badge workspace parity
This commit is contained in:
@@ -1,8 +1,14 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
|
import { useShallow } from "zustand/shallow";
|
||||||
import { getIsElectronRuntimeMac } from "@/constants/layout";
|
import { getIsElectronRuntimeMac } from "@/constants/layout";
|
||||||
import { useAggregatedAgents } from "./use-aggregated-agents";
|
import { useAggregatedAgents } from "./use-aggregated-agents";
|
||||||
import { getDesktopHost } from "@/desktop/host";
|
import { getDesktopHost } from "@/desktop/host";
|
||||||
|
import { useSessionStore } from "@/stores/session-store";
|
||||||
|
import {
|
||||||
|
deriveMacDockBadgeCountFromWorkspaceStatuses,
|
||||||
|
type DesktopBadgeWorkspaceStatus,
|
||||||
|
} from "@/utils/desktop-badge-state";
|
||||||
|
|
||||||
type FaviconStatus = "none" | "running" | "attention";
|
type FaviconStatus = "none" | "running" | "attention";
|
||||||
type ColorScheme = "dark" | "light";
|
type ColorScheme = "dark" | "light";
|
||||||
@@ -37,21 +43,6 @@ function deriveFaviconStatus(
|
|||||||
return "none";
|
return "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function deriveMacDockBadgeCount(
|
|
||||||
agents: ReturnType<typeof useAggregatedAgents>["agents"],
|
|
||||||
): number | undefined {
|
|
||||||
const attentionCount = agents.filter(
|
|
||||||
(agent) =>
|
|
||||||
(agent.pendingPermissionCount ?? 0) > 0 ||
|
|
||||||
(agent.requiresAttention && agent.attentionReason === "finished"),
|
|
||||||
).length;
|
|
||||||
if (attentionCount > 0) {
|
|
||||||
return attentionCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFaviconUri(status: FaviconStatus, colorScheme: ColorScheme): string {
|
function getFaviconUri(status: FaviconStatus, colorScheme: ColorScheme): string {
|
||||||
const image = FAVICON_IMAGES[colorScheme][status];
|
const image = FAVICON_IMAGES[colorScheme][status];
|
||||||
if (typeof image === "object" && "uri" in image) {
|
if (typeof image === "object" && "uri" in image) {
|
||||||
@@ -112,6 +103,17 @@ async function updateMacDockBadge(count?: number) {
|
|||||||
|
|
||||||
export function useFaviconStatus() {
|
export function useFaviconStatus() {
|
||||||
const { agents } = useAggregatedAgents();
|
const { agents } = useAggregatedAgents();
|
||||||
|
const workspaceStatuses = useSessionStore(
|
||||||
|
useShallow((state) => {
|
||||||
|
const statuses: DesktopBadgeWorkspaceStatus[] = [];
|
||||||
|
for (const session of Object.values(state.sessions)) {
|
||||||
|
for (const workspace of session.workspaces.values()) {
|
||||||
|
statuses.push(workspace.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statuses;
|
||||||
|
}),
|
||||||
|
);
|
||||||
const [colorScheme, setColorScheme] = useState<ColorScheme>(getSystemColorScheme);
|
const [colorScheme, setColorScheme] = useState<ColorScheme>(getSystemColorScheme);
|
||||||
const lastDockBadgeCountRef = useRef<number | undefined>(undefined);
|
const lastDockBadgeCountRef = useRef<number | undefined>(undefined);
|
||||||
|
|
||||||
@@ -135,10 +137,10 @@ export function useFaviconStatus() {
|
|||||||
const status = deriveFaviconStatus(agents);
|
const status = deriveFaviconStatus(agents);
|
||||||
updateFavicon(status, colorScheme);
|
updateFavicon(status, colorScheme);
|
||||||
|
|
||||||
const dockBadgeCount = deriveMacDockBadgeCount(agents);
|
const dockBadgeCount = deriveMacDockBadgeCountFromWorkspaceStatuses(workspaceStatuses);
|
||||||
if (dockBadgeCount !== lastDockBadgeCountRef.current) {
|
if (dockBadgeCount !== lastDockBadgeCountRef.current) {
|
||||||
lastDockBadgeCountRef.current = dockBadgeCount;
|
lastDockBadgeCountRef.current = dockBadgeCount;
|
||||||
void updateMacDockBadge(dockBadgeCount);
|
void updateMacDockBadge(dockBadgeCount);
|
||||||
}
|
}
|
||||||
}, [agents, colorScheme]);
|
}, [agents, colorScheme, workspaceStatuses]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
deriveMacDockBadgeCountFromWorkspaceStatuses,
|
||||||
|
isWorkspaceActionableForDesktopBadge,
|
||||||
|
} from "./desktop-badge-state";
|
||||||
|
|
||||||
|
describe("desktop-badge-state", () => {
|
||||||
|
it("treats attention-requiring workspace statuses as actionable", () => {
|
||||||
|
expect(isWorkspaceActionableForDesktopBadge("attention")).toBe(true);
|
||||||
|
expect(isWorkspaceActionableForDesktopBadge("needs_input")).toBe(true);
|
||||||
|
expect(isWorkspaceActionableForDesktopBadge("failed")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores running and done workspace statuses", () => {
|
||||||
|
expect(isWorkspaceActionableForDesktopBadge("running")).toBe(false);
|
||||||
|
expect(isWorkspaceActionableForDesktopBadge("done")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns undefined when no visible workspaces need attention", () => {
|
||||||
|
expect(deriveMacDockBadgeCountFromWorkspaceStatuses(["done", "running"])).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("counts only actionable visible workspaces", () => {
|
||||||
|
expect(
|
||||||
|
deriveMacDockBadgeCountFromWorkspaceStatuses([
|
||||||
|
"done",
|
||||||
|
"attention",
|
||||||
|
"running",
|
||||||
|
"needs_input",
|
||||||
|
"failed",
|
||||||
|
]),
|
||||||
|
).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
||||||
|
|
||||||
|
export type DesktopBadgeWorkspaceStatus = WorkspaceDescriptor["status"];
|
||||||
|
|
||||||
|
export function isWorkspaceActionableForDesktopBadge(
|
||||||
|
status: DesktopBadgeWorkspaceStatus,
|
||||||
|
): boolean {
|
||||||
|
return status === "attention" || status === "needs_input" || status === "failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deriveMacDockBadgeCountFromWorkspaceStatuses(
|
||||||
|
statuses: readonly DesktopBadgeWorkspaceStatus[],
|
||||||
|
): number | undefined {
|
||||||
|
const actionableCount = statuses.filter(isWorkspaceActionableForDesktopBadge).length;
|
||||||
|
return actionableCount > 0 ? actionableCount : undefined;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user