Skip to content

Commit 4ed7d80

Browse files
committed
refactor: move external-apps service to trpc
1 parent 8d1c386 commit 4ed7d80

File tree

13 files changed

+304
-329
lines changed

13 files changed

+304
-329
lines changed

apps/array/src/main/di/container.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import "reflect-metadata";
22
import { Container } from "inversify";
33
import { ContextMenuService } from "../services/context-menu/service.js";
4+
import { ExternalAppsService } from "../services/external-apps/service.js";
45
import { GitService } from "../services/git/service.js";
56
import { MAIN_TOKENS } from "./tokens.js";
67

@@ -15,6 +16,9 @@ export const container = new Container({
1516
container
1617
.bind<ContextMenuService>(MAIN_TOKENS.ContextMenuService)
1718
.to(ContextMenuService);
19+
container
20+
.bind<ExternalAppsService>(MAIN_TOKENS.ExternalAppsService)
21+
.to(ExternalAppsService);
1822
container.bind<GitService>(MAIN_TOKENS.GitService).to(GitService);
1923

2024
export function get<T>(token: symbol): T {

apps/array/src/main/di/tokens.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
export const MAIN_TOKENS = Object.freeze({
88
// Services
99
ContextMenuService: Symbol.for("Main.ContextMenuService"),
10+
ExternalAppsService: Symbol.for("Main.ExternalAppsService"),
1011
GitService: Symbol.for("Main.GitService"),
1112
});

apps/array/src/main/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,7 @@ import { registerFoldersIpc } from "./services/folders.js";
3636
import { registerFsIpc } from "./services/fs.js";
3737
import { registerGitIpc } from "./services/git.js";
3838
import "./services/index.js";
39-
import {
40-
getOrRefreshApps,
41-
registerExternalAppsIpc,
42-
} from "./services/externalApps.js";
39+
import { ExternalAppsService } from "./services/external-apps/service.js";
4340
import { registerOAuthHandlers } from "./services/oauth.js";
4441
import {
4542
initializePostHog,
@@ -264,7 +261,7 @@ app.whenReady().then(() => {
264261
trackAppEvent(ANALYTICS_EVENTS.APP_STARTED);
265262

266263
// Preload external app icons in background
267-
getOrRefreshApps().catch(() => {
264+
new ExternalAppsService().getDetectedApps().catch(() => {
268265
// Silently fail, will retry on first use
269266
});
270267
});
@@ -305,5 +302,4 @@ registerFileWatcherIpc(() => mainWindow);
305302
registerFoldersIpc(() => mainWindow);
306303
registerWorktreeIpc();
307304
registerShellIpc();
308-
registerExternalAppsIpc();
309305
registerWorkspaceIpc(() => mainWindow);

apps/array/src/main/preload.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -295,31 +295,6 @@ contextBridge.exposeInMainWorld("electronAPI", {
295295
): Promise<string | null> =>
296296
ipcRenderer.invoke("worktree-get-main-repo", mainRepoPath, worktreePath),
297297
},
298-
// External Apps API
299-
externalApps: {
300-
getDetectedApps: (): Promise<
301-
Array<{
302-
id: string;
303-
name: string;
304-
type: "editor" | "terminal";
305-
path: string;
306-
command: string;
307-
icon?: string;
308-
}>
309-
> => ipcRenderer.invoke("external-apps:get-detected-apps"),
310-
openInApp: (
311-
appId: string,
312-
path: string,
313-
): Promise<{ success: boolean; error?: string }> =>
314-
ipcRenderer.invoke("external-apps:open-in-app", appId, path),
315-
setLastUsed: (appId: string): Promise<void> =>
316-
ipcRenderer.invoke("external-apps:set-last-used", appId),
317-
getLastUsed: (): Promise<{
318-
lastUsedApp?: string;
319-
}> => ipcRenderer.invoke("external-apps:get-last-used"),
320-
copyPath: (path: string): Promise<void> =>
321-
ipcRenderer.invoke("external-apps:copy-path", path),
322-
},
323298
// Workspace API
324299
workspace: {
325300
create: (options: CreateWorkspaceOptions): Promise<WorkspaceInfo> =>

apps/array/src/main/services/context-menu/service.ts

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
} from "electron";
77
import { injectable } from "inversify";
88
import type { DetectedApplication } from "../../../shared/types.js";
9+
import { get } from "../../di/container.js";
10+
import { MAIN_TOKENS } from "../../di/tokens.js";
911
import { getMainWindow } from "../../trpc/context.js";
10-
import { externalAppsStore, getOrRefreshApps } from "../externalApps.js";
12+
import type { ExternalAppsService } from "../external-apps/service.js";
1113
import type {
1214
ActionItemDef,
1315
ConfirmOptions,
@@ -33,11 +35,24 @@ import type {
3335
export class ContextMenuService {
3436
private readonly ICON_SIZE = 16;
3537

38+
private getExternalAppsService() {
39+
return get<ExternalAppsService>(MAIN_TOKENS.ExternalAppsService);
40+
}
41+
42+
private async getExternalAppsData() {
43+
const service = this.getExternalAppsService();
44+
const [apps, lastUsed] = await Promise.all([
45+
service.getDetectedApps(),
46+
service.getLastUsed(),
47+
]);
48+
return { apps, lastUsedAppId: lastUsed.lastUsedApp };
49+
}
50+
3651
async showTaskContextMenu(
3752
input: TaskContextMenuInput,
3853
): Promise<TaskContextMenuResult> {
3954
const { taskTitle, worktreePath } = input;
40-
const apps = await getOrRefreshApps();
55+
const { apps, lastUsedAppId } = await this.getExternalAppsData();
4156

4257
return this.showMenu<TaskAction>([
4358
this.item("Rename", { type: "rename" }),
@@ -58,7 +73,10 @@ export class ContextMenuService {
5873
},
5974
),
6075
...(worktreePath
61-
? [this.separator(), ...this.externalAppItems<TaskAction>(apps)]
76+
? [
77+
this.separator(),
78+
...this.externalAppItems<TaskAction>(apps, lastUsedAppId),
79+
]
6280
: []),
6381
]);
6482
}
@@ -67,7 +85,7 @@ export class ContextMenuService {
6785
input: FolderContextMenuInput,
6886
): Promise<FolderContextMenuResult> {
6987
const { folderName, folderPath } = input;
70-
const apps = await getOrRefreshApps();
88+
const { apps, lastUsedAppId } = await this.getExternalAppsData();
7189

7290
return this.showMenu<FolderAction>([
7391
this.item(
@@ -84,7 +102,10 @@ export class ContextMenuService {
84102
},
85103
),
86104
...(folderPath
87-
? [this.separator(), ...this.externalAppItems<FolderAction>(apps)]
105+
? [
106+
this.separator(),
107+
...this.externalAppItems<FolderAction>(apps, lastUsedAppId),
108+
]
88109
: []),
89110
]);
90111
}
@@ -93,7 +114,7 @@ export class ContextMenuService {
93114
input: TabContextMenuInput,
94115
): Promise<TabContextMenuResult> {
95116
const { canClose, filePath } = input;
96-
const apps = await getOrRefreshApps();
117+
const { apps, lastUsedAppId } = await this.getExternalAppsData();
97118

98119
return this.showMenu<TabAction>([
99120
this.item(
@@ -107,7 +128,10 @@ export class ContextMenuService {
107128
this.item("Close other tabs", { type: "close-others" }),
108129
this.item("Close tabs to the right", { type: "close-right" }),
109130
...(filePath
110-
? [this.separator(), ...this.externalAppItems<TabAction>(apps)]
131+
? [
132+
this.separator(),
133+
...this.externalAppItems<TabAction>(apps, lastUsedAppId),
134+
]
111135
: []),
112136
]);
113137
}
@@ -125,7 +149,7 @@ export class ContextMenuService {
125149
async showFileContextMenu(
126150
input: FileContextMenuInput,
127151
): Promise<FileContextMenuResult> {
128-
const apps = await getOrRefreshApps();
152+
const { apps, lastUsedAppId } = await this.getExternalAppsData();
129153

130154
return this.showMenu<FileAction>([
131155
...(input.showCollapseAll
@@ -134,17 +158,18 @@ export class ContextMenuService {
134158
this.separator(),
135159
]
136160
: []),
137-
...this.externalAppItems<FileAction>(apps),
161+
...this.externalAppItems<FileAction>(apps, lastUsedAppId),
138162
]);
139163
}
140164

141-
private externalAppItems<T>(apps: DetectedApplication[]): MenuItemDef<T>[] {
165+
private externalAppItems<T>(
166+
apps: DetectedApplication[],
167+
lastUsedAppId?: string,
168+
): MenuItemDef<T>[] {
142169
if (apps.length === 0) {
143170
return [this.disabled("No external apps detected")];
144171
}
145172

146-
const lastUsedAppId =
147-
externalAppsStore.get("externalAppsPrefs")?.lastUsedApp;
148173
const lastUsedApp = apps.find((app) => app.id === lastUsedAppId) || apps[0];
149174
const openIn = (appId: string): T =>
150175
({ type: "external-app", action: { type: "open-in-app", appId } }) as T;

0 commit comments

Comments
 (0)