diff --git a/CHANGELOG.md b/CHANGELOG.md index e23265c0..bedcf54e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.7] - 2026-03-13 + +### Added +- **Update Notifications**: Integrated a native update check system in the sidebar that notifies you when a new version is available on GitHub. +- **Improved Settings**: Refined layout and styling for a cleaner settings experience. + +### Fixed +- **UI/UX Refinements**: Polished sidebar animations and layout for better visual consistency. +- **i18n**: Added missing translations for update notifications and system status. + ## [1.0.6] - 2026-03-13 ### Added diff --git a/package.json b/package.json index 89b342c9..70f4f419 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "psysonic", - "version": "1.0.6", + "version": "1.0.7", "private": true, "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2fdb431e..dbd54a72 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "psysonic" -version = "1.0.6" +version = "1.0.7" description = "Psysonic Desktop Music Player" authors = [] license = "" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 70dfad32..250c75d9 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Psysonic", - "version": "1.0.6", + "version": "1.0.7", "identifier": "dev.psysonic.app", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 50b67e3c..10d0ff58 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { NavLink } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Disc3, Users, Music4, Radio, Settings, Heart, BarChart3, Shuffle, ListMusic, - PanelLeftClose, PanelLeft, HelpCircle, Dices + PanelLeftClose, PanelLeft, HelpCircle, Dices, ArrowUpCircle } from 'lucide-react'; const PsysonicLogo = () => ( @@ -21,14 +21,74 @@ const navItems = [ { icon: Heart, labelKey: 'sidebar.favorites', to: '/favorites' }, ]; -export default function Sidebar({ - isCollapsed = false, - toggleCollapse -}: { +function isNewer(latest: string, current: string): boolean { + const parse = (v: string) => v.replace(/^v/, '').split('.').map(Number); + const [lMaj, lMin, lPat] = parse(latest); + const [cMaj, cMin, cPat] = parse(current); + if (lMaj !== cMaj) return lMaj > cMaj; + if (lMin !== cMin) return lMin > cMin; + return lPat > cPat; +} + +function UpdateToast({ isCollapsed, latestVersion }: { isCollapsed: boolean; latestVersion: string }) { + const { t } = useTranslation(); + + if (isCollapsed) { + return ( +
+ +
+ ); + } + + return ( +
+
+ + {t('sidebar.updateAvailable')} +
+
{t('sidebar.updateReady', { version: latestVersion })}
+ + {t('sidebar.updateLink')} + +
+ ); +} + +export default function Sidebar({ + isCollapsed = false, + toggleCollapse +}: { isCollapsed?: boolean; toggleCollapse?: () => void; }) { const { t } = useTranslation(); + const [latestVersion, setLatestVersion] = useState(null); + + useEffect(() => { + let cancelled = false; + const timer = setTimeout(async () => { + try { + const { getVersion } = await import('@tauri-apps/api/app'); + const current = await getVersion(); + const res = await fetch('https://api.github.com/repos/Psychotoxical/psysonic/releases/latest'); + if (!res.ok) return; + const data = await res.json(); + const tag: string = data.tag_name ?? ''; + if (!cancelled && tag && isNewer(tag, current)) { + setLatestVersion(tag.startsWith('v') ? tag : `v${tag}`); + } + } catch { + // network unavailable or not running in Tauri — silently skip + } + }, 1500); + return () => { cancelled = true; clearTimeout(timer); }; + }, []); return (