release: 0.1.3 — periodic update check + sidebar 1/3/7/all groups
- updateStore: arm a 2-hour periodic checkForUpdates after the startup pass so users who keep the client open for days still see new releases without restarting. Idempotent, silent — only the top-right popup surface fires. - Sidebar: bucket session history into Past 24h / Past 3 days / Past 7 days / All older (was Today/Yesterday/7d/30d/Older). Rolling windows from `Date.now()` instead of calendar bounds. - i18n: replace `today/yesterday/last7days/last30days` keys with `within1day/within3days/within7days`; ScheduledTasksList and ScheduledTasksEmpty migrated to the new keys. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "heicode-desktop",
|
||||
"private": true,
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "heicode-desktop"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicegui/nicegui/main/nicegui/static/tauri-schema-v2.json",
|
||||
"productName": "HeiCode",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"identifier": "com.heicode.desktop",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
|
||||
@@ -17,9 +17,9 @@ import { useHeicodeAuthStore } from '../../stores/heicodeAuthStore'
|
||||
const isTauri = typeof window !== 'undefined' && ('__TAURI_INTERNALS__' in window || '__TAURI__' in window)
|
||||
const isWindows = typeof navigator !== 'undefined' && /Win/.test(navigator.platform)
|
||||
|
||||
type TimeGroup = 'today' | 'yesterday' | 'last7days' | 'last30days' | 'older'
|
||||
type TimeGroup = 'within1day' | 'within3days' | 'within7days' | 'older'
|
||||
|
||||
const TIME_GROUP_ORDER: TimeGroup[] = ['today', 'yesterday', 'last7days', 'last30days', 'older']
|
||||
const TIME_GROUP_ORDER: TimeGroup[] = ['within1day', 'within3days', 'within7days', 'older']
|
||||
|
||||
export function Sidebar() {
|
||||
const sessions = useSessionStore((s) => s.sessions)
|
||||
@@ -131,10 +131,9 @@ export function Sidebar() {
|
||||
const t = useTranslation()
|
||||
|
||||
const timeGroupLabels: Record<TimeGroup, string> = {
|
||||
today: t('sidebar.timeGroup.today'),
|
||||
yesterday: t('sidebar.timeGroup.yesterday'),
|
||||
last7days: t('sidebar.timeGroup.last7days'),
|
||||
last30days: t('sidebar.timeGroup.last30days'),
|
||||
within1day: t('sidebar.timeGroup.within1day'),
|
||||
within3days: t('sidebar.timeGroup.within3days'),
|
||||
within7days: t('sidebar.timeGroup.within7days'),
|
||||
older: t('sidebar.timeGroup.older'),
|
||||
}
|
||||
|
||||
@@ -454,19 +453,17 @@ export function Sidebar() {
|
||||
|
||||
function groupByTime(sessions: SessionListItem[]): Map<TimeGroup, SessionListItem[]> {
|
||||
const groups = new Map<TimeGroup, SessionListItem[]>()
|
||||
const now = new Date()
|
||||
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
|
||||
const startOfYesterday = startOfToday - 86400000
|
||||
const sevenDaysAgo = startOfToday - 7 * 86400000
|
||||
const thirtyDaysAgo = startOfToday - 30 * 86400000
|
||||
const now = Date.now()
|
||||
const oneDayAgo = now - 86400000
|
||||
const threeDaysAgo = now - 3 * 86400000
|
||||
const sevenDaysAgo = now - 7 * 86400000
|
||||
|
||||
for (const session of sessions) {
|
||||
const ts = new Date(session.modifiedAt).getTime()
|
||||
let group: TimeGroup
|
||||
if (ts >= startOfToday) group = 'today'
|
||||
else if (ts >= startOfYesterday) group = 'yesterday'
|
||||
else if (ts >= sevenDaysAgo) group = 'last7days'
|
||||
else if (ts >= thirtyDaysAgo) group = 'last30days'
|
||||
if (ts >= oneDayAgo) group = 'within1day'
|
||||
else if (ts >= threeDaysAgo) group = 'within3days'
|
||||
else if (ts >= sevenDaysAgo) group = 'within7days'
|
||||
else group = 'older'
|
||||
|
||||
if (!groups.has(group)) groups.set(group, [])
|
||||
|
||||
@@ -31,11 +31,10 @@ export const en = {
|
||||
'sidebar.confirmDelete': 'Delete this session? This cannot be undone.',
|
||||
'sidebar.allProjects': 'All projects',
|
||||
'sidebar.other': 'Other',
|
||||
'sidebar.timeGroup.today': 'Today',
|
||||
'sidebar.timeGroup.yesterday': 'Yesterday',
|
||||
'sidebar.timeGroup.last7days': 'Last 7 days',
|
||||
'sidebar.timeGroup.last30days': 'Last 30 days',
|
||||
'sidebar.timeGroup.older': 'Older',
|
||||
'sidebar.timeGroup.within1day': 'Past 24 hours',
|
||||
'sidebar.timeGroup.within3days': 'Past 3 days',
|
||||
'sidebar.timeGroup.within7days': 'Past 7 days',
|
||||
'sidebar.timeGroup.older': 'All older',
|
||||
'sidebar.collapse': 'Collapse sidebar',
|
||||
'sidebar.expand': 'Expand sidebar',
|
||||
'sidebar.logout': 'Log out',
|
||||
|
||||
@@ -33,11 +33,10 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'sidebar.confirmDelete': '确定要删除这个会话吗?此操作不可撤销。',
|
||||
'sidebar.allProjects': '所有项目',
|
||||
'sidebar.other': '其他',
|
||||
'sidebar.timeGroup.today': '今天',
|
||||
'sidebar.timeGroup.yesterday': '昨天',
|
||||
'sidebar.timeGroup.last7days': '最近 7 天',
|
||||
'sidebar.timeGroup.last30days': '最近 30 天',
|
||||
'sidebar.timeGroup.older': '更早',
|
||||
'sidebar.timeGroup.within1day': '1 天内',
|
||||
'sidebar.timeGroup.within3days': '3 天内',
|
||||
'sidebar.timeGroup.within7days': '7 天内',
|
||||
'sidebar.timeGroup.older': '全部',
|
||||
'sidebar.collapse': '折叠侧边栏',
|
||||
'sidebar.expand': '展开侧边栏',
|
||||
'sidebar.logout': '退出登录',
|
||||
|
||||
@@ -29,11 +29,11 @@ export function ScheduledTasksEmpty() {
|
||||
</div>
|
||||
<div className="px-3 py-2 text-[var(--color-text-secondary)] hover:bg-[#EBEBE6] transition-all rounded-lg cursor-pointer duration-200 ease-in-out flex items-center gap-3">
|
||||
<span className="material-symbols-outlined">history</span>
|
||||
<span>{t('sidebar.timeGroup.today')}</span>
|
||||
<span>{t('sidebar.timeGroup.within1day')}</span>
|
||||
</div>
|
||||
<div className="px-3 py-2 text-[var(--color-text-secondary)] hover:bg-[#EBEBE6] transition-all rounded-lg cursor-pointer duration-200 ease-in-out flex items-center gap-3">
|
||||
<span className="material-symbols-outlined">event_note</span>
|
||||
<span>{t('sidebar.timeGroup.last7days')}</span>
|
||||
<span>{t('sidebar.timeGroup.within7days')}</span>
|
||||
</div>
|
||||
<div className="px-3 py-2 text-[var(--color-text-secondary)] hover:bg-[#EBEBE6] transition-all rounded-lg cursor-pointer duration-200 ease-in-out flex items-center gap-3">
|
||||
<span className="material-symbols-outlined">archive</span>
|
||||
|
||||
@@ -42,11 +42,11 @@ export function ScheduledTasksList() {
|
||||
</button>
|
||||
<button className="flex items-center gap-3 px-3 py-2 w-full text-[var(--color-text-secondary)] hover:bg-[#EBEBE6] transition-all rounded-lg font-medium text-sm duration-200 ease-in-out">
|
||||
<span className="material-symbols-outlined">history</span>
|
||||
{t('sidebar.timeGroup.today')}
|
||||
{t('sidebar.timeGroup.within1day')}
|
||||
</button>
|
||||
<button className="flex items-center gap-3 px-3 py-2 w-full text-[var(--color-text-secondary)] hover:bg-[#EBEBE6] transition-all rounded-lg font-medium text-sm duration-200 ease-in-out">
|
||||
<span className="material-symbols-outlined">event_note</span>
|
||||
{t('sidebar.timeGroup.last7days')}
|
||||
{t('sidebar.timeGroup.within7days')}
|
||||
</button>
|
||||
<button className="flex items-center gap-3 px-3 py-2 w-full text-[var(--color-text-secondary)] hover:bg-[#EBEBE6] transition-all rounded-lg font-medium text-sm duration-200 ease-in-out">
|
||||
<span className="material-symbols-outlined">archive</span>
|
||||
|
||||
@@ -35,6 +35,12 @@ type UpdateStore = {
|
||||
|
||||
let pendingUpdate: Update | null = null
|
||||
let startupCheckPromise: Promise<void> | null = null
|
||||
let periodicCheckTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
// How often the long-running client re-polls the updater manifest while
|
||||
// idle. 2 hours strikes a balance between "user sees a new release the
|
||||
// same day it ships" and "we don't hammer the blob endpoint".
|
||||
const PERIODIC_CHECK_INTERVAL_MS = 2 * 60 * 60 * 1000
|
||||
|
||||
function readDismissedUpdateVersion(): string | null {
|
||||
if (typeof window === 'undefined') return null
|
||||
@@ -100,6 +106,18 @@ export const useUpdateStore = create<UpdateStore>((set, get) => ({
|
||||
}
|
||||
|
||||
await startupCheckPromise
|
||||
|
||||
// After the startup check settles, arm a periodic re-check so users
|
||||
// who keep the app open for hours/days still see new releases without
|
||||
// having to restart. Idempotent — only one timer ever exists.
|
||||
if (!periodicCheckTimer) {
|
||||
periodicCheckTimer = setInterval(() => {
|
||||
// silent:true → background errors don't paint the red banner;
|
||||
// a successful hit still flips shouldPrompt and triggers the
|
||||
// top-right popup via the existing render path.
|
||||
void get().checkForUpdates({ silent: true })
|
||||
}, PERIODIC_CHECK_INTERVAL_MS)
|
||||
}
|
||||
},
|
||||
|
||||
checkForUpdates: async ({ silent = false } = {}) => {
|
||||
|
||||
Reference in New Issue
Block a user