mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
feat: Update Notification system and UI refinements (v1.0.7)
This commit is contained in:
@@ -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/),
|
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).
|
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
|
## [1.0.6] - 2026-03-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "psysonic",
|
"name": "psysonic",
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "psysonic"
|
name = "psysonic"
|
||||||
version = "1.0.6"
|
version = "1.0.7"
|
||||||
description = "Psysonic Desktop Music Player"
|
description = "Psysonic Desktop Music Player"
|
||||||
authors = []
|
authors = []
|
||||||
license = ""
|
license = ""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Psysonic",
|
"productName": "Psysonic",
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"identifier": "dev.psysonic.app",
|
"identifier": "dev.psysonic.app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Disc3, Users, Music4, Radio, Settings, Heart, BarChart3, Shuffle, ListMusic,
|
Disc3, Users, Music4, Radio, Settings, Heart, BarChart3, Shuffle, ListMusic,
|
||||||
PanelLeftClose, PanelLeft, HelpCircle, Dices
|
PanelLeftClose, PanelLeft, HelpCircle, Dices, ArrowUpCircle
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
const PsysonicLogo = () => (
|
const PsysonicLogo = () => (
|
||||||
@@ -21,14 +21,74 @@ const navItems = [
|
|||||||
{ icon: Heart, labelKey: 'sidebar.favorites', to: '/favorites' },
|
{ icon: Heart, labelKey: 'sidebar.favorites', to: '/favorites' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Sidebar({
|
function isNewer(latest: string, current: string): boolean {
|
||||||
isCollapsed = false,
|
const parse = (v: string) => v.replace(/^v/, '').split('.').map(Number);
|
||||||
toggleCollapse
|
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 (
|
||||||
|
<div className="update-toast-icon" style={{ marginTop: 'auto' }} title={`${t('sidebar.updateAvailable')}: ${latestVersion}`}>
|
||||||
|
<ArrowUpCircle size={20} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="update-toast">
|
||||||
|
<div className="update-toast-header">
|
||||||
|
<ArrowUpCircle size={14} />
|
||||||
|
<span className="update-toast-label">{t('sidebar.updateAvailable')}</span>
|
||||||
|
</div>
|
||||||
|
<div className="update-toast-version">{t('sidebar.updateReady', { version: latestVersion })}</div>
|
||||||
|
<a
|
||||||
|
className="update-toast-link"
|
||||||
|
href="https://github.com/Psychotoxical/psysonic/releases"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{t('sidebar.updateLink')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Sidebar({
|
||||||
|
isCollapsed = false,
|
||||||
|
toggleCollapse
|
||||||
|
}: {
|
||||||
isCollapsed?: boolean;
|
isCollapsed?: boolean;
|
||||||
toggleCollapse?: () => void;
|
toggleCollapse?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [latestVersion, setLatestVersion] = useState<string | null>(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 (
|
return (
|
||||||
<aside className={`sidebar animate-slide-in ${isCollapsed ? 'collapsed' : ''}`}>
|
<aside className={`sidebar animate-slide-in ${isCollapsed ? 'collapsed' : ''}`}>
|
||||||
@@ -61,10 +121,11 @@ export default function Sidebar({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{!isCollapsed && <span className="nav-section-label" style={{ marginTop: 'auto' }}>{t('sidebar.system')}</span>}
|
{!isCollapsed && <span className="nav-section-label" style={{ marginTop: 'auto' }}>{t('sidebar.system')}</span>}
|
||||||
|
{latestVersion && <UpdateToast isCollapsed={isCollapsed} latestVersion={latestVersion} />}
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/statistics"
|
to="/statistics"
|
||||||
className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}
|
className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`}
|
||||||
style={isCollapsed ? { marginTop: 'auto' } : undefined}
|
style={isCollapsed && !latestVersion ? { marginTop: 'auto' } : undefined}
|
||||||
title={isCollapsed ? t('sidebar.statistics') : undefined}
|
title={isCollapsed ? t('sidebar.statistics') : undefined}
|
||||||
>
|
>
|
||||||
<BarChart3 size={isCollapsed ? 22 : 18} />
|
<BarChart3 size={isCollapsed ? 22 : 18} />
|
||||||
|
|||||||
+8
-2
@@ -18,7 +18,10 @@ const enTranslation = {
|
|||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
help: 'Help',
|
help: 'Help',
|
||||||
expand: 'Expand Sidebar',
|
expand: 'Expand Sidebar',
|
||||||
collapse: 'Collapse Sidebar'
|
collapse: 'Collapse Sidebar',
|
||||||
|
updateAvailable: 'Update available',
|
||||||
|
updateReady: '{{version}} is ready',
|
||||||
|
updateLink: 'Go to release →'
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
starred: 'Personal Favorites',
|
starred: 'Personal Favorites',
|
||||||
@@ -372,7 +375,10 @@ const deTranslation = {
|
|||||||
settings: 'Einstellungen',
|
settings: 'Einstellungen',
|
||||||
help: 'Hilfe',
|
help: 'Hilfe',
|
||||||
expand: 'Sidebar einblenden',
|
expand: 'Sidebar einblenden',
|
||||||
collapse: 'Sidebar ausblenden'
|
collapse: 'Sidebar ausblenden',
|
||||||
|
updateAvailable: 'Update verfügbar',
|
||||||
|
updateReady: '{{version}} ist bereit',
|
||||||
|
updateLink: 'Zum Release →'
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
starred: 'Persönliche Favoriten',
|
starred: 'Persönliche Favoriten',
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ export default function Settings() {
|
|||||||
Psysonic
|
Psysonic
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
|
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
|
||||||
{t('settings.aboutVersion')} 1.0.6
|
{t('settings.aboutVersion')} 1.0.7
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -164,6 +164,70 @@
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── Update Toast ─── */
|
||||||
|
@keyframes update-toast-in {
|
||||||
|
from { opacity: 0; transform: translateY(6px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast {
|
||||||
|
margin: 0 var(--space-1) var(--space-2);
|
||||||
|
padding: var(--space-3) var(--space-3);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--accent-dim);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
animation: update-toast-in 0.35s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast-version {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
padding-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast-link {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
padding-left: 22px;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast-link:hover {
|
||||||
|
opacity: 1;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-toast-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--space-2) 0;
|
||||||
|
color: var(--accent);
|
||||||
|
animation: update-toast-in 0.35s ease both;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
/* ─── Main Content ─── */
|
/* ─── Main Content ─── */
|
||||||
.main-content {
|
.main-content {
|
||||||
grid-area: main;
|
grid-area: main;
|
||||||
|
|||||||
Reference in New Issue
Block a user