mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add handlers and setting for nightly release
This commit is contained in:
@@ -11,6 +11,7 @@ import { lazy, Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context';
|
||||
import { useCheckForUpdates } from '/@/renderer/hooks/use-check-for-updates';
|
||||
import { useSyncSettingsToMain } from '/@/renderer/hooks/use-sync-settings-to-main';
|
||||
import { AppRouter } from '/@/renderer/router/app-router';
|
||||
import { useCssSettings, useHotkeySettings, useLanguage } from '/@/renderer/store';
|
||||
@@ -38,6 +39,7 @@ export const App = () => {
|
||||
const cssRef = useRef<HTMLStyleElement | null>(null);
|
||||
|
||||
useSyncSettingsToMain();
|
||||
useCheckForUpdates();
|
||||
|
||||
const [webAudio, setWebAudio] = useState<WebAudio>();
|
||||
|
||||
|
||||
@@ -41,6 +41,13 @@ export const UpdateSettings = memo(() => {
|
||||
}),
|
||||
value: 'beta',
|
||||
},
|
||||
{
|
||||
label: t('setting.releaseChannel', {
|
||||
context: 'optionAlpha',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: 'alpha',
|
||||
},
|
||||
]}
|
||||
defaultValue={
|
||||
(localSettings?.get('release_channel') as string | undefined) || 'latest'
|
||||
@@ -50,7 +57,7 @@ export const UpdateSettings = memo(() => {
|
||||
localSettings?.set('release_channel', value);
|
||||
setSettings({
|
||||
window: {
|
||||
releaseChannel: value as 'beta' | 'latest',
|
||||
releaseChannel: value as 'alpha' | 'beta' | 'latest',
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './use-app-focus';
|
||||
export * from './use-check-for-updates';
|
||||
export * from './use-container-query';
|
||||
export * from './use-fast-average-color';
|
||||
export * from './use-hide-scrollbar';
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import isElectron from 'is-electron';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const CHECK_FOR_UPDATES_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
||||
|
||||
const utils = isElectron() ? window.api?.utils : null;
|
||||
|
||||
export const useCheckForUpdates = () => {
|
||||
const [enablePeriodicCheck, setEnablePeriodicCheck] = useState(false);
|
||||
|
||||
// We want to skip the first check since it's already checked in the main process when the app is started
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setEnablePeriodicCheck(true), CHECK_FOR_UPDATES_INTERVAL_MS);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const isEnabled =
|
||||
enablePeriodicCheck &&
|
||||
Boolean(isElectron() && utils?.checkForUpdates && !utils?.disableAutoUpdates?.());
|
||||
|
||||
return useQuery({
|
||||
enabled: isEnabled,
|
||||
queryFn: () => utils?.checkForUpdates?.(),
|
||||
queryKey: ['app-check-for-updates'],
|
||||
refetchInterval: CHECK_FOR_UPDATES_INTERVAL_MS,
|
||||
refetchIntervalInBackground: true,
|
||||
});
|
||||
};
|
||||
@@ -20,11 +20,27 @@ import { Text } from '/@/shared/components/text/text';
|
||||
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
|
||||
|
||||
const GITHUB_RELEASES_URL = 'https://api.github.com/repos/jeffvli/feishin/releases';
|
||||
const GITHUB_COMPARE_URL = 'https://api.github.com/repos/jeffvli/feishin/compare';
|
||||
const RELEASES_TO_FETCH = 30;
|
||||
|
||||
interface GitHubCompareCommit {
|
||||
commit: {
|
||||
author: { date: string; name: string };
|
||||
message: string;
|
||||
};
|
||||
html_url: string;
|
||||
sha: string;
|
||||
}
|
||||
|
||||
interface GitHubCompareResponse {
|
||||
commits: GitHubCompareCommit[];
|
||||
total_commits: number;
|
||||
}
|
||||
|
||||
interface GitHubRelease {
|
||||
body: null | string;
|
||||
name: null | string;
|
||||
prerelease: boolean;
|
||||
published_at: string;
|
||||
tag_name: string;
|
||||
}
|
||||
@@ -34,13 +50,22 @@ interface ReleaseNotesContentProps {
|
||||
version: string;
|
||||
}
|
||||
|
||||
function isAlphaVersion(version: string): boolean {
|
||||
return version.includes('-alpha');
|
||||
}
|
||||
|
||||
function parseVersionFromTag(tagName: string): string {
|
||||
return tagName.startsWith('v') ? tagName.slice(1) : tagName;
|
||||
}
|
||||
|
||||
function toTag(version: string): string {
|
||||
return version.startsWith('v') ? version : `v${version}`;
|
||||
}
|
||||
|
||||
const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedVersion, setSelectedVersion] = useState(version);
|
||||
const isAlpha = isAlphaVersion(selectedVersion);
|
||||
|
||||
// Fetch list of recent releases for the selector
|
||||
const { data: releasesList = [] } = useQuery({
|
||||
@@ -54,6 +79,10 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
retry: 2,
|
||||
});
|
||||
|
||||
const latestStableRelease = useMemo(() => {
|
||||
return releasesList.find((r) => !r.prerelease);
|
||||
}, [releasesList]);
|
||||
|
||||
const releaseOptions = useMemo(() => {
|
||||
const options = releasesList.slice(0, RELEASES_TO_FETCH).map((r) => {
|
||||
const v = parseVersionFromTag(r.tag_name);
|
||||
@@ -70,14 +99,36 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
return options;
|
||||
}, [releasesList, version]);
|
||||
|
||||
// For alpha: fetch commits between latest stable and current alpha
|
||||
const {
|
||||
data: compareData,
|
||||
isError: isCompareError,
|
||||
isLoading: isCompareLoading,
|
||||
} = useQuery({
|
||||
enabled: isAlpha && !!latestStableRelease,
|
||||
queryFn: async () => {
|
||||
const base = latestStableRelease!.tag_name;
|
||||
const head = toTag(selectedVersion);
|
||||
const response = await axios.get<GitHubCompareResponse>(
|
||||
`${GITHUB_COMPARE_URL}/${base}...${head}`,
|
||||
{ params: { per_page: 100 } },
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
queryKey: ['github-compare', latestStableRelease?.tag_name, selectedVersion],
|
||||
retry: 2,
|
||||
});
|
||||
|
||||
// For non-alpha: fetch release by tag
|
||||
const {
|
||||
data: releaseData,
|
||||
isError,
|
||||
isLoading,
|
||||
} = useQuery({
|
||||
enabled: !isAlpha,
|
||||
queryFn: async () => {
|
||||
const response = await axios.get<GitHubRelease>(
|
||||
`${GITHUB_RELEASES_URL}/tags/v${selectedVersion}`,
|
||||
`${GITHUB_RELEASES_URL}/tags/${toTag(selectedVersion)}`,
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
@@ -87,7 +138,7 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
|
||||
// Convert markdown to HTML using GitHub's markdown API
|
||||
const { data: htmlContent, isLoading: isConverting } = useQuery({
|
||||
enabled: !!releaseData?.body,
|
||||
enabled: !isAlpha && !!releaseData?.body,
|
||||
queryFn: async () => {
|
||||
const response = await axios.post(
|
||||
'https://api.github.com/markdown',
|
||||
@@ -136,7 +187,10 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
});
|
||||
}, [htmlContent]);
|
||||
|
||||
if (isLoading || isConverting) {
|
||||
const isLoadingState = isAlpha ? isCompareLoading : isLoading || isConverting;
|
||||
const isErrorState = isAlpha ? isCompareError : isError || !releaseData;
|
||||
|
||||
if (isLoadingState) {
|
||||
return (
|
||||
<Center h={400}>
|
||||
<Spinner />
|
||||
@@ -144,7 +198,8 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || !releaseData) {
|
||||
if (isErrorState) {
|
||||
const showCompareError = isAlpha && latestStableRelease;
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{releaseOptions.length > 1 && (
|
||||
@@ -158,7 +213,11 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://github.com/jeffvli/feishin/releases/tag/v${selectedVersion}`}
|
||||
href={
|
||||
showCompareError
|
||||
? `https://github.com/jeffvli/feishin/compare/${latestStableRelease.tag_name}...${toTag(selectedVersion)}`
|
||||
: `https://github.com/jeffvli/feishin/releases/tag/${toTag(selectedVersion)}`
|
||||
}
|
||||
onClick={onDismiss}
|
||||
rightSection={<Icon icon="externalLink" />}
|
||||
target="_blank"
|
||||
@@ -174,6 +233,128 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
);
|
||||
}
|
||||
|
||||
if (isAlpha && !latestStableRelease) {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{releaseOptions.length > 1 && (
|
||||
<Select
|
||||
data={releaseOptions}
|
||||
onChange={(v) => v && setSelectedVersion(v)}
|
||||
value={selectedVersion}
|
||||
/>
|
||||
)}
|
||||
<Text isMuted size="sm">
|
||||
{t('page.releasenotes.noStableReleaseToCompare', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://github.com/jeffvli/feishin/releases/tag/${toTag(selectedVersion)}`}
|
||||
onClick={onDismiss}
|
||||
rightSection={<Icon icon="externalLink" />}
|
||||
target="_blank"
|
||||
variant="subtle"
|
||||
>
|
||||
{t('action.viewMore', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
<Button onClick={onDismiss} variant="filled">
|
||||
{t('common.dismiss', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
if (isAlpha && compareData) {
|
||||
const commits = compareData.commits ?? [];
|
||||
const compareUrl = `https://github.com/jeffvli/feishin/compare/${latestStableRelease?.tag_name}...${toTag(selectedVersion)}`;
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{releaseOptions.length > 1 && (
|
||||
<Select
|
||||
data={releaseOptions}
|
||||
onChange={(v) => v && setSelectedVersion(v)}
|
||||
value={selectedVersion}
|
||||
/>
|
||||
)}
|
||||
<Text isMuted size="sm">
|
||||
{t('page.releasenotes.commitsSinceStable', {
|
||||
postProcess: 'sentenceCase',
|
||||
stable: latestStableRelease
|
||||
? parseVersionFromTag(latestStableRelease.tag_name)
|
||||
: '',
|
||||
})}
|
||||
</Text>
|
||||
<ScrollArea
|
||||
style={{
|
||||
height: '400px',
|
||||
}}
|
||||
>
|
||||
<Stack gap="xs">
|
||||
{commits.length === 0 ? (
|
||||
<Text isMuted size="sm">
|
||||
{t('page.releasenotes.noNewCommits', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
) : (
|
||||
commits.map((c) => {
|
||||
const firstLine = c.commit.message.split('\n')[0];
|
||||
return (
|
||||
<Group
|
||||
gap="sm"
|
||||
key={c.sha}
|
||||
style={{ alignItems: 'flex-start' }}
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Text
|
||||
size="sm"
|
||||
style={{ flex: 1 }}
|
||||
title={c.commit.message}
|
||||
truncate
|
||||
>
|
||||
{firstLine}
|
||||
</Text>
|
||||
<Text isMuted size="xs">
|
||||
{formatHrDateTime(c.commit.author.date)}
|
||||
</Text>
|
||||
<Button
|
||||
component="a"
|
||||
href={c.html_url}
|
||||
rightSection={<Icon icon="externalLink" />}
|
||||
size="compact-xs"
|
||||
target="_blank"
|
||||
variant="subtle"
|
||||
>
|
||||
{t('common.view', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Stack>
|
||||
</ScrollArea>
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
component="a"
|
||||
href={compareUrl}
|
||||
onClick={onDismiss}
|
||||
rightSection={<Icon icon="externalLink" />}
|
||||
target="_blank"
|
||||
variant="subtle"
|
||||
>
|
||||
{t('action.viewMore', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
<Button onClick={onDismiss} variant="filled">
|
||||
{t('common.dismiss', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{releaseOptions.length > 1 && (
|
||||
@@ -198,7 +379,7 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://github.com/jeffvli/feishin/releases/tag/v${selectedVersion}`}
|
||||
href={`https://github.com/jeffvli/feishin/releases/tag/${toTag(selectedVersion)}`}
|
||||
onClick={onDismiss}
|
||||
rightSection={<Icon icon="externalLink" />}
|
||||
target="_blank"
|
||||
|
||||
@@ -588,7 +588,7 @@ const WindowSettingsSchema = z.object({
|
||||
exitToTray: z.boolean(),
|
||||
minimizeToTray: z.boolean(),
|
||||
preventSleepOnPlayback: z.boolean(),
|
||||
releaseChannel: z.enum(['beta', 'latest']),
|
||||
releaseChannel: z.enum(['alpha', 'beta', 'latest']),
|
||||
startMinimized: z.boolean(),
|
||||
tray: z.boolean(),
|
||||
windowBarStyle: z.nativeEnum(Platform),
|
||||
|
||||
Reference in New Issue
Block a user