Move manual sync to a single 'Update all' button in the header popover
The feed/channels 'Update' buttons triggered a global sync of all channels, which was misleading inside a specific category or page context. Now one 'Update all' button in the sync indicator popover runs subscriptions then videos sequentially, tolerates an already-running sync (409), and pages keep their finished_at-based cache invalidation.
This commit is contained in:
parent
5fa5a391d7
commit
0dc1336951
5 changed files with 94 additions and 19 deletions
|
|
@ -1,17 +1,35 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Link, NavLink, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { getSyncStatus, listCategories } from '../api/client'
|
||||
import { getSyncStatus, listCategories, syncSubscriptions, syncVideos } from '../api/client'
|
||||
import { formatRelativeTime } from '../utils/format'
|
||||
import Icon from './Icon'
|
||||
|
||||
function SyncIndicator() {
|
||||
const [open, setOpen] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const queryClient = useQueryClient()
|
||||
const { data } = useQuery({
|
||||
queryKey: ['sync-status'], queryFn: getSyncStatus,
|
||||
refetchInterval: (query) => query.state.data?.videos.running || query.state.data?.subscriptions.running ? 2000 : 60_000,
|
||||
})
|
||||
const syncAllMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const ignoreInProgress = (error: unknown) => error instanceof Error && error.message.includes('already in progress')
|
||||
try {
|
||||
await syncSubscriptions()
|
||||
} catch (error) {
|
||||
if (!ignoreInProgress(error)) throw error
|
||||
}
|
||||
try {
|
||||
await syncVideos()
|
||||
} catch (error) {
|
||||
if (!ignoreInProgress(error)) throw error
|
||||
}
|
||||
},
|
||||
onMutate: () => { queryClient.invalidateQueries({ queryKey: ['sync-status'] }) },
|
||||
onSettled: () => { queryClient.invalidateQueries({ queryKey: ['sync-status'] }) },
|
||||
})
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const close = (event: MouseEvent) => { if (!ref.current?.contains(event.target as Node)) setOpen(false) }
|
||||
|
|
@ -19,6 +37,7 @@ function SyncIndicator() {
|
|||
return () => document.removeEventListener('mousedown', close)
|
||||
}, [open])
|
||||
const running = data?.videos.running || data?.subscriptions.running
|
||||
const busy = syncAllMutation.isPending || running
|
||||
const failed = data?.videos.status === 'failed' || data?.subscriptions.status === 'failed'
|
||||
const recent = [data?.videos.finished_at, data?.subscriptions.finished_at].filter((value): value is string => Boolean(value)).sort().at(-1)
|
||||
const label = running ? 'Обновление...' : failed ? 'Ошибка синхронизации' : recent ? `Обновлено ${formatRelativeTime(recent)}` : 'Синхронизация'
|
||||
|
|
@ -27,11 +46,16 @@ function SyncIndicator() {
|
|||
<Icon name={failed ? 'alert' : running ? 'refresh' : 'check'} size={17} className={running ? 'spin' : ''} />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
{open && <div className="sync-popover" role="status">
|
||||
{open && <div className="sync-popover">
|
||||
<strong>Синхронизация</strong>
|
||||
<p>Подписки: {data?.subscriptions.running ? 'обновляются' : data?.subscriptions.finished_at ? formatRelativeTime(data.subscriptions.finished_at) : 'ещё не обновлялись'}</p>
|
||||
<p>Видео: {data?.videos.running ? 'обновляются' : data?.videos.finished_at ? formatRelativeTime(data.videos.finished_at) : 'ещё не обновлялись'}</p>
|
||||
{failed && <p className="error">Последнее обновление завершилось ошибкой. Подробности в настройках.</p>}
|
||||
<button type="button" className="button-secondary sync-all-button" onClick={() => syncAllMutation.mutate()} disabled={busy}>
|
||||
<Icon name="refresh" size={17} className={busy ? 'spin' : ''} />
|
||||
{busy ? 'Обновляем…' : 'Обновить всё'}
|
||||
</button>
|
||||
{syncAllMutation.isError && <p className="error" role="alert">Не удалось запустить обновление. Попробуйте позже.</p>}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue