From ddeaed82bb71262fb7e5fdd58b5e75e343bc32b0 Mon Sep 17 00:00:00 2001 From: n0vella <177157984+n0vella@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:42:04 +0200 Subject: [PATCH] updater integrated on ui --- .github/workflows/publish.yml | 2 +- package.json | 1 + pnpm-lock.yaml | 7 +++ resources/translations/en.json | 7 ++- resources/translations/es.json | 7 ++- src-tauri/tauri.conf.json | 4 +- src/DB.tsx | 72 +++++++++++------------ src/Settings.tsx | 1 + src/Updater.tsx | 104 +++++++++++++++++++++++++++++++++ src/components/TitleBar.tsx | 11 ++-- src/index.d.ts | 3 +- src/pages/Settings.tsx | 11 +++- 12 files changed, 180 insertions(+), 50 deletions(-) create mode 100644 src/Updater.tsx diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1a6c5da..b783d69 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -74,7 +74,7 @@ jobs: with: tagName: __VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version. releaseName: '__VERSION__' - releaseBody: 'See the assets to download this version and install.' + releaseBody: ${{ github.event.head_commit.message }} releaseDraft: true prerelease: false args: ${{ matrix.args }} diff --git a/package.json b/package.json index 18f25c6..5b07198 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@dnd-kit/utilities": "^3.2.2", "@tauri-apps/api": "^1.6.0", "@uidotdev/usehooks": "^2.4.1", + "date-fns": "^3.6.0", "i18next": "^23.12.2", "lodash": "^4.17.21", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54a4e16..85db8e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: '@uidotdev/usehooks': specifier: ^2.4.1 version: 2.4.1(react-dom@18.2.0)(react@18.2.0) + date-fns: + specifier: ^3.6.0 + version: 3.6.0 i18next: specifier: ^23.12.2 version: 23.12.2 @@ -3027,6 +3030,10 @@ packages: is-data-view: 1.0.1 dev: true + /date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: false + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: diff --git a/resources/translations/en.json b/resources/translations/en.json index 4882950..d0a9213 100644 --- a/resources/translations/en.json +++ b/resources/translations/en.json @@ -55,5 +55,10 @@ "welcome_message": "Welcome to Cardo. On this screen you'll see your playlist and what's new on your favorite podcasts.", "current_podcast": "This podcast", "copy_feed_url": "Copy podcast URL", - "feed_url_copied": "Podcast URL copied to clipboard" + "feed_url_copied": "Podcast URL copied to clipboard", + "new_update_available": "A new update is available!", + "release_notes": "Release Notes", + "install": "Install", + "later": "Later", + "check_updates": "Check for updates" } \ No newline at end of file diff --git a/resources/translations/es.json b/resources/translations/es.json index 3683a90..aa1ec15 100644 --- a/resources/translations/es.json +++ b/resources/translations/es.json @@ -54,5 +54,10 @@ "welcome_message": "Bienvenido a Cardo. En esta pantalla verás tu cola de reproducción y las novedades en tus podcasts favoritos.", "current_podcast": "Este podcast", "copy_feed_url": "Copiar URL del podcast", - "feed_url_copied": "URL del podcast copiada al portapapeles" + "feed_url_copied": "URL del podcast copiada al portapapeles", + "new_update_available": "¡Hay una nueva actualizacion disponible!", + "release_notes": "Notas del lanzamiento", + "install": "Instalar", + "later": "Mas tarde", + "check_updates": "Buscar actualizaciones" } \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a9352bd..d46f0e7 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,7 +7,7 @@ }, "package": { "productName": "Cardo", - "version": "1.0.0" + "version": "1.1.0" }, "tauri": { "updater": { @@ -15,7 +15,7 @@ "endpoints": [ "https://github.com/n0vella/cardo/releases/latest/download/latest.json" ], - "dialog": true, + "dialog": false, "pubkey":"dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDdDODExNjU4QTczOUZDMUIKUldRYi9EbW5XQmFCZkdydnVrZ1dWQ0NBR2NMckRpLzBOam5HNzBOcUk0U3VLNTc3b21FKytDeksK" }, "allowlist": { diff --git a/src/DB.tsx b/src/DB.tsx index e6dd2ff..b1fcd88 100644 --- a/src/DB.tsx +++ b/src/DB.tsx @@ -93,52 +93,43 @@ const updateEpisodeState = async (episodeUrl: string, podcastUrl: string, positi // #endregion // #region MISC -const getSyncKey = async (): Promise => { + +const getMiscKey = async (key: string): Promise => { const r: { value: string }[] = await db.select( `SELECT value from misc - WHERE description = "syncKey"`, - ) + WHERE description = "$1"`, [key]) if (r.length > 0) { return r[0].value } } -const setSyncKey = async (key: string) => { +const setMiscValue = async (key: string, value: string) => { await db.execute( `INSERT into misc (description, value) - VALUES ("syncKey", $1) + VALUES ("$1", $2) ON CONFLICT (description) DO UPDATE - SET value = $1 - WHERE description = "syncKey" + SET value = $2 + WHERE description = "$1" `, - [key], + [key, value], ) } -const getLastSync = async (): Promise => { - const r: { value: number }[] = await db.select( - `SELECT value from misc - WHERE description = "lastSync"`, - ) - if (r.length > 0) { - return r[0].value - } else { - return 0 - } + +const getSyncKey = async (): Promise => { + return await getMiscKey('syncKey') } -const setLastSync = async (timestamp: number) => { +const setSyncKey = async (key: string) => { + return await setMiscValue('syncKey', key) +} - await db.execute( - `INSERT into misc (description, value) - VALUES ("lastSync", $1) - ON CONFLICT (description) DO UPDATE - SET value = $1 - WHERE description = "lastSync" - `, - [timestamp], - ) +const getLastSync = async (): Promise => { + return Number(await getMiscKey('lastSync')) ?? 0 +} +const setLastSync = async (timestamp: number) => { + return await setMiscValue('lastSync', timestamp.toString()) } const getLastPlayed = async (): Promise => { @@ -166,16 +157,17 @@ const setLastPlaying = async (playingEpisode?: EpisodeData) => { const data = playingEpisode ? JSON.stringify(playingEpisode): 'NONE' - await db.execute( - `INSERT into misc (description, value) - VALUES ("lastPlaying", $1) - ON CONFLICT (description) DO UPDATE - SET value = $1 - WHERE description = "lastPlaying" - `, - [data], - ) + return await setMiscValue('lastPlaying', data) + +} + +const getLastUpdate = async() => { + return Number(await getMiscKey('lastUpdate')) ?? 0 +} + +const setLastUpdate = async(timestamp: number) => { + return await setMiscValue('lastUpdate', timestamp.toString()) } // #endregion @@ -556,7 +548,11 @@ function initDB() { updatingFeeds, updateSubscriptionsFeed }, - dbLoaded + dbLoaded, + appUpdate: { + getLastUpdate, + setLastUpdate + } } } diff --git a/src/Settings.tsx b/src/Settings.tsx index 0b58aaf..a6b23f7 100644 --- a/src/Settings.tsx +++ b/src/Settings.tsx @@ -120,6 +120,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) { general: { numberOfDaysInNews: 15, fetchSubscriptionsAtStartup: true, + checkUpdates: true }, colors: { primary: 'dark', diff --git a/src/Updater.tsx b/src/Updater.tsx new file mode 100644 index 0000000..f5a7f0d --- /dev/null +++ b/src/Updater.tsx @@ -0,0 +1,104 @@ +import { + checkUpdate, + installUpdate, + onUpdaterEvent, +} from '@tauri-apps/api/updater' +import { relaunch } from '@tauri-apps/api/process' +import { useEffect, useRef, useState } from 'react' +import { UnlistenFn } from '@tauri-apps/api/event' +import { useDB } from './DB' +import { parse } from 'date-fns'; +import { useTranslation } from 'react-i18next' +import { upArrow } from './Icons' + + +export default function Updater() { + const unlistenCeckUpdates = useRef() + const [dialog, setDialog] = useState<{ version: string, releaseNotes: string }>() + const [showDialog, setShowDialog] = useState(false) + const { appUpdate } = useDB() + const { t } = useTranslation() + + useEffect(() => { + checkUpdates() + + return () => unlistenCeckUpdates.current && unlistenCeckUpdates.current() + }, []) + + + const checkUpdates = async () => { + + unlistenCeckUpdates.current = await onUpdaterEvent(({ error, status }) => { + // This will log all updater events, including status updates and errors. + console.log('Updater event', error, status) + }) + + try { + const { shouldUpdate, manifest } = await checkUpdate() + + if (!manifest || !shouldUpdate) return + + + setDialog({ + version: manifest.version, + releaseNotes: manifest.body + }) + + const formatString = "yyyy-MM-dd HH:mm:ss.SS xxxxx" + const releaseDate = parse(manifest.date, formatString, new Date()) + + const lastUpdate = new Date(await appUpdate.getLastUpdate()) + + setShowDialog(releaseDate.getTime() > lastUpdate.getTime()) // annoying dialog is shown only once, after that only the title bar icon appears + + await appUpdate.setLastUpdate(Date.now()) + + } catch (error) { + console.error(error) + } + } + + return ( + <> + + +
+

{t('new_update_available')}

+
+

{t('release_notes')}: v{dialog?.version}

+

{dialog?.releaseNotes}

+
+
+ + + +
+
+ + ) +} \ No newline at end of file diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 2c4c5ea..f15ed01 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -5,6 +5,7 @@ import { SyncButton, useSync } from "../sync/Nextcloud"; import { usePlayer } from "./AudioPlayer"; import { UnlistenFn } from "@tauri-apps/api/event"; import { useSettings } from "../Settings"; +import Updater from "../Updater"; function TitleBar() { @@ -12,7 +13,7 @@ function TitleBar() { const [maximized, setMaximized] = useState(false) const { onExit: savePlayerStatus } = usePlayer() const { performSync } = useSync() - const [{ sync: {syncBeforeAppClose} }, _] = useSettings() + const [{ sync: {syncBeforeAppClose}, general: {checkUpdates} }, _] = useSettings() const unlistenClose = useRef() @@ -44,17 +45,19 @@ function TitleBar() { return (
-
+
+ + {checkUpdates && }
-

Cardo

+

Cardo