From 251106fbdfde70fc721803a35c2aae0f6bcbbfe9 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Thu, 6 Nov 2025 19:23:07 +0800 Subject: [PATCH] implement what's new closes #669 --- src/main/configStore.ts | 1 + src/renderer/src/App.tsx | 3 + src/renderer/src/components/WhatsNew.tsx | 66 +++++++++++++++++++ src/renderer/src/hooks/useUserSettingsRoot.ts | 4 ++ src/renderer/src/versions.ts | 18 +++++ types.ts | 1 + 6 files changed, 93 insertions(+) create mode 100644 src/renderer/src/components/WhatsNew.tsx create mode 100644 src/renderer/src/versions.ts diff --git a/src/main/configStore.ts b/src/main/configStore.ts index e7bf11c6..b675cc24 100644 --- a/src/main/configStore.ts +++ b/src/main/configStore.ts @@ -97,6 +97,7 @@ const defaultKeyBindings: KeyBinding[] = [ const defaults: Config = { version: 1, + lastAppVersion: app.getVersion(), captureFormat: 'jpeg', customOutDir: undefined, keyframeCut: true, diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index d5fd1184..5338ab82 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -106,6 +106,7 @@ import ErrorDialog from './components/ErrorDialog'; import useErrorHandling from './hooks/useErrorHandling'; import GenericDialog, { useDialog } from './components/GenericDialog'; import useHtml5ify from './hooks/useHtml5ify'; +import WhatsNew from './components/WhatsNew'; const electron = window.require('electron'); const { exists } = window.require('fs-extra'); @@ -2779,6 +2780,8 @@ function App() { !open && closeGenericDialog()} /> + + !open && setGenericError(undefined)} /> diff --git a/src/renderer/src/components/WhatsNew.tsx b/src/renderer/src/components/WhatsNew.tsx new file mode 100644 index 00000000..9727ea45 --- /dev/null +++ b/src/renderer/src/components/WhatsNew.tsx @@ -0,0 +1,66 @@ +import { useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import semver from 'semver'; +import { FaCode, FaFile } from 'react-icons/fa'; + +import * as Dialog from './Dialog'; +import useUserSettings from '../hooks/useUserSettings'; +import { appVersion, isMasBuild } from '../util'; +import versions from '../versions'; +import Button from './Button'; + +const remote = window.require('@electron/remote'); +const { shell } = remote; + +export default function WhatsNew() { + const { t } = useTranslation(); + const { lastAppVersion, setLastAppVersion } = useUserSettings(); + + const [initialLastAppVersion] = useState(lastAppVersion); + + useEffect(() => { + //setLastAppVersion(appVersion); + }, [setLastAppVersion]); + + const matchingVersions = useMemo(() => versions + .filter(({ version }) => semver.gt(version, initialLastAppVersion) && semver.lte(version, appVersion)) + .sort(({ version: a }, { version: b }) => semver.compare(b, a)), [initialLastAppVersion]); + + if (initialLastAppVersion === appVersion) return null; + + return ( + + + + + + {t('What\'s new in LosslessCut?')} + + +
+ {matchingVersions.map(({ version, highlights }, i) => { + // +1 because reverse order + const prevVersion = matchingVersions[i + 1]?.version ?? initialLastAppVersion; + + return ( +
+ {!(isMasBuild && matchingVersions.length === 1) &&

v{version}

} +
    + {highlights?.map((h) => ( +
  • {h}
  • + ))} +
+ + + +
+ ); + })} +
+ + +
+
+
+ ); +} diff --git a/src/renderer/src/hooks/useUserSettingsRoot.ts b/src/renderer/src/hooks/useUserSettingsRoot.ts index c33a5a7c..7a3ba949 100644 --- a/src/renderer/src/hooks/useUserSettingsRoot.ts +++ b/src/renderer/src/hooks/useUserSettingsRoot.ts @@ -49,6 +49,8 @@ export default function useUserSettingsRoot() { // Without this there was a huge performance issue https://github.com/mifi/lossless-cut/issues/1097 const safeGetConfigInitial = (key: T) => () => safeGetConfig(key); + const [lastAppVersion, setLastAppVersion] = useState(safeGetConfigInitial('lastAppVersion')); + useEffect(() => safeSetConfig({ lastAppVersion }), [lastAppVersion]); const [captureFormat, setCaptureFormat] = useState(safeGetConfigInitial('captureFormat')); useEffect(() => safeSetConfig({ captureFormat }), [captureFormat]); const [customOutDir, setCustomOutDir] = useState(safeGetConfigInitial('customOutDir')); @@ -222,6 +224,7 @@ export default function useUserSettingsRoot() { const springAnimation = useMemo(() => (prefersReducedMotion ? { duration: 0 } : mySpring), [prefersReducedMotion]); const settings = { + lastAppVersion, captureFormat, customOutDir, keyframeCut, @@ -297,6 +300,7 @@ export default function useUserSettingsRoot() { return { settings, + setLastAppVersion, setCaptureFormat, setCustomOutDir, setKeyframeCut, diff --git a/src/renderer/src/versions.ts b/src/renderer/src/versions.ts new file mode 100644 index 00000000..b7f1fc21 --- /dev/null +++ b/src/renderer/src/versions.ts @@ -0,0 +1,18 @@ +const versions: { version: string, highlights?: string[] }[] = [ + { + version: '3.66.1', + highlights: [ + '‼️ Upgraded Electron to 38 - They dropped support for macOS 11', + '🆕 Markers: Segments that don\'t have any end time are now considered "markers". They are rendered differently, and are excluded from exports. Markers can be useful for bookmarking locations on the timeline and all markers can be batch exported as screenshots.', + '🔈 Play multiple audio tracks simultaneously (with FFmpeg-assisted playback)', + '✅ Remember segment selected/deselected state inside .llc file', + '💿 Split timeline by byte size', + 'Upgraded FFmpeg to 8.0', + ], + }, + { + version: '3.64.0', + }, +]; + +export default versions; diff --git a/types.ts b/types.ts index c0655900..321c14ed 100644 --- a/types.ts +++ b/types.ts @@ -54,6 +54,7 @@ export type FixCodecTagOption = 'always' | 'never' | 'auto'; export interface Config { version: number, + lastAppVersion: string, captureFormat: CaptureFormat, customOutDir: string | undefined, keyframeCut: boolean,