Skip to content

Commit

Permalink
feat: resources are updated automatically once a new version is relea…
Browse files Browse the repository at this point in the history
…sed (blib-la#224)

## Motivation

* Provides an automated way to handle version updates of Captain
* When the resources (like embedded-python) are updated in the manifest,
it will show the installer
* If no resource changed with an update of Captain, Captain will just
open

## Issues closed

closes blib-la#216

---------

Co-authored-by: Gregor Adams <1148334+pixelass@users.noreply.github.com>
  • Loading branch information
TimPietrusky and pixelass committed Apr 24, 2024
1 parent e3a5de0 commit 2aab5fb
Show file tree
Hide file tree
Showing 47 changed files with 1,036 additions and 350 deletions.
44 changes: 22 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -37,10 +37,10 @@
"sharp": "0.33.2"
},
"devDependencies": {
"@captn/joy": "^0.18.0",
"@captn/react": "^0.18.0",
"@captn/theme": "^0.18.0",
"@captn/utils": "^0.18.0",
"@captn/joy": "^0.20.0",
"@captn/react": "^0.20.0",
"@captn/theme": "^0.20.0",
"@captn/utils": "^0.20.0",
"@commitlint/cli": "^19.2.2",
"@commitlint/config-conventional": "^19.2.2",
"@dnd-kit/core": "^6.1.0",
Expand Down
36 changes: 36 additions & 0 deletions playwright/update.test.ts
@@ -0,0 +1,36 @@
import type { ElectronApplication, Page } from "@playwright/test";
import { test, expect } from "@playwright/test";
import { _electron as electron } from "playwright";

let electronApp: ElectronApplication;
let page: Page;

test.beforeAll(async () => {
electronApp = await electron.launch({
args: ["."],
env: {
...process.env,
TEST_ENV: "test",
TEST_APP_STATUS: "UPDATE",
},
});
const isPackaged = await electronApp.evaluate(async ({ app }) => app.isPackaged);

expect(isPackaged).toBe(false);
});

test.afterAll(async () => {
await electronApp.close();
});

test("Renders the update page", async () => {
page = await electronApp.firstWindow();
const title = await page.title();
expect(title).toBe("Captain");
});

test("Shows the update button", async () => {
page = await electronApp.firstWindow();

await expect(page.getByTestId("installer-02-start")).toBeVisible();
});
14 changes: 14 additions & 0 deletions src/client/ions/hooks/__tests__/color-scheme.test.tsx
Expand Up @@ -19,6 +19,20 @@ jest.mock("@mui/joy/styles", () => ({
}));

describe("useSsrColorScheme", () => {
beforeEach(() => {
Object.defineProperty(window, "ipc", {
value: {
send: jest.fn(),
on: jest.fn(() => jest.fn()),
},
configurable: true,
});
});

afterEach(() => {
jest.clearAllMocks();
});

it("should set initial mode to 'system'", () => {
const { result } = renderHook(() => useSsrColorScheme());
expect(result.current.mode).toBe("system");
Expand Down
4 changes: 3 additions & 1 deletion src/client/ions/hooks/color-scheme.ts
@@ -1,3 +1,4 @@
import { USER_THEME_KEY } from "@captn/utils/constants";
import { useColorScheme } from "@mui/joy/styles";
import type { Mode } from "@mui/system/cssVars/useCurrentColorScheme";
import { useCallback, useEffect, useState } from "react";
Expand All @@ -12,9 +13,10 @@ export function useSsrColorScheme() {
},
[setMode]
);

useEffect(() => {
setMode_(mode ?? "system");

window.ipc.send(USER_THEME_KEY, mode ?? "system");
}, [mode]);
return { mode: mode_, setMode: setLazyMode };
}
79 changes: 79 additions & 0 deletions src/client/ions/hooks/install-progress.ts
@@ -0,0 +1,79 @@
import { DownloadState } from "@captn/utils/constants";
import type { Progress } from "electron-dl";
import { useCallback, useEffect, useState } from "react";

import { buildKey } from "#/build-key";
import { ID } from "#/enums";

export function useInstallProgress({ state }: { state: DownloadState }) {
const [status, setStatus] = useState(state);
const [error, setError] = useState("");
const [name, setName] = useState("");
const [size, setSize] = useState("");
const [progress, setProgress] = useState<Progress>({
percent: 0,
transferredBytes: 0,
totalBytes: 0,
});
const reset = useCallback(() => {
setProgress({
percent: 0,
transferredBytes: 0,
totalBytes: 0,
});
}, []);

useEffect(() => {
const unsubscribeStarted = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":started" }),
({ name, size }) => {
setStatus(DownloadState.ACTIVE);
setName(name);
setSize(size);
setError("");
}
);
const unsubscribeProgress = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":progress" }),
(progress: Progress) => {
setStatus(DownloadState.ACTIVE);
setProgress(progress);
}
);
const unsubscribeCancelled = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":canceled" }),
() => {
setStatus(DownloadState.CANCELED);
}
);
const unsubscribeCompleted = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":completed" }),
() => {
setStatus(DownloadState.DONE);
}
);
const unsubscribeUnpacking = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":unpacking" }),
() => {
setStatus(DownloadState.UNPACKING);
}
);
const unsubscribeFailed = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":failed" }),
message => {
setStatus(DownloadState.FAILED);
setError(message);
}
);

return () => {
unsubscribeStarted();
unsubscribeProgress();
unsubscribeCancelled();
unsubscribeCompleted();
unsubscribeUnpacking();
unsubscribeFailed();
};
}, []);
return { status, progress, name, size, error, reset };
}
35 changes: 35 additions & 0 deletions src/client/organisms/installer/index.tsx
@@ -0,0 +1,35 @@
import Box from "@mui/joy/Box";
import Typography from "@mui/joy/Typography";
import type { ReactNode } from "react";

import { Illustration } from "@/organisms/illustration";

export function InstallStep({
illustration,
heading,
children,
}: {
illustration: string;
heading: string;
children?: ReactNode;
}) {
return (
<>
<Illustration height={200} path={illustration} />
<Typography level="h1" sx={{ my: 2, textAlign: "center" }}>
{heading}
</Typography>
<Box
sx={{
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
mx: 8,
}}
>
{children}
</Box>
</>
);
}
1 change: 1 addition & 0 deletions src/client/pages/[locale]/apps/explorer.tsx
Expand Up @@ -207,6 +207,7 @@ export default function Page(_properties: InferGetStaticPropsType<typeof getStat
language: "en",
type: "app",
label: "Preview",
creatorID: "Blibla",
},
},
{
Expand Down
3 changes: 3 additions & 0 deletions src/client/pages/[locale]/core/downloads.tsx
Expand Up @@ -7,6 +7,7 @@ import DownloadingIcon from "@mui/icons-material/Downloading";
import ErrorIcon from "@mui/icons-material/Error";
import PauseCircleIcon from "@mui/icons-material/PauseCircle";
import PendingIcon from "@mui/icons-material/Pending";
import SystemUpdateAltIcon from "@mui/icons-material/SystemUpdateAlt";
import Box from "@mui/joy/Box";
import LinearProgress from "@mui/joy/LinearProgress";
import List from "@mui/joy/List";
Expand Down Expand Up @@ -38,6 +39,7 @@ const iconMap: Record<DownloadState, ReactNode> = {
[DownloadState.FAILED]: <ErrorIcon />,
[DownloadState.CANCELED]: <PauseCircleIcon />,
[DownloadState.UNPACKING]: <BuildCircleIcon />,
[DownloadState.UPDATE]: <SystemUpdateAltIcon />,
} as const;
const iconColors: Record<DownloadState, ColorPaletteProp> = {
[DownloadState.ACTIVE]: "green",
Expand All @@ -46,6 +48,7 @@ const iconColors: Record<DownloadState, ColorPaletteProp> = {
[DownloadState.FAILED]: "red",
[DownloadState.CANCELED]: "neutral",
[DownloadState.UNPACKING]: "green",
[DownloadState.UPDATE]: "blue",
} as const;

export function DownloadListItem({ label, percent, state }: DownloadListItemProperties) {
Expand Down

0 comments on commit 2aab5fb

Please sign in to comment.