77 lines
2.9 KiB
TypeScript
77 lines
2.9 KiB
TypeScript
import type { Candidate, Item, ReleasePreview } from '../types.js'
|
|
|
|
export type ScanErrorKind = 'not_found' | 'no_discogs_token' | 'rate_limited' | 'server'
|
|
|
|
export type ScanState =
|
|
| { phase: 'scan' }
|
|
| { phase: 'looking'; code: string }
|
|
| { phase: 'candidates'; code: string; candidates: Candidate[] }
|
|
| {
|
|
phase: 'confirm'
|
|
code: string | null
|
|
candidate: Candidate
|
|
preview: ReleasePreview | null
|
|
matchAlbumId: number | null
|
|
adding: boolean
|
|
addError: string | null
|
|
}
|
|
| { phase: 'added'; item: Item }
|
|
| { phase: 'error'; kind: ScanErrorKind; code: string | null }
|
|
|
|
export const INITIAL_SCAN_STATE: ScanState = { phase: 'scan' }
|
|
|
|
export type ScanAction =
|
|
| { type: 'DETECT'; code: string }
|
|
| { type: 'CANDIDATES'; code: string; candidates: Candidate[] }
|
|
| { type: 'NOT_FOUND'; code: string }
|
|
| { type: 'ERROR'; kind: ScanErrorKind; code: string | null }
|
|
| { type: 'SELECT'; candidate: Candidate }
|
|
| { type: 'PREVIEW'; preview: ReleasePreview }
|
|
| { type: 'SET_MATCH'; albumId: number }
|
|
| { type: 'CLEAR_MATCH' }
|
|
| { type: 'ADD_START' }
|
|
| { type: 'ADDED'; item: Item }
|
|
| { type: 'ADD_ERROR'; message: string }
|
|
| { type: 'RESET' }
|
|
|
|
export function scanReducer(state: ScanState, action: ScanAction): ScanState {
|
|
switch (action.type) {
|
|
case 'DETECT':
|
|
return state.phase === 'scan' ? { phase: 'looking', code: action.code } : state
|
|
case 'CANDIDATES':
|
|
return state.phase === 'looking' ? { phase: 'candidates', code: action.code, candidates: action.candidates } : state
|
|
case 'NOT_FOUND':
|
|
return state.phase === 'looking' ? { phase: 'error', kind: 'not_found', code: action.code } : state
|
|
case 'ERROR':
|
|
return state.phase === 'looking' || state.phase === 'candidates' || state.phase === 'confirm'
|
|
? { phase: 'error', kind: action.kind, code: action.code }
|
|
: state
|
|
case 'SELECT':
|
|
return state.phase === 'candidates'
|
|
? {
|
|
phase: 'confirm',
|
|
code: state.code,
|
|
candidate: action.candidate,
|
|
preview: null,
|
|
matchAlbumId: null,
|
|
adding: false,
|
|
addError: null,
|
|
}
|
|
: state
|
|
case 'PREVIEW':
|
|
return state.phase === 'confirm' ? { ...state, preview: action.preview } : state
|
|
case 'SET_MATCH':
|
|
return state.phase === 'confirm' ? { ...state, matchAlbumId: action.albumId } : state
|
|
case 'CLEAR_MATCH':
|
|
return state.phase === 'confirm' ? { ...state, matchAlbumId: null } : state
|
|
case 'ADD_START':
|
|
return state.phase === 'confirm' && state.preview ? { ...state, adding: true, addError: null } : state
|
|
case 'ADDED':
|
|
return state.phase === 'confirm' ? { phase: 'added', item: action.item } : state
|
|
case 'ADD_ERROR':
|
|
return state.phase === 'confirm' ? { ...state, adding: false, addError: action.message } : state
|
|
case 'RESET':
|
|
return INITIAL_SCAN_STATE
|
|
}
|
|
}
|