diff --git a/apps/desktop/src/components/AppUpdater.test.ts b/apps/desktop/src/components/AppUpdater.test.ts index 1c3cd30e19..c1e2d91470 100644 --- a/apps/desktop/src/components/AppUpdater.test.ts +++ b/apps/desktop/src/components/AppUpdater.test.ts @@ -1,19 +1,18 @@ import AppUpdater from '$components/AppUpdater.svelte'; import { EventContext } from '$lib/analytics/eventContext'; import { PostHogWrapper } from '$lib/analytics/posthog'; -import { Tauri } from '$lib/backend/tauri'; +import createBackend, { type Update } from '$lib/backend'; import { ShortcutService } from '$lib/shortcuts/shortcutService'; import { getSettingsdServiceMock } from '$lib/testing/mockSettingsdService'; import { UPDATER_SERVICE, UpdaterService } from '$lib/updater/updater'; import { render, screen } from '@testing-library/svelte'; import { expect, test, describe, vi, beforeEach, afterEach } from 'vitest'; -import type { Update } from '@tauri-apps/plugin-updater'; describe('AppUpdater', () => { let updater: UpdaterService; let context: Map; - const tauri = new Tauri(); - const shortcuts = new ShortcutService(tauri); + const backend = createBackend(); + const shortcuts = new ShortcutService(backend); const MockSettingsService = getSettingsdServiceMock(); const settingsService = new MockSettingsService(); const eventContext = new EventContext(); @@ -21,9 +20,9 @@ describe('AppUpdater', () => { beforeEach(() => { vi.useFakeTimers(); - updater = new UpdaterService(tauri, posthog, shortcuts); + updater = new UpdaterService(backend, posthog, shortcuts); context = new Map([[UPDATER_SERVICE._key, updater]]); - vi.spyOn(tauri, 'listen').mockReturnValue(async () => {}); + vi.spyOn(backend, 'listen').mockReturnValue(async () => {}); vi.mock('$env/dynamic/public', () => { return { env: { @@ -39,23 +38,18 @@ describe('AppUpdater', () => { }); test('should be hidden if no update', async () => { - vi.spyOn(tauri, 'checkUpdate').mockReturnValue( - mockUpdate({ - version: '1' - }) - ); + vi.spyOn(backend, 'checkUpdate').mockReturnValue(mockUpdate(null)); render(AppUpdater, { context }); await vi.advanceTimersToNextTimerAsync(); const updateBanner = screen.queryByTestId('update-banner'); - expect(updateBanner).toBeNull(); + expect(updateBanner).toBe(null); }); test('should display download button', async () => { - vi.spyOn(tauri, 'checkUpdate').mockReturnValue( + vi.spyOn(backend, 'checkUpdate').mockReturnValue( mockUpdate({ - available: true, version: '1', body: 'release notes' }) @@ -68,24 +62,19 @@ describe('AppUpdater', () => { expect(button).toBeVisible(); }); - test('should display up-to-date on manaul check', async () => { - vi.spyOn(tauri, 'checkUpdate').mockReturnValue( - mockUpdate({ - available: false - }) - ); - render(AppUpdater, { context }); + test('should display up-to-date on manual check', async () => { + vi.spyOn(backend, 'checkUpdate').mockReturnValue(mockUpdate(null)); + const { getByTestId } = render(AppUpdater, { context }); updater.checkForUpdate(true); await vi.advanceTimersToNextTimerAsync(); - const button = screen.getByTestId('got-it'); + const button = getByTestId('got-it'); expect(button).toBeVisible(); }); test('should display restart button on install complete', async () => { - vi.spyOn(tauri, 'checkUpdate').mockReturnValue( + vi.spyOn(backend, 'checkUpdate').mockReturnValue( mockUpdate({ - available: true, version: '2', body: 'release notes' }) @@ -102,7 +91,11 @@ describe('AppUpdater', () => { }); }); -async function mockUpdate(update: Partial): Promise { +async function mockUpdate(update: Partial | null): Promise { + if (update === null) { + return await Promise.resolve(null); + } + return await Promise.resolve({ download: () => {}, install: () => {}, diff --git a/apps/desktop/src/components/BranchHeaderContextMenu.svelte b/apps/desktop/src/components/BranchHeaderContextMenu.svelte index 35015f4fb1..4abef4d925 100644 --- a/apps/desktop/src/components/BranchHeaderContextMenu.svelte +++ b/apps/desktop/src/components/BranchHeaderContextMenu.svelte @@ -24,7 +24,7 @@ import { projectAiGenEnabled } from '$lib/config/config'; import { DEFAULT_FORGE_FACTORY } from '$lib/forge/forgeFactory.svelte'; import { STACK_SERVICE } from '$lib/stacks/stackService.svelte'; - import { openExternalUrl } from '$lib/utils/url'; + import { URL_SERVICE } from '$lib/utils/url'; import { inject } from '@gitbutler/shared/context'; import { ContextMenu, @@ -59,6 +59,7 @@ const stackService = inject(STACK_SERVICE); const forge = inject(DEFAULT_FORGE_FACTORY); const promptService = inject(PROMPT_SERVICE); + const urlService = inject(URL_SERVICE); const [insertBlankCommitInBranch, commitInsertion] = stackService.insertBlankCommit; const [updateBranchNameMutation] = stackService.updateBranchName; const [createRef, refCreation] = stackService.createReference; @@ -180,7 +181,7 @@ testId={TestId.BranchHeaderContextMenu_OpenInBrowser} onclick={() => { const url = forge.current.branch(branchName)?.url; - if (url) openExternalUrl(url); + if (url) urlService.openExternalUrl(url); close(); }} /> @@ -299,7 +300,7 @@ label="Open PR in browser" testId={TestId.BranchHeaderContextMenu_OpenPRInBrowser} onclick={() => { - openExternalUrl(pr.htmlUrl); + urlService.openExternalUrl(pr.htmlUrl); close(); }} /> diff --git a/apps/desktop/src/components/BranchList.svelte b/apps/desktop/src/components/BranchList.svelte index fb77c905fc..a0f3570ad6 100644 --- a/apps/desktop/src/components/BranchList.svelte +++ b/apps/desktop/src/components/BranchList.svelte @@ -18,7 +18,7 @@ import { STACK_SERVICE } from '$lib/stacks/stackService.svelte'; import { combineResults } from '$lib/state/helpers'; import { UI_STATE } from '$lib/state/uiState.svelte'; - import { openExternalUrl } from '$lib/utils/url'; + import { URL_SERVICE } from '$lib/utils/url'; import { copyToClipboard } from '@gitbutler/shared/clipboard'; import { inject } from '@gitbutler/shared/context'; @@ -43,6 +43,7 @@ const uiState = inject(UI_STATE); const modeService = inject(MODE_SERVICE); const forge = inject(DEFAULT_FORGE_FACTORY); + const urlService = inject(URL_SERVICE); const intelligentScrollingService = inject(INTELLIGENT_SCROLLING_SERVICE); const [insertBlankCommitInBranch, commitInsertion] = stackService.insertBlankCommit; @@ -264,7 +265,7 @@ disabled={!prUrl} onclick={() => { if (prUrl) { - openExternalUrl(prUrl); + urlService.openExternalUrl(prUrl); } }} icon={forge.current.name === 'gitlab' ? 'view-mr-browser' : 'view-pr-browser'} diff --git a/apps/desktop/src/components/CloneForm.svelte b/apps/desktop/src/components/CloneForm.svelte index 4f82b61138..ffc10db994 100644 --- a/apps/desktop/src/components/CloneForm.svelte +++ b/apps/desktop/src/components/CloneForm.svelte @@ -3,7 +3,7 @@ import InfoMessage, { type MessageStyle } from '$components/InfoMessage.svelte'; import Section from '$components/Section.svelte'; import { POSTHOG_WRAPPER } from '$lib/analytics/posthog'; - import { invoke } from '$lib/backend/ipc'; + import { GIT_SERVICE } from '$lib/git/gitService'; import { PROJECTS_SERVICE } from '$lib/project/projectsService'; import { projectPath } from '$lib/routes/routes.svelte'; import { parseRemoteUrl } from '$lib/url/gitUrl'; @@ -18,6 +18,7 @@ import { onMount } from 'svelte'; const projectsService = inject(PROJECTS_SERVICE); + const gitService = inject(GIT_SERVICE); const posthog = inject(POSTHOG_WRAPPER); let loading = $state(false); @@ -84,10 +85,7 @@ const targetDir = await join(targetDirPath, remoteUrl.name); - await invoke('git_clone_repository', { - repositoryUrl, - targetDir - }); + await gitService.cloneRepo(repositoryUrl, targetDir); posthog.capture('Repository Cloned', { protocol: remoteUrl.protocol }); const project = await projectsService.addProject(targetDir); diff --git a/apps/desktop/src/components/CommitContextMenu.svelte b/apps/desktop/src/components/CommitContextMenu.svelte index 492dd26dbb..5a8296deeb 100644 --- a/apps/desktop/src/components/CommitContextMenu.svelte +++ b/apps/desktop/src/components/CommitContextMenu.svelte @@ -45,7 +45,7 @@ import { writeClipboard } from '$lib/backend/clipboard'; import { rewrapCommitMessage } from '$lib/config/uiFeatureFlags'; import { STACK_SERVICE } from '$lib/stacks/stackService.svelte'; - import { openExternalUrl } from '$lib/utils/url'; + import { URL_SERVICE } from '$lib/utils/url'; import { inject } from '@gitbutler/shared/context'; import { ContextMenu, @@ -66,6 +66,7 @@ let { flat, projectId, openId = $bindable(), rightClickTrigger, contextData }: Props = $props(); + const urlService = inject(URL_SERVICE); const stackService = inject(STACK_SERVICE); const [insertBlankCommitInBranch, commitInsertion] = stackService.insertBlankCommit; const [createRef, refCreation] = stackService.createReference; @@ -164,7 +165,7 @@ { - await openExternalUrl(commitUrl); + await urlService.openExternalUrl(commitUrl); close(); }} /> diff --git a/apps/desktop/src/components/CommitSigningForm.svelte b/apps/desktop/src/components/CommitSigningForm.svelte index 2d53bee214..624aa8aed0 100644 --- a/apps/desktop/src/components/CommitSigningForm.svelte +++ b/apps/desktop/src/components/CommitSigningForm.svelte @@ -2,8 +2,8 @@ import InfoMessage from '$components/InfoMessage.svelte'; import Section from '$components/Section.svelte'; import SectionCardDisclaimer from '$components/SectionCardDisclaimer.svelte'; - import { invoke } from '$lib/backend/ipc'; import { GIT_CONFIG_SERVICE } from '$lib/config/gitConfigService'; + import { GIT_SERVICE } from '$lib/git/gitService'; import { inject } from '@gitbutler/shared/context'; import { Button, Link, SectionCard, Select, SelectItem, Textbox, Toggle } from '@gitbutler/ui'; @@ -12,6 +12,7 @@ const { projectId }: { projectId: string } = $props(); const gitConfig = inject(GIT_CONFIG_SERVICE); + const gitService = inject(GIT_SERVICE); let signCommits = $state(false); @@ -57,8 +58,9 @@ errorMessage = ''; checked = true; loading = true; - await invoke('check_signing_settings', { projectId: projectId }) - .then((_) => { + await gitService + .checkSigningSettings(projectId) + .then(() => { signCheckResult = true; }) .catch((err) => { diff --git a/apps/desktop/src/components/DecorativeSplitView.svelte b/apps/desktop/src/components/DecorativeSplitView.svelte index 27f6f89175..d37d86e5c4 100644 --- a/apps/desktop/src/components/DecorativeSplitView.svelte +++ b/apps/desktop/src/components/DecorativeSplitView.svelte @@ -2,7 +2,7 @@ import AccountLink from '$components/AccountLink.svelte'; import gbLogoSvg from '$lib/assets/gb-logo.svg?raw'; import { USER } from '$lib/user/user'; - import { openExternalUrl } from '$lib/utils/url'; + import { URL_SERVICE } from '$lib/utils/url'; import { inject } from '@gitbutler/shared/context'; import { Icon } from '@gitbutler/ui'; import { type Snippet } from 'svelte'; @@ -17,6 +17,7 @@ const { hideDetails, img, children, testId }: Props = $props(); const user = inject(USER); + const urlService = inject(URL_SERVICE);
@@ -51,7 +52,8 @@ await instalCLI()}>Install But CLI (requires admin) or diff --git a/apps/desktop/src/components/FileContextMenu.svelte b/apps/desktop/src/components/FileContextMenu.svelte index 8db2d4beca..e472c90054 100644 --- a/apps/desktop/src/components/FileContextMenu.svelte +++ b/apps/desktop/src/components/FileContextMenu.svelte @@ -6,6 +6,7 @@ import { writeClipboard } from '$lib/backend/clipboard'; import { changesToDiffSpec } from '$lib/commits/utils'; import { projectAiExperimentalFeaturesEnabled, projectAiGenEnabled } from '$lib/config/config'; + import { FILE_SERVICE } from '$lib/files/fileService'; import { isTreeChange, type TreeChange } from '$lib/hunks/change'; import { platformName } from '$lib/platform/platform'; import { vscodePath } from '$lib/project/project'; @@ -15,7 +16,7 @@ import { STACK_SERVICE } from '$lib/stacks/stackService.svelte'; import { UI_STATE } from '$lib/state/uiState.svelte'; import { computeChangeStatus } from '$lib/utils/fileStatus'; - import { getEditorUri, openExternalUrl, showFileInFolder } from '$lib/utils/url'; + import { getEditorUri, URL_SERVICE } from '$lib/utils/url'; import { inject } from '@gitbutler/shared/context'; import { @@ -61,6 +62,8 @@ const idSelection = inject(ID_SELECTION); const aiService = inject(AI_SERVICE); const actionService = inject(ACTION_SERVICE); + const fileService = inject(FILE_SERVICE); + const urlService = inject(URL_SERVICE); const [autoCommit, autoCommitting] = actionService.autoCommit; const [branchChanges, branchingChanges] = actionService.branchChanges; const [absorbChanges, absorbingChanges] = actionService.absorb; @@ -428,7 +431,7 @@ schemeId: $userSettings.defaultCodeEditor.schemeIdentifer, path: [vscodePath(projectPath), change.path] }); - openExternalUrl(path); + urlService.openExternalUrl(path); } } contextMenu.close(); @@ -446,7 +449,7 @@ const projectPath = project?.path; if (projectPath) { const absPath = await join(projectPath, item.changes[0]!.path); - await showFileInFolder(absPath); + await fileService.showFileInFolder(absPath); } contextMenu.close(); }} diff --git a/apps/desktop/src/components/GithubIntegration.svelte b/apps/desktop/src/components/GithubIntegration.svelte index 8f2d730be4..bf47c4a208 100644 --- a/apps/desktop/src/components/GithubIntegration.svelte +++ b/apps/desktop/src/components/GithubIntegration.svelte @@ -2,7 +2,7 @@ import { writeClipboard } from '$lib/backend/clipboard'; import { GITHUB_USER_SERVICE } from '$lib/forge/github/githubUserService.svelte'; import { USER_SERVICE } from '$lib/user/userService'; - import { openExternalUrl } from '$lib/utils/url'; + import { URL_SERVICE } from '$lib/utils/url'; import { inject } from '@gitbutler/shared/context'; import { Button, Icon, Modal, SectionCard, chipToasts as toasts } from '@gitbutler/ui'; @@ -18,6 +18,7 @@ const githubUserService = inject(GITHUB_USER_SERVICE); const userService = inject(USER_SERVICE); const user = userService.user; + const urlService = inject(URL_SERVICE); // step flags let codeCopied = $state(false); @@ -160,7 +161,7 @@ disabled={GhActivationLinkPressed} icon="open-link" onclick={() => { - openExternalUrl('https://github.com/login/device'); + urlService.openExternalUrl('https://github.com/login/device'); GhActivationLinkPressed = true; // add timeout to prevent show the check button before the page is opened diff --git a/apps/desktop/src/components/HunkContextMenu.svelte b/apps/desktop/src/components/HunkContextMenu.svelte index ea738b108b..865f85e1db 100644 --- a/apps/desktop/src/components/HunkContextMenu.svelte +++ b/apps/desktop/src/components/HunkContextMenu.svelte @@ -19,7 +19,7 @@ import { PROJECTS_SERVICE } from '$lib/project/projectsService'; import { SETTINGS } from '$lib/settings/userSettings'; import { STACK_SERVICE } from '$lib/stacks/stackService.svelte'; - import { getEditorUri, openExternalUrl } from '$lib/utils/url'; + import { getEditorUri, URL_SERVICE } from '$lib/utils/url'; import { inject } from '@gitbutler/shared/context'; import { ContextMenu, ContextMenuItem, ContextMenuSection, TestId } from '@gitbutler/ui'; import type { TreeChange } from '$lib/hunks/change'; @@ -50,6 +50,7 @@ const stackService = inject(STACK_SERVICE); const ircService = inject(IRC_SERVICE); const projectService = inject(PROJECTS_SERVICE); + const urlService = inject(URL_SERVICE); const userSettings = inject(SETTINGS); const ircChats = $derived(ircService.getChats()); @@ -174,7 +175,7 @@ path: [vscodePath(project.path), filePath], line: item.beforeLineNumber ?? item.afterLineNumber }); - openExternalUrl(path); + urlService.openExternalUrl(path); } contextMenu?.close(); }} diff --git a/apps/desktop/src/components/IconLink.svelte b/apps/desktop/src/components/IconLink.svelte index 3328af43e8..d439bf14a6 100644 --- a/apps/desktop/src/components/IconLink.svelte +++ b/apps/desktop/src/components/IconLink.svelte @@ -1,6 +1,6 @@ - - {/await} + {/if}