Explain search outcomes and provide a direct version selection action
This commit is contained in:
@@ -5,7 +5,7 @@ import './latest-activity.css'
|
||||
import { lockBodyScroll } from '../../lib/scrollLock'
|
||||
|
||||
type Event = { id: string; service: string; state: string; message: string; started_at?: string; finished_at?: string }
|
||||
type Operation = { id: string; label: string; status: string; events: Event[] }
|
||||
type Operation = { summary?: { title: string; message: string; next: string; action?: string }; id: string; label: string; status: string; events: Event[] }
|
||||
|
||||
export default function LatestActivity({ operation, besideDownload, onDismiss }: {
|
||||
operation: Operation; besideDownload: boolean; onDismiss: () => void
|
||||
@@ -21,8 +21,8 @@ export default function LatestActivity({ operation, besideDownload, onDismiss }:
|
||||
const choosing = /[1-9]\d* releases? (found|shown)/i.test(message)
|
||||
const sent = operation.status === 'complete' && /sent|accepted.*release/i.test(message) && /download|release|Sonarr|Radarr/i.test(message)
|
||||
const interrupted = latest?.id === 'connection-error'
|
||||
const status = working ? 'Working on it' : choosing ? 'Choose a download' : operation.status === 'complete' ? 'Done' : 'Needs your attention'
|
||||
const currentStep = working
|
||||
const status = operation.summary?.title ?? (working ? 'Working on it' : choosing ? 'Choose a download' : operation.status === 'complete' ? 'Done' : 'Needs your attention')
|
||||
const currentStep = operation.summary?.message ?? (working
|
||||
? /send release/i.test(operation.label) ? 'Sending your download...'
|
||||
: /search/i.test(operation.label) ? 'Looking for a download...'
|
||||
: latest?.service === 'Jellyfin' ? 'Checking if it is ready to watch...'
|
||||
@@ -32,13 +32,13 @@ export default function LatestActivity({ operation, besideDownload, onDismiss }:
|
||||
: choosing ? 'The search is finished. Choose a version to download.'
|
||||
: sent ? 'Your download has been sent.'
|
||||
: operation.status === 'error' ? 'We could not finish this step.'
|
||||
: 'This check is finished.'
|
||||
const nextStep = working ? 'Please wait. You can close this box while we work.'
|
||||
: 'This check is finished.')
|
||||
const nextStep = operation.summary?.next ?? (working ? 'Please wait. You can close this box while we work.'
|
||||
: interrupted ? 'Close this box and check the request before trying again.'
|
||||
: choosing ? 'Close this box to see the available downloads.'
|
||||
: sent ? 'You can close this box. The request will update when the download starts.'
|
||||
: operation.status === 'error' ? 'Close this box to review the request and its available options.'
|
||||
: 'Close this box to see the updated request status.'
|
||||
: 'Close this box to see the updated request status.')
|
||||
const progress = <div className={`activity-process is-${working ? 'working' : operation.status}`} role="progressbar" aria-label={currentStep} aria-valuemin={0} aria-valuemax={100} aria-valuenow={working || operation.status === 'error' ? undefined : 100}><span /></div>
|
||||
|
||||
useEffect(() => {
|
||||
@@ -72,7 +72,7 @@ export default function LatestActivity({ operation, besideDownload, onDismiss }:
|
||||
{progress}
|
||||
<p className="activity-next-step">{nextStep}</p>
|
||||
</div>
|
||||
{!working && <footer><button type="button" onClick={() => { setOpen(false); onDismiss() }}>Dismiss activity</button></footer>}
|
||||
{!working && <footer><button type="button" onClick={() => { setOpen(false); onDismiss() }}>{operation.summary?.action ?? 'Dismiss activity'}</button></footer>}
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
@@ -148,6 +148,7 @@ type OperationEvent = {
|
||||
}
|
||||
|
||||
type OperationProgress = {
|
||||
summary?: { title: string; message: string; next: string; action?: string }
|
||||
id: string
|
||||
label: string
|
||||
status: 'running' | 'complete' | 'error' | string
|
||||
@@ -601,8 +602,22 @@ export default function RequestTimelinePage() {
|
||||
try { result = await response.clone().json() } catch { /* Non-JSON error is handled below. */ }
|
||||
const needsAttention = !response.ok || result?.status === 'attention' || result?.outcome === 'attention'
|
||||
const finalState = needsAttention ? 'error' : result?.status === 'searching' ? 'searching' : 'complete'
|
||||
const interactiveSearch = /\/actions\/search(?:\?|$)/.test(url)
|
||||
const items = Array.isArray(result?.releases) ? result.releases : []
|
||||
const available = items.some((item: ReleaseOption) => item.selectable && !item.requiresOverride)
|
||||
const outside = items.some((item: ReleaseOption) => item.selectable && item.requiresOverride)
|
||||
const canChoose = available || (outside && result?.canIgnoreProfileLimits === true)
|
||||
const summary = interactiveSearch
|
||||
? !response.ok
|
||||
? { title: 'Search could not finish', message: 'We could not complete the search.', next: 'Try again shortly. If it keeps happening, contact an admin.' }
|
||||
: canChoose
|
||||
? { title: available ? 'Downloads found' : 'Other versions are available', message: available ? 'Choose the version you want to download.' : 'These versions are outside your usual download settings.', next: available ? 'Your download starts after you choose a version.' : 'You can review them and confirm a download outside your profile.', action: 'Choose a version' }
|
||||
: { title: items.length ? 'No suitable downloads' : 'Nothing available yet', message: items.length ? 'The versions found cannot be downloaded with your current settings.' : 'No downloads were found in this search.', next: result?.nextOffset != null ? 'You can check the next group of missing episodes.' : 'You can try again later.', action: result?.nextOffset != null ? 'View search results' : undefined }
|
||||
: response.ok && /\/actions\/grab$/.test(url)
|
||||
? { title: 'Waiting to start', message: 'Your download has been sent.', next: 'Close this box to follow its progress. It may take a moment to start.' }
|
||||
: undefined
|
||||
setOperationProgress((current) => current?.id === operationId ? {
|
||||
...current, status: finalState,
|
||||
...current, status: finalState, summary,
|
||||
events: [...current.events.map(event => event.state === 'active' ? { ...event, state: 'complete' as const } : event), { id: 'result', service: 'Magent', state: needsAttention ? 'error' : 'complete',
|
||||
message: result?.message || (typeof result?.detail === 'string' ? result.detail : response.ok ? 'Action completed. The pipeline will update as the media services report progress.' : 'The action failed. Recheck the request before trying again.') }],
|
||||
} : current)
|
||||
|
||||
Reference in New Issue
Block a user