Add study data client adapter
This commit is contained in:
+102
-36
@@ -1,6 +1,7 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import JSZip from 'jszip'
|
||||
import { buildVerseSearchIndex } from './searchIndex'
|
||||
import type { StudyDataClient, TranslationNotesByVerse } from './studyDataClient'
|
||||
import type {
|
||||
AudioBibleContext,
|
||||
AudioBibleSegment,
|
||||
@@ -352,9 +353,7 @@ function allBrowserTags() {
|
||||
return [...unique.values()].sort((a, b) => a.label.localeCompare(b.label))
|
||||
}
|
||||
|
||||
export async function getTranslations(): Promise<TranslationResource[]> {
|
||||
if (inTauri()) return []
|
||||
|
||||
async function getBrowserTranslations(): Promise<TranslationResource[]> {
|
||||
const catalog = await loadBrowserCatalog()
|
||||
return catalog.resources
|
||||
.filter((resource) => resource.resource_type === 'translation')
|
||||
@@ -369,22 +368,18 @@ export async function getTranslations(): Promise<TranslationResource[]> {
|
||||
}))
|
||||
}
|
||||
|
||||
export async function getLibraryState(translationId = defaultTranslationId): Promise<LibraryState> {
|
||||
if (inTauri()) {
|
||||
return invoke<LibraryState>('get_library_state')
|
||||
}
|
||||
|
||||
async function getBrowserLibraryState(translationId = defaultTranslationId): Promise<LibraryState> {
|
||||
return {
|
||||
verses: await loadBrowserVersesForTranslation(translationId),
|
||||
tags: allBrowserTags(),
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTranslationNotesForVerses(
|
||||
async function getBrowserTranslationNotesForVerses(
|
||||
translationId: string,
|
||||
verses: Verse[],
|
||||
): Promise<Map<string, { footnotes: Footnote[]; studyNotes: Footnote[] }>> {
|
||||
if (inTauri() || verses.length === 0) return new Map()
|
||||
): Promise<TranslationNotesByVerse> {
|
||||
if (verses.length === 0) return new Map()
|
||||
|
||||
const chapterKeys = [
|
||||
...new Set(
|
||||
@@ -411,25 +406,17 @@ export async function getTranslationNotesForVerses(
|
||||
return notesByVerse
|
||||
}
|
||||
|
||||
export async function searchVerses(query: string, translationId = defaultTranslationId): Promise<Verse[]> {
|
||||
if (inTauri()) {
|
||||
return invoke<Verse[]>('search_verses', { query })
|
||||
}
|
||||
|
||||
async function searchBrowserVerses(query: string, translationId = defaultTranslationId): Promise<Verse[]> {
|
||||
const searchIndex = await loadBrowserSearchIndex(translationId)
|
||||
return searchIndex.search(query)
|
||||
}
|
||||
|
||||
export async function addTagToVerse(
|
||||
async function addBrowserTagToVerse(
|
||||
verseId: number,
|
||||
label: string,
|
||||
color: string,
|
||||
translationId = defaultTranslationId,
|
||||
) {
|
||||
if (inTauri()) {
|
||||
return invoke<Verse>('add_tag_to_verse', { verseId, label, color })
|
||||
}
|
||||
|
||||
const tagsByVerse = readBrowserTags()
|
||||
const current = tagsByVerse[verseId] ?? []
|
||||
const existing = current.find((tag) => tag.label.toLowerCase() === label.toLowerCase())
|
||||
@@ -443,9 +430,7 @@ export async function addTagToVerse(
|
||||
return verse
|
||||
}
|
||||
|
||||
export async function getCommentaryForVerse(verse: Verse): Promise<CommentaryEntry[]> {
|
||||
if (inTauri()) return []
|
||||
|
||||
async function getBrowserCommentaryForVerse(verse: Verse): Promise<CommentaryEntry[]> {
|
||||
const commentary = await loadBrowserCommentary()
|
||||
const chapterEntries = commentary.get(commentaryKey(verse.book, verse.chapter)) ?? []
|
||||
return chapterEntries.filter(
|
||||
@@ -453,9 +438,7 @@ export async function getCommentaryForVerse(verse: Verse): Promise<CommentaryEnt
|
||||
)
|
||||
}
|
||||
|
||||
export async function getDictionaryEntriesForTerms(terms: string[]): Promise<DictionaryEntry[]> {
|
||||
if (inTauri()) return []
|
||||
|
||||
async function getBrowserDictionaryEntriesForTerms(terms: string[]): Promise<DictionaryEntry[]> {
|
||||
const dictionary = await loadBrowserDictionary()
|
||||
const seen = new Set<string>()
|
||||
const entries: DictionaryEntry[] = []
|
||||
@@ -472,14 +455,10 @@ export async function getDictionaryEntriesForTerms(terms: string[]): Promise<Dic
|
||||
return entries
|
||||
}
|
||||
|
||||
export async function getMapContextForVerses(
|
||||
async function getBrowserMapContextForVerses(
|
||||
selectedVerse: Verse,
|
||||
pageVerses: Verse[],
|
||||
): Promise<MapContext> {
|
||||
if (inTauri()) {
|
||||
return { selectedPlaces: [], pagePlaces: [], relatedRoutes: [] }
|
||||
}
|
||||
|
||||
const versePlaces = await loadBrowserVersePlaces()
|
||||
const selectedVerseId = browserVerseKey(selectedVerse)
|
||||
const pageVerseIds = new Set(pageVerses.map((verse) => browserVerseKey(verse)).filter(Boolean))
|
||||
@@ -496,12 +475,10 @@ export async function getMapContextForVerses(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAudioContextForVerse(
|
||||
async function getBrowserAudioContextForVerse(
|
||||
translationId: string,
|
||||
verse: Verse,
|
||||
): Promise<AudioBibleContext | null> {
|
||||
if (inTauri()) return null
|
||||
|
||||
const catalog = await loadBrowserCatalog()
|
||||
const resource = catalog.resources.find(
|
||||
(item) =>
|
||||
@@ -547,7 +524,7 @@ export async function getAudioContextForVerse(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAudioObjectUrl(segment: AudioBibleSegment): Promise<string> {
|
||||
async function getBrowserAudioObjectUrl(segment: AudioBibleSegment): Promise<string> {
|
||||
const cacheKey = `${segment.audioZipUrl}#${segment.audioEntryPath}`
|
||||
if (!browserAudioObjectUrlCache.has(cacheKey)) {
|
||||
browserAudioObjectUrlCache.set(
|
||||
@@ -566,6 +543,95 @@ export async function getAudioObjectUrl(segment: AudioBibleSegment): Promise<str
|
||||
return browserAudioObjectUrlCache.get(cacheKey)!
|
||||
}
|
||||
|
||||
const emptyMapContext: MapContext = {
|
||||
selectedPlaces: [],
|
||||
pagePlaces: [],
|
||||
relatedRoutes: [],
|
||||
}
|
||||
|
||||
const browserStudyDataClient: StudyDataClient = {
|
||||
getTranslations: getBrowserTranslations,
|
||||
getLibraryState: getBrowserLibraryState,
|
||||
getTranslationNotesForVerses: getBrowserTranslationNotesForVerses,
|
||||
searchVerses: searchBrowserVerses,
|
||||
addTagToVerse: addBrowserTagToVerse,
|
||||
getCommentaryForVerse: getBrowserCommentaryForVerse,
|
||||
getDictionaryEntriesForTerms: getBrowserDictionaryEntriesForTerms,
|
||||
getMapContextForVerses: getBrowserMapContextForVerses,
|
||||
getAudioContextForVerse: getBrowserAudioContextForVerse,
|
||||
getAudioObjectUrl: getBrowserAudioObjectUrl,
|
||||
}
|
||||
|
||||
const tauriStudyDataClient: StudyDataClient = {
|
||||
getTranslations: async () => [],
|
||||
getLibraryState: () => invoke<LibraryState>('get_library_state'),
|
||||
getTranslationNotesForVerses: async () => new Map(),
|
||||
searchVerses: (query) => invoke<Verse[]>('search_verses', { query }),
|
||||
addTagToVerse: (verseId, label, color) => invoke<Verse>('add_tag_to_verse', { verseId, label, color }),
|
||||
getCommentaryForVerse: async () => [],
|
||||
getDictionaryEntriesForTerms: async () => [],
|
||||
getMapContextForVerses: async () => emptyMapContext,
|
||||
getAudioContextForVerse: async () => null,
|
||||
getAudioObjectUrl: async () => {
|
||||
throw new Error('Audio object URLs are not available in the Tauri data adapter yet.')
|
||||
},
|
||||
}
|
||||
|
||||
export function getStudyDataClient(): StudyDataClient {
|
||||
return inTauri() ? tauriStudyDataClient : browserStudyDataClient
|
||||
}
|
||||
|
||||
export function getTranslations(): Promise<TranslationResource[]> {
|
||||
return getStudyDataClient().getTranslations()
|
||||
}
|
||||
|
||||
export function getLibraryState(translationId = defaultTranslationId): Promise<LibraryState> {
|
||||
return getStudyDataClient().getLibraryState(translationId)
|
||||
}
|
||||
|
||||
export function getTranslationNotesForVerses(
|
||||
translationId: string,
|
||||
verses: Verse[],
|
||||
): Promise<TranslationNotesByVerse> {
|
||||
return getStudyDataClient().getTranslationNotesForVerses(translationId, verses)
|
||||
}
|
||||
|
||||
export function searchVerses(query: string, translationId = defaultTranslationId): Promise<Verse[]> {
|
||||
return getStudyDataClient().searchVerses(query, translationId)
|
||||
}
|
||||
|
||||
export function addTagToVerse(
|
||||
verseId: number,
|
||||
label: string,
|
||||
color: string,
|
||||
translationId = defaultTranslationId,
|
||||
) {
|
||||
return getStudyDataClient().addTagToVerse(verseId, label, color, translationId)
|
||||
}
|
||||
|
||||
export function getCommentaryForVerse(verse: Verse): Promise<CommentaryEntry[]> {
|
||||
return getStudyDataClient().getCommentaryForVerse(verse)
|
||||
}
|
||||
|
||||
export function getDictionaryEntriesForTerms(terms: string[]): Promise<DictionaryEntry[]> {
|
||||
return getStudyDataClient().getDictionaryEntriesForTerms(terms)
|
||||
}
|
||||
|
||||
export function getMapContextForVerses(selectedVerse: Verse, pageVerses: Verse[]): Promise<MapContext> {
|
||||
return getStudyDataClient().getMapContextForVerses(selectedVerse, pageVerses)
|
||||
}
|
||||
|
||||
export function getAudioContextForVerse(
|
||||
translationId: string,
|
||||
verse: Verse,
|
||||
): Promise<AudioBibleContext | null> {
|
||||
return getStudyDataClient().getAudioContextForVerse(translationId, verse)
|
||||
}
|
||||
|
||||
export function getAudioObjectUrl(segment: AudioBibleSegment): Promise<string> {
|
||||
return getStudyDataClient().getAudioObjectUrl(segment)
|
||||
}
|
||||
|
||||
async function loadBrowserCatalog(): Promise<ImportedCatalog> {
|
||||
browserCatalogCache ??= fetchJson<ImportedCatalog>(`${libreBibleDevRoot}/catalog.json`)
|
||||
return browserCatalogCache
|
||||
|
||||
Reference in New Issue
Block a user