feat(web): prioritize SMS operations workspace
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
export interface FleetMessageSummary {
|
||||
readonly id: string;
|
||||
readonly direction: string;
|
||||
readonly phoneNumber: string;
|
||||
readonly content: string;
|
||||
readonly timestamp: string;
|
||||
}
|
||||
|
||||
export interface FleetMessagesSnapshot {
|
||||
readonly latest?: FleetMessageSummary;
|
||||
}
|
||||
|
||||
export interface FleetMessagesDataSource {
|
||||
load(instanceId: string, signal?: AbortSignal): Promise<FleetMessagesSnapshot>;
|
||||
}
|
||||
|
||||
export type FleetMessageLoadState = Readonly<{
|
||||
latest?: FleetMessageSummary;
|
||||
unavailable?: true;
|
||||
}>;
|
||||
|
||||
const FLEET_MESSAGE_CONCURRENCY = 4;
|
||||
export async function loadFleetMessageSummaries(
|
||||
dataSource: FleetMessagesDataSource,
|
||||
instanceIds: readonly string[],
|
||||
signal: AbortSignal,
|
||||
): Promise<ReadonlyMap<string, FleetMessageLoadState>> {
|
||||
const results = new Map<string, FleetMessageLoadState>();
|
||||
let cursor = 0;
|
||||
async function worker(): Promise<void> {
|
||||
while (!signal.aborted) {
|
||||
const index = cursor++;
|
||||
const instanceId = instanceIds[index];
|
||||
if (instanceId === undefined) return;
|
||||
try {
|
||||
const result = await dataSource.load(instanceId, signal);
|
||||
if (signal.aborted) return;
|
||||
results.set(instanceId, result.latest ? { latest: result.latest } : {});
|
||||
} catch {
|
||||
if (signal.aborted) return;
|
||||
results.set(instanceId, { unavailable: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(
|
||||
Array.from({ length: Math.min(FLEET_MESSAGE_CONCURRENCY, instanceIds.length) }, () => worker()),
|
||||
);
|
||||
return signal.aborted ? new Map() : results;
|
||||
}
|
||||
|
||||
function record(value: unknown): Record<string, unknown> | undefined {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function parseMessage(value: unknown): FleetMessageSummary | undefined {
|
||||
const item = record(value);
|
||||
if (
|
||||
typeof item?.id !== 'string' ||
|
||||
typeof item.direction !== 'string' ||
|
||||
typeof item.phoneNumber !== 'string' ||
|
||||
typeof item.content !== 'string' ||
|
||||
typeof item.timestamp !== 'string'
|
||||
)
|
||||
return undefined;
|
||||
return {
|
||||
id: item.id,
|
||||
direction: item.direction,
|
||||
phoneNumber: item.phoneNumber,
|
||||
content: item.content,
|
||||
timestamp: item.timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
export function createFleetMessagesApiDataSource(
|
||||
fetcher: typeof fetch = fetch,
|
||||
): FleetMessagesDataSource {
|
||||
return {
|
||||
async load(instanceId, signal): Promise<FleetMessagesSnapshot> {
|
||||
const response = await fetcher(
|
||||
`/api/v1/instances/${encodeURIComponent(instanceId)}/messages?limit=1&offset=0`,
|
||||
{
|
||||
method: 'GET',
|
||||
credentials: 'same-origin',
|
||||
headers: { accept: 'application/json' },
|
||||
...(signal ? { signal } : {}),
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error(`Fleet messages request failed (${response.status}).`);
|
||||
const root = record(await response.json());
|
||||
if (!Array.isArray(root?.messages)) throw new Error('Fleet messages response is invalid.');
|
||||
if (root.messages.length === 0) return {};
|
||||
const latest = parseMessage(root.messages[0]);
|
||||
if (!latest) throw new Error('Fleet messages response is invalid.');
|
||||
return { latest };
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user