Stockage flat côté ticket + métadonnées d'attachments, lecture exposée côté backend/MCP, et UI minimale de liste/ajout dans TicketDetail. Traverse le domaine (Issue, ports), l'application (usecases + assistant de ticket), les adaptateurs infra/MCP (issues store, orchestrateur), les DTO backend/web- server/app-tauri, et le frontend (domain/ports/adapters/hooks/UI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
/**
|
|
* Tauri adapter for {@link SystemGateway}. This is the *only* place (together
|
|
* with the sibling adapters) that touches `@tauri-apps/api`. Components reach it
|
|
* exclusively through the port — never `invoke()` directly.
|
|
*/
|
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
import { listen } from "@tauri-apps/api/event";
|
|
import { open } from "@tauri-apps/plugin-dialog";
|
|
|
|
import type { AppExitWorkGuardState, DomainEvent, HealthReport, Unsubscribe } from "@/domain";
|
|
import type { SystemGateway } from "@/ports";
|
|
|
|
/** Tauri event name carrying relayed domain events (mirror of `DOMAIN_EVENT`). */
|
|
const DOMAIN_EVENT = "domain://event";
|
|
|
|
/** Tauri event name carrying the app-exit work guard state (ticket #83). */
|
|
const APP_EXIT_WORK_GUARD = "app-exit-work-guard";
|
|
|
|
export class TauriSystemGateway implements SystemGateway {
|
|
async health(note?: string): Promise<HealthReport> {
|
|
// The backend command takes an optional `request: { note }` (camelCase).
|
|
return invoke<HealthReport>("health", {
|
|
request: note === undefined ? null : { note },
|
|
});
|
|
}
|
|
|
|
async onDomainEvent(
|
|
handler: (event: DomainEvent) => void,
|
|
): Promise<Unsubscribe> {
|
|
const unlisten = await listen<DomainEvent>(DOMAIN_EVENT, (e) => {
|
|
handler(e.payload);
|
|
});
|
|
return unlisten;
|
|
}
|
|
|
|
async pickFolder(): Promise<string | null> {
|
|
const result = await open({ directory: true, multiple: false });
|
|
if (result === null || result === undefined) return null;
|
|
// `open` with `multiple: false` returns a string when a path is chosen.
|
|
return typeof result === "string" ? result : null;
|
|
}
|
|
|
|
async pickArchiveFile(): Promise<string | null> {
|
|
const result = await open({
|
|
directory: false,
|
|
multiple: false,
|
|
filters: [{ name: "Plugin archive", extensions: ["ideaplug", "zip", "vsix"] }],
|
|
});
|
|
return typeof result === "string" ? result : null;
|
|
}
|
|
|
|
async pickFile(): Promise<string | null> {
|
|
const result = await open({ directory: false, multiple: false });
|
|
return typeof result === "string" ? result : null;
|
|
}
|
|
|
|
async onAppExitWorkGuard(
|
|
handler: (state: AppExitWorkGuardState) => void,
|
|
): Promise<Unsubscribe> {
|
|
const unlisten = await listen<AppExitWorkGuardState>(APP_EXIT_WORK_GUARD, (e) => {
|
|
handler(e.payload);
|
|
});
|
|
return unlisten;
|
|
}
|
|
|
|
async confirmAppExit(): Promise<void> {
|
|
await invoke("confirm_app_exit");
|
|
}
|
|
}
|