50 lines
3.0 KiB
TypeScript
50 lines
3.0 KiB
TypeScript
import type { CaseState, DocumentExhibit, ExhibitRelation, Connection } from '../src/types.js'
|
|||
|
|
|
||
|
|
function requirementsMet(document: DocumentExhibit, earnedFlags: ReadonlySet<string>) {
|
||
|
|
return (document.requiredFlags || []).every(flag => earnedFlags.has(flag))
|
||
|
|
}
|
||
|
|
|
||
|
|
export function filterLevelVisibility(full: CaseState, earnedFlags: Iterable<string>, seenDocumentIds: Iterable<string>, authorMode: boolean): CaseState {
|
||
|
|
if (authorMode) return { ...full, newlyVisibleDocumentIds: [] }
|
||
|
|
const earned = new Set(earnedFlags)
|
||
|
|
const seen = new Set(seenDocumentIds)
|
||
|
|
const visibleExhibits = full.exhibits.filter(exhibit => !exhibit.hidden && (exhibit.type !== 'document' || requirementsMet(exhibit, earned)))
|
||
|
|
const visibleIds = new Set(visibleExhibits.map(exhibit => exhibit.id))
|
||
|
|
const sanitize = (exhibit: typeof visibleExhibits[number]) => {
|
||
|
|
if (exhibit.type !== 'document') return exhibit
|
||
|
|
const { requiredFlags: _requirements, ...document } = exhibit
|
||
|
|
return document
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
...full,
|
||
|
|
exhibits: visibleExhibits.map(sanitize),
|
||
|
|
relations: full.relations.filter(relation => visibleIds.has(relation.fromExhibitId) && visibleIds.has(relation.toExhibitId)),
|
||
|
|
connections: full.connections.filter(connection => visibleIds.has(connection.fromExhibitId) && visibleIds.has(connection.toExhibitId)),
|
||
|
|
newlyVisibleDocumentIds: visibleExhibits.flatMap(exhibit => exhibit.type === 'document' && !seen.has(exhibit.id) ? [exhibit.id] : []),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function appendMissingById<T extends { id: string }>(submitted: T[], preserved: T[]) {
|
||
|
|
const ids = new Set(submitted.map(item => item.id))
|
||
|
|
return [...submitted, ...preserved.filter(item => !ids.has(item.id))]
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Preserve server-hidden objects during the legacy whole-board PUT used by play mode. */
|
||
|
|
export function mergePlayerStateForPersistence(full: CaseState, visible: CaseState, submitted: CaseState): CaseState {
|
||
|
|
const visibleIds = new Set(visible.exhibits.map(exhibit => exhibit.id))
|
||
|
|
const unavailableIds = new Set(full.exhibits.filter(exhibit => !visibleIds.has(exhibit.id)).map(exhibit => exhibit.id))
|
||
|
|
const fullDocuments = new Map(full.exhibits.flatMap(exhibit => exhibit.type === 'document' ? [[exhibit.id, exhibit] as const] : []))
|
||
|
|
const submittedExhibits = submitted.exhibits.map(exhibit => exhibit.type === 'document'
|
||
|
|
? { ...exhibit, requiredFlags: fullDocuments.get(exhibit.id)?.requiredFlags || exhibit.requiredFlags || [] }
|
||
|
|
: exhibit)
|
||
|
|
const preservedExhibits = full.exhibits.filter(exhibit => unavailableIds.has(exhibit.id))
|
||
|
|
const touchesUnavailable = (item: ExhibitRelation | Connection) => unavailableIds.has(item.fromExhibitId) || unavailableIds.has(item.toExhibitId)
|
||
|
|
return {
|
||
|
|
...submitted,
|
||
|
|
exhibits: appendMissingById(submittedExhibits, preservedExhibits),
|
||
|
|
relations: appendMissingById(submitted.relations, full.relations.filter(touchesUnavailable)),
|
||
|
|
connections: appendMissingById(submitted.connections, full.connections.filter(touchesUnavailable)),
|
||
|
|
newlyVisibleDocumentIds: [],
|
||
|
|
}
|
||
|
|
}
|