Line data Source code
1 : import * as React from 'react';
2 :
3 : import { DASHQL_LATEST_RELEASE_MANIFEST, DASHQL_STABLE_RELEASE_MANIFEST } from '../../globals.js';
4 : import { RESULT_ERROR, RESULT_OK, Result } from '../../utils/result.js';
5 : import { useLogger } from '../logger/logger_provider.js';
6 : import {
7 : LATEST_RELEASE_MANIFEST_CTX,
8 : LATEST_UPDATE_MANIFEST_CTX,
9 : INSTALLATION_STATUS_CTX,
10 : InstallableUpdate,
11 : InstallationState,
12 : InstallationStatusCode,
13 : STABLE_RELEASE_MANIFEST_CTX,
14 : STABLE_UPDATE_MANIFEST_CTX,
15 : VERSION_CHECK_CTX,
16 : VERSION_CHECK_REFRESH_CTX,
17 : VersionCheckStatusCode,
18 : useVersionCheckRefreshState,
19 : } from './version_check.js';
20 : import { detectReleaseChannel, loadReleaseManifest, ReleaseChannel, ReleaseManifest } from './web_version_check.js';
21 : import { DASHQL_VERSION } from '../../globals.js';
22 :
23 : type Props = {children: React.ReactElement};
24 :
25 1 : export const ElectronVersionCheck: React.FC<Props> = ({children}) => {
26 1 : const logger = useLogger();
27 1 : const bridge = globalThis.dashqlElectron!.updates;
28 1 : const activeChannel = detectReleaseChannel(DASHQL_VERSION);
29 1 : const [status, setStatus] = React.useState<VersionCheckStatusCode>(VersionCheckStatusCode.Unknown);
30 1 : const [installation, setInstallation] = React.useState<InstallationState | null>(null);
31 1 : const [stableRelease, setStableRelease] = React.useState<Result<ReleaseManifest> | null>(null);
32 1 : const [latestRelease, setLatestRelease] = React.useState<Result<ReleaseManifest> | null>(null);
33 1 : const updateRef = React.useRef<Record<ReleaseChannel, InstallableUpdate> | null>(null);
34 :
35 1 : if (updateRef.current === null) {
36 1 : updateRef.current = {
37 1 : stable: {download: async () => await bridge.download('stable')},
38 1 : latest: {download: async () => await bridge.download('latest')},
39 1 : };
40 : }
41 1 : const updates = updateRef.current;
42 1 : const [stableUpdate, setStableUpdate] = React.useState<Result<InstallableUpdate | null> | null>(null);
43 1 : const [latestUpdate, setLatestUpdate] = React.useState<Result<InstallableUpdate | null> | null>(null);
44 :
45 1 : const applyStatus = React.useEffectEvent((next: DashQLElectronUpdateStatus) => {
46 1 : switch (next.status) {
47 0 : case 'disabled':
48 0 : setStatus(VersionCheckStatusCode.Disabled);
49 0 : if (activeChannel === 'latest') {
50 0 : setLatestUpdate({type: RESULT_OK, value: null});
51 : } else {
52 0 : setStableUpdate({type: RESULT_OK, value: null});
53 : }
54 0 : break;
55 0 : case 'checking':
56 0 : setStatus(VersionCheckStatusCode.Unknown);
57 0 : break;
58 1 : case 'up-to-date':
59 1 : setStatus(VersionCheckStatusCode.UpToDate);
60 1 : if (next.channel === 'latest') {
61 0 : setLatestUpdate({type: RESULT_OK, value: null});
62 : } else {
63 1 : setStableUpdate({type: RESULT_OK, value: null});
64 : }
65 1 : break;
66 0 : case 'available':
67 0 : setStatus(VersionCheckStatusCode.UpdateAvailable);
68 0 : if (next.channel === 'latest') {
69 0 : setLatestUpdate({type: RESULT_OK, value: updates.latest});
70 : } else {
71 0 : setStableUpdate({type: RESULT_OK, value: updates.stable});
72 : }
73 0 : break;
74 0 : case 'downloading':
75 0 : setStatus(VersionCheckStatusCode.UpdateInstalling);
76 0 : setInstallation({
77 0 : update: updates[next.channel],
78 0 : statusCode: InstallationStatusCode.InProgress,
79 0 : totalBytes: next.total,
80 0 : loadedBytes: next.transferred,
81 0 : inProgressBytes: 0,
82 0 : error: null,
83 0 : });
84 0 : break;
85 0 : case 'downloaded':
86 0 : setStatus(VersionCheckStatusCode.RestartPending);
87 0 : setInstallation({
88 0 : update: updates[next.channel],
89 0 : statusCode: InstallationStatusCode.RestartPending,
90 0 : totalBytes: null,
91 0 : loadedBytes: 0,
92 0 : inProgressBytes: 0,
93 0 : error: null,
94 0 : });
95 0 : break;
96 0 : case 'error': {
97 0 : const error = new Error(next.message);
98 0 : setStatus(VersionCheckStatusCode.UpdateFailed);
99 0 : if (next.channel === 'latest') {
100 0 : setLatestUpdate({type: RESULT_ERROR, error});
101 : } else {
102 0 : setStableUpdate({type: RESULT_ERROR, error});
103 : }
104 0 : setInstallation((current) => current === null ? null : {
105 0 : ...current,
106 0 : statusCode: InstallationStatusCode.Failed,
107 0 : error,
108 0 : });
109 0 : break;
110 0 : }
111 1 : }
112 1 : });
113 :
114 1 : const check = React.useCallback(async () => {
115 1 : await Promise.all([
116 1 : loadReleaseManifest('stable', DASHQL_STABLE_RELEASE_MANIFEST, logger)
117 1 : .then(value => setStableRelease({type: RESULT_OK, value}))
118 1 : .catch(error => setStableRelease({type: RESULT_ERROR, error})),
119 1 : loadReleaseManifest('latest', DASHQL_LATEST_RELEASE_MANIFEST, logger)
120 1 : .then(value => setLatestRelease({type: RESULT_OK, value}))
121 1 : .catch(error => setLatestRelease({type: RESULT_ERROR, error})),
122 1 : bridge.check(activeChannel)
123 1 : .then(applyStatus)
124 1 : .catch(error => applyStatus({status: 'error', channel: activeChannel, message: String(error)})),
125 1 : ]);
126 1 : }, [bridge, logger]);
127 1 : const refresh = useVersionCheckRefreshState(check);
128 :
129 1 : React.useEffect(() => {
130 1 : const unsubscribe = bridge.onStatus(applyStatus);
131 1 : void bridge.getStatus().then(applyStatus);
132 1 : return unsubscribe;
133 1 : }, [bridge]);
134 :
135 1 : const stableInstallableUpdate = stableUpdate ?? (activeChannel === 'latest' ? {type: RESULT_OK, value: updates.stable} as const : null);
136 1 : const latestInstallableUpdate = latestUpdate ?? (activeChannel === 'stable' ? {type: RESULT_OK, value: updates.latest} as const : null);
137 1 : return (
138 1 : <VERSION_CHECK_CTX.Provider value={status}>
139 1 : <VERSION_CHECK_REFRESH_CTX.Provider value={refresh}>
140 1 : <INSTALLATION_STATUS_CTX.Provider value={installation}>
141 1 : <STABLE_RELEASE_MANIFEST_CTX.Provider value={stableRelease}>
142 1 : <STABLE_UPDATE_MANIFEST_CTX.Provider value={stableInstallableUpdate}>
143 1 : <LATEST_RELEASE_MANIFEST_CTX.Provider value={latestRelease}>
144 1 : <LATEST_UPDATE_MANIFEST_CTX.Provider value={latestInstallableUpdate}>
145 1 : {children}
146 1 : </LATEST_UPDATE_MANIFEST_CTX.Provider>
147 1 : </LATEST_RELEASE_MANIFEST_CTX.Provider>
148 1 : </STABLE_UPDATE_MANIFEST_CTX.Provider>
149 1 : </STABLE_RELEASE_MANIFEST_CTX.Provider>
150 1 : </INSTALLATION_STATUS_CTX.Provider>
151 1 : </VERSION_CHECK_REFRESH_CTX.Provider>
152 1 : </VERSION_CHECK_CTX.Provider>
153 : );
154 1 : };
|