src/book-open.ts (4409 bytes)
1 // The open of a book: the open attempts, the owner of subtitles that load 2 // during an open, the restore of the last book, the error of a failed 3 // attempt, and the meta data that comes after the open. Pure: no DOM. 4 5 import type { BookMeta } from './token-cache'; 6 7 /** 8 * The open attempts of books. Each attempt gets the next number: 1, 2, 3 ... 9 * A large PDF can load for many seconds, so the user can pick another book 10 * while one loads. The book of the last attempt that loads shows. 11 */ 12 export interface Opens { 13 /** The number of the last attempt. */ 14 tried: number; 15 /** The attempt of the book that shows, or 0. */ 16 shown: number; 17 /** The count of attempts that the user started: a file of the file picker. */ 18 byUser: number; 19 /** The attempts that still load. */ 20 loading: Set<number>; 21 } 22 23 export function createOpens(): Opens { 24 return { tried: 0, shown: 0, byUser: 0, loading: new Set() }; 25 } 26 27 /** Starts an attempt, and gives its number. `byUser` is true for a file that the user picked. */ 28 export function beginOpen(o: Opens, byUser: boolean): number { 29 const attempt = ++o.tried; 30 if (byUser) o.byUser++; 31 o.loading.add(attempt); 32 return attempt; 33 } 34 35 /** 36 * Ends an attempt. `loaded` is true when its file loaded as a PDF. Gives 37 * true when the book of the attempt shows now: it loaded, and no later 38 * attempt shows a book. 39 */ 40 export function endOpen(o: Opens, attempt: number, loaded: boolean): boolean { 41 o.loading.delete(attempt); 42 if (!loaded || attempt < o.shown) return false; 43 o.shown = attempt; 44 return true; 45 } 46 47 /** True while an attempt loads that can replace the book that shows: an attempt after it. */ 48 export function opening(o: Opens): boolean { 49 for (const attempt of o.loading) if (attempt > o.shown) return true; 50 return false; 51 } 52 53 /** 54 * The book of subtitles that the user loads now, when `book` shows. While an 55 * open loads that can replace `book`, the subtitles belong to no book yet: 56 * they are for the book that shows when the opens end, the new book or, when 57 * its open fails, `book`. Before, a subtitle file for the new book went into 58 * the meta data of the book before it, over its own subtitles, and the new 59 * book did not get it. 60 */ 61 export function subtitlesOwner(o: Opens, book: string | null): string | null { 62 return opening(o) ? null : book; 63 } 64 65 /** 66 * True when the user started an open after `since`, the value of o.byUser 67 * when the restore of the last book started. The restore waits for the 68 * database, and the first open after an upgrade can take many seconds. A 69 * book that the user picks meanwhile wins: the restore does not open the 70 * last book over it, also while it still loads, and does not forget the copy 71 * of the last book, because the copy can be of the user's book now. 72 */ 73 export function userOpened(o: Opens, since: number): boolean { 74 return o.byUser !== since; 75 } 76 77 /** 78 * True when the strip may tell why the attempt `attempt` failed: no later 79 * attempt shows a book. For the restore of the last book, `since` is 80 * o.byUser when the restore started, and the error shows only when the user 81 * started no open after that: the user's book tells its own state. Before, 82 * "This file is not a PDF, or it is damaged." of an older pick showed over 83 * the good book that the user picked after it. 84 */ 85 export function failureShows(o: Opens, attempt: number, since?: number): boolean { 86 return attempt > o.shown && (since === undefined || !userOpened(o, since)); 87 } 88 89 /** What changed after the open of a book, before its meta data came. */ 90 export interface Changes { 91 /** A page turn: by the user, or by the follow of the audio. */ 92 page: boolean; 93 /** A change of "Force OCR" by the user. */ 94 forceOcr: boolean; 95 } 96 97 /** 98 * The fields of the meta data of a book that apply when the meta data comes 99 * after the open: each field that did not change after the open. A field 100 * that is not in the result does not apply. Without a saved setting, "Force 101 * OCR" is off. The upgrade of the database can hold the meta data for many 102 * seconds, and before, it set "Force OCR" back after the user ticked it. 103 */ 104 export function lateMeta(meta: BookMeta, changed: Changes): { page?: number; forceOcr?: boolean } { 105 const late: { page?: number; forceOcr?: boolean } = {}; 106 if (!changed.page && meta.page !== undefined) late.page = meta.page; 107 if (!changed.forceOcr) late.forceOcr = meta.forceOcr ?? false; 108 return late; 109 }