Skip to content

Commit 7ebd4e8

Browse files
authored
Merge pull request #9804 from gitbutlerapp/backend-refactor-web-tauri
Generic backend
2 parents a5b3ee2 + 1de6bb5 commit 7ebd4e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+780
-469
lines changed

apps/desktop/src/components/AppUpdater.test.ts

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
import AppUpdater from '$components/AppUpdater.svelte';
22
import { EventContext } from '$lib/analytics/eventContext';
33
import { PostHogWrapper } from '$lib/analytics/posthog';
4-
import { Tauri } from '$lib/backend/tauri';
4+
import createBackend, { type Update } from '$lib/backend';
55
import { ShortcutService } from '$lib/shortcuts/shortcutService';
66
import { getSettingsdServiceMock } from '$lib/testing/mockSettingsdService';
77
import { UPDATER_SERVICE, UpdaterService } from '$lib/updater/updater';
88
import { render, screen } from '@testing-library/svelte';
99
import { expect, test, describe, vi, beforeEach, afterEach } from 'vitest';
10-
import type { Update } from '@tauri-apps/plugin-updater';
1110

1211
describe('AppUpdater', () => {
1312
let updater: UpdaterService;
1413
let context: Map<any, any>;
15-
const tauri = new Tauri();
16-
const shortcuts = new ShortcutService(tauri);
14+
const backend = createBackend();
15+
const shortcuts = new ShortcutService(backend);
1716
const MockSettingsService = getSettingsdServiceMock();
1817
const settingsService = new MockSettingsService();
1918
const eventContext = new EventContext();
2019
const posthog = new PostHogWrapper(settingsService, eventContext);
2120

2221
beforeEach(() => {
2322
vi.useFakeTimers();
24-
updater = new UpdaterService(tauri, posthog, shortcuts);
23+
updater = new UpdaterService(backend, posthog, shortcuts);
2524
context = new Map([[UPDATER_SERVICE._key, updater]]);
26-
vi.spyOn(tauri, 'listen').mockReturnValue(async () => {});
25+
vi.spyOn(backend, 'listen').mockReturnValue(async () => {});
2726
vi.mock('$env/dynamic/public', () => {
2827
return {
2928
env: {
@@ -39,23 +38,18 @@ describe('AppUpdater', () => {
3938
});
4039

4140
test('should be hidden if no update', async () => {
42-
vi.spyOn(tauri, 'checkUpdate').mockReturnValue(
43-
mockUpdate({
44-
version: '1'
45-
})
46-
);
41+
vi.spyOn(backend, 'checkUpdate').mockReturnValue(mockUpdate(null));
4742

4843
render(AppUpdater, { context });
4944
await vi.advanceTimersToNextTimerAsync();
5045

5146
const updateBanner = screen.queryByTestId('update-banner');
52-
expect(updateBanner).toBeNull();
47+
expect(updateBanner).toBe(null);
5348
});
5449

5550
test('should display download button', async () => {
56-
vi.spyOn(tauri, 'checkUpdate').mockReturnValue(
51+
vi.spyOn(backend, 'checkUpdate').mockReturnValue(
5752
mockUpdate({
58-
available: true,
5953
version: '1',
6054
body: 'release notes'
6155
})
@@ -68,24 +62,19 @@ describe('AppUpdater', () => {
6862
expect(button).toBeVisible();
6963
});
7064

71-
test('should display up-to-date on manaul check', async () => {
72-
vi.spyOn(tauri, 'checkUpdate').mockReturnValue(
73-
mockUpdate({
74-
available: false
75-
})
76-
);
77-
render(AppUpdater, { context });
65+
test('should display up-to-date on manual check', async () => {
66+
vi.spyOn(backend, 'checkUpdate').mockReturnValue(mockUpdate(null));
67+
const { getByTestId } = render(AppUpdater, { context });
7868
updater.checkForUpdate(true);
7969
await vi.advanceTimersToNextTimerAsync();
8070

81-
const button = screen.getByTestId('got-it');
71+
const button = getByTestId('got-it');
8272
expect(button).toBeVisible();
8373
});
8474

8575
test('should display restart button on install complete', async () => {
86-
vi.spyOn(tauri, 'checkUpdate').mockReturnValue(
76+
vi.spyOn(backend, 'checkUpdate').mockReturnValue(
8777
mockUpdate({
88-
available: true,
8978
version: '2',
9079
body: 'release notes'
9180
})
@@ -102,7 +91,11 @@ describe('AppUpdater', () => {
10291
});
10392
});
10493

105-
async function mockUpdate(update: Partial<Update>): Promise<Update> {
94+
async function mockUpdate(update: Partial<Update> | null): Promise<Update | null> {
95+
if (update === null) {
96+
return await Promise.resolve(null);
97+
}
98+
10699
return await Promise.resolve({
107100
download: () => {},
108101
install: () => {},

apps/desktop/src/components/BranchHeaderContextMenu.svelte

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import { projectAiGenEnabled } from '$lib/config/config';
2525
import { DEFAULT_FORGE_FACTORY } from '$lib/forge/forgeFactory.svelte';
2626
import { STACK_SERVICE } from '$lib/stacks/stackService.svelte';
27-
import { openExternalUrl } from '$lib/utils/url';
27+
import { URL_SERVICE } from '$lib/utils/url';
2828
import { inject } from '@gitbutler/shared/context';
2929
import {
3030
ContextMenu,
@@ -59,6 +59,7 @@
5959
const stackService = inject(STACK_SERVICE);
6060
const forge = inject(DEFAULT_FORGE_FACTORY);
6161
const promptService = inject(PROMPT_SERVICE);
62+
const urlService = inject(URL_SERVICE);
6263
const [insertBlankCommitInBranch, commitInsertion] = stackService.insertBlankCommit;
6364
const [updateBranchNameMutation] = stackService.updateBranchName;
6465
const [createRef, refCreation] = stackService.createReference;
@@ -180,7 +181,7 @@
180181
testId={TestId.BranchHeaderContextMenu_OpenInBrowser}
181182
onclick={() => {
182183
const url = forge.current.branch(branchName)?.url;
183-
if (url) openExternalUrl(url);
184+
if (url) urlService.openExternalUrl(url);
184185
close();
185186
}}
186187
/>
@@ -299,7 +300,7 @@
299300
label="Open PR in browser"
300301
testId={TestId.BranchHeaderContextMenu_OpenPRInBrowser}
301302
onclick={() => {
302-
openExternalUrl(pr.htmlUrl);
303+
urlService.openExternalUrl(pr.htmlUrl);
303304
close();
304305
}}
305306
/>

apps/desktop/src/components/BranchList.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import { STACK_SERVICE } from '$lib/stacks/stackService.svelte';
1919
import { combineResults } from '$lib/state/helpers';
2020
import { UI_STATE } from '$lib/state/uiState.svelte';
21-
import { openExternalUrl } from '$lib/utils/url';
21+
import { URL_SERVICE } from '$lib/utils/url';
2222
import { copyToClipboard } from '@gitbutler/shared/clipboard';
2323
import { inject } from '@gitbutler/shared/context';
2424
@@ -43,6 +43,7 @@
4343
const uiState = inject(UI_STATE);
4444
const modeService = inject(MODE_SERVICE);
4545
const forge = inject(DEFAULT_FORGE_FACTORY);
46+
const urlService = inject(URL_SERVICE);
4647
const intelligentScrollingService = inject(INTELLIGENT_SCROLLING_SERVICE);
4748
4849
const [insertBlankCommitInBranch, commitInsertion] = stackService.insertBlankCommit;
@@ -264,7 +265,7 @@
264265
disabled={!prUrl}
265266
onclick={() => {
266267
if (prUrl) {
267-
openExternalUrl(prUrl);
268+
urlService.openExternalUrl(prUrl);
268269
}
269270
}}
270271
icon={forge.current.name === 'gitlab' ? 'view-mr-browser' : 'view-pr-browser'}

apps/desktop/src/components/CloneForm.svelte

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import InfoMessage, { type MessageStyle } from '$components/InfoMessage.svelte';
44
import Section from '$components/Section.svelte';
55
import { POSTHOG_WRAPPER } from '$lib/analytics/posthog';
6-
import { invoke } from '$lib/backend/ipc';
6+
import { GIT_SERVICE } from '$lib/git/gitService';
77
import { PROJECTS_SERVICE } from '$lib/project/projectsService';
88
import { projectPath } from '$lib/routes/routes.svelte';
99
import { parseRemoteUrl } from '$lib/url/gitUrl';
@@ -18,6 +18,7 @@
1818
import { onMount } from 'svelte';
1919
2020
const projectsService = inject(PROJECTS_SERVICE);
21+
const gitService = inject(GIT_SERVICE);
2122
const posthog = inject(POSTHOG_WRAPPER);
2223
2324
let loading = $state(false);
@@ -84,10 +85,7 @@
8485
8586
const targetDir = await join(targetDirPath, remoteUrl.name);
8687
87-
await invoke('git_clone_repository', {
88-
repositoryUrl,
89-
targetDir
90-
});
88+
await gitService.cloneRepo(repositoryUrl, targetDir);
9189
9290
posthog.capture('Repository Cloned', { protocol: remoteUrl.protocol });
9391
const project = await projectsService.addProject(targetDir);

apps/desktop/src/components/CommitContextMenu.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
import { writeClipboard } from '$lib/backend/clipboard';
4646
import { rewrapCommitMessage } from '$lib/config/uiFeatureFlags';
4747
import { STACK_SERVICE } from '$lib/stacks/stackService.svelte';
48-
import { openExternalUrl } from '$lib/utils/url';
48+
import { URL_SERVICE } from '$lib/utils/url';
4949
import { inject } from '@gitbutler/shared/context';
5050
import {
5151
ContextMenu,
@@ -66,6 +66,7 @@
6666
6767
let { flat, projectId, openId = $bindable(), rightClickTrigger, contextData }: Props = $props();
6868
69+
const urlService = inject(URL_SERVICE);
6970
const stackService = inject(STACK_SERVICE);
7071
const [insertBlankCommitInBranch, commitInsertion] = stackService.insertBlankCommit;
7172
const [createRef, refCreation] = stackService.createReference;
@@ -164,7 +165,7 @@
164165
<ContextMenuItem
165166
label="Open in browser"
166167
onclick={async () => {
167-
await openExternalUrl(commitUrl);
168+
await urlService.openExternalUrl(commitUrl);
168169
close();
169170
}}
170171
/>

apps/desktop/src/components/CommitSigningForm.svelte

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import InfoMessage from '$components/InfoMessage.svelte';
33
import Section from '$components/Section.svelte';
44
import SectionCardDisclaimer from '$components/SectionCardDisclaimer.svelte';
5-
import { invoke } from '$lib/backend/ipc';
65
import { GIT_CONFIG_SERVICE } from '$lib/config/gitConfigService';
6+
import { GIT_SERVICE } from '$lib/git/gitService';
77
import { inject } from '@gitbutler/shared/context';
88
import { Button, Link, SectionCard, Select, SelectItem, Textbox, Toggle } from '@gitbutler/ui';
99
@@ -12,6 +12,7 @@
1212
const { projectId }: { projectId: string } = $props();
1313
1414
const gitConfig = inject(GIT_CONFIG_SERVICE);
15+
const gitService = inject(GIT_SERVICE);
1516
1617
let signCommits = $state(false);
1718
@@ -57,8 +58,9 @@
5758
errorMessage = '';
5859
checked = true;
5960
loading = true;
60-
await invoke('check_signing_settings', { projectId: projectId })
61-
.then((_) => {
61+
await gitService
62+
.checkSigningSettings(projectId)
63+
.then(() => {
6264
signCheckResult = true;
6365
})
6466
.catch((err) => {

apps/desktop/src/components/DecorativeSplitView.svelte

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import AccountLink from '$components/AccountLink.svelte';
33
import gbLogoSvg from '$lib/assets/gb-logo.svg?raw';
44
import { USER } from '$lib/user/user';
5-
import { openExternalUrl } from '$lib/utils/url';
5+
import { URL_SERVICE } from '$lib/utils/url';
66
import { inject } from '@gitbutler/shared/context';
77
import { Icon } from '@gitbutler/ui';
88
import { type Snippet } from 'svelte';
@@ -17,6 +17,7 @@
1717
const { hideDetails, img, children, testId }: Props = $props();
1818
1919
const user = inject(USER);
20+
const urlService = inject(URL_SERVICE);
2021
</script>
2122

2223
<div class="decorative-split-view" data-testid={testId}>
@@ -51,15 +52,17 @@
5152
<button
5253
type="button"
5354
class="right-side__link"
54-
onclick={async () => await openExternalUrl('https://docs.gitbutler.com/')}
55+
onclick={async () =>
56+
await urlService.openExternalUrl('https://docs.gitbutler.com/')}
5557
>
5658
<Icon name="docs" opacity={0.6} />
5759
<span class="text-14 text-semibold">GitButler docs</span>
5860
</button>
5961
<button
6062
type="button"
6163
class="right-side__link"
62-
onclick={async () => await openExternalUrl('https://discord.com/invite/MmFkmaJ42D')}
64+
onclick={async () =>
65+
await urlService.openExternalUrl('https://discord.com/invite/MmFkmaJ42D')}
6366
>
6467
<Icon name="discord" opacity={0.6} />
6568
<span class="text-14 text-semibold">Join community</span>

apps/desktop/src/components/EditMode.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import { SETTINGS } from '$lib/settings/userSettings';
1818
import { USER_SERVICE } from '$lib/user/userService';
1919
import { computeChangeStatus } from '$lib/utils/fileStatus';
20-
import { getEditorUri, openExternalUrl } from '$lib/utils/url';
20+
import { getEditorUri, URL_SERVICE } from '$lib/utils/url';
2121
import { inject } from '@gitbutler/shared/context';
2222
2323
import { Avatar, Badge, Button, FileListItem, InfoButton, Modal } from '@gitbutler/ui';
@@ -42,6 +42,7 @@
4242
const modeService = inject(MODE_SERVICE);
4343
const userSettings = inject(SETTINGS);
4444
const fileService = inject(FILE_SERVICE);
45+
const urlService = inject(URL_SERVICE);
4546
4647
const userService = inject(USER_SERVICE);
4748
const user = userService.user;
@@ -208,7 +209,7 @@
208209
schemeId: $userSettings.defaultCodeEditor.schemeIdentifer,
209210
path: [vscodePath(projectPath), file.path]
210211
});
211-
openExternalUrl(path);
212+
urlService.openExternalUrl(path);
212213
}
213214
}
214215

apps/desktop/src/components/Feed.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import CliSymLink from '$components/profileSettings/CliSymLink.svelte';
55
import { ACTION_SERVICE } from '$lib/actions/actionService.svelte';
66
import laneNewSvg from '$lib/assets/empty-state/lane-new.svg?raw';
7-
import { invoke } from '$lib/backend/ipc';
7+
import { CLI_MANAGER } from '$lib/cli/cli';
88
import { SETTINGS_SERVICE } from '$lib/config/appSettingsV2';
99
import { projectAiGenEnabled } from '$lib/config/config';
1010
import { FEED_FACTORY } from '$lib/feed/feed';
@@ -26,6 +26,9 @@
2626
const settingsService = inject(SETTINGS_SERVICE);
2727
const settingsStore = $derived(settingsService.appSettings);
2828
29+
const cliManager = inject(CLI_MANAGER);
30+
const [instalCLI, installingCLI] = cliManager.install;
31+
2932
const combinedEntries = $derived(feed.combined);
3033
const lastAddedId = $derived(feed.lastAddedId);
3134
@@ -202,7 +205,8 @@
202205
kind="outline"
203206
icon="play"
204207
size="tag"
205-
onclick={async () => await invoke('install_cli')}>Install But CLI</Button
208+
loading={installingCLI.current.isLoading}
209+
onclick={async () => await instalCLI()}>Install But CLI</Button
206210
>
207211
<span class="clr-text-2">(requires admin)</span>
208212
or

apps/desktop/src/components/FileContextMenu.svelte

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { writeClipboard } from '$lib/backend/clipboard';
77
import { changesToDiffSpec } from '$lib/commits/utils';
88
import { projectAiExperimentalFeaturesEnabled, projectAiGenEnabled } from '$lib/config/config';
9+
import { FILE_SERVICE } from '$lib/files/fileService';
910
import { isTreeChange, type TreeChange } from '$lib/hunks/change';
1011
import { platformName } from '$lib/platform/platform';
1112
import { vscodePath } from '$lib/project/project';
@@ -15,7 +16,7 @@
1516
import { STACK_SERVICE } from '$lib/stacks/stackService.svelte';
1617
import { UI_STATE } from '$lib/state/uiState.svelte';
1718
import { computeChangeStatus } from '$lib/utils/fileStatus';
18-
import { getEditorUri, openExternalUrl, showFileInFolder } from '$lib/utils/url';
19+
import { getEditorUri, URL_SERVICE } from '$lib/utils/url';
1920
import { inject } from '@gitbutler/shared/context';
2021
2122
import {
@@ -61,6 +62,8 @@
6162
const idSelection = inject(ID_SELECTION);
6263
const aiService = inject(AI_SERVICE);
6364
const actionService = inject(ACTION_SERVICE);
65+
const fileService = inject(FILE_SERVICE);
66+
const urlService = inject(URL_SERVICE);
6467
const [autoCommit, autoCommitting] = actionService.autoCommit;
6568
const [branchChanges, branchingChanges] = actionService.branchChanges;
6669
const [absorbChanges, absorbingChanges] = actionService.absorb;
@@ -428,7 +431,7 @@
428431
schemeId: $userSettings.defaultCodeEditor.schemeIdentifer,
429432
path: [vscodePath(projectPath), change.path]
430433
});
431-
openExternalUrl(path);
434+
urlService.openExternalUrl(path);
432435
}
433436
}
434437
contextMenu.close();
@@ -446,7 +449,7 @@
446449
const projectPath = project?.path;
447450
if (projectPath) {
448451
const absPath = await join(projectPath, item.changes[0]!.path);
449-
await showFileInFolder(absPath);
452+
await fileService.showFileInFolder(absPath);
450453
}
451454
contextMenu.close();
452455
}}

0 commit comments

Comments
 (0)