Initial Libre Study app scaffold
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import type { LibraryState, Tag, Verse } from './types'
|
||||
|
||||
type TauriWindow = Window &
|
||||
typeof globalThis & {
|
||||
__TAURI_INTERNALS__?: unknown
|
||||
}
|
||||
|
||||
const browserTagKey = 'libre-study-browser-tags'
|
||||
|
||||
const browserVerses: Verse[] = [
|
||||
{
|
||||
id: 1,
|
||||
book: 'Genesis',
|
||||
chapter: 1,
|
||||
verse: 1,
|
||||
text: 'In the beginning God created the heaven and the earth.',
|
||||
tags: [],
|
||||
strongs: [
|
||||
{
|
||||
strong: 'H7225',
|
||||
lemma: 'רֵאשִׁית',
|
||||
transliteration: 'reshith',
|
||||
definition: 'Beginning, first, chief, the first part.',
|
||||
language: 'Hebrew',
|
||||
},
|
||||
{
|
||||
strong: 'H430',
|
||||
lemma: 'אֱלֹהִים',
|
||||
transliteration: 'elohim',
|
||||
definition: 'God, gods, judges, rulers; plural form often used of the one true God.',
|
||||
language: 'Hebrew',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
book: 'Psalm',
|
||||
chapter: 23,
|
||||
verse: 1,
|
||||
text: 'The LORD is my shepherd; I shall not want.',
|
||||
tags: [],
|
||||
strongs: [
|
||||
{
|
||||
strong: 'H3068',
|
||||
lemma: 'יְהוָֹה',
|
||||
transliteration: 'YHWH',
|
||||
definition: 'The covenant name of the LORD.',
|
||||
language: 'Hebrew',
|
||||
},
|
||||
{
|
||||
strong: 'H7462',
|
||||
lemma: 'רָעָה',
|
||||
transliteration: 'raah',
|
||||
definition: 'To shepherd, pasture, feed, tend a flock.',
|
||||
language: 'Hebrew',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
book: 'John',
|
||||
chapter: 3,
|
||||
verse: 16,
|
||||
text: 'For God so loved the world, that he gave his only begotten Son, that whosoever believeth in him should not perish, but have everlasting life.',
|
||||
tags: [],
|
||||
strongs: [
|
||||
{
|
||||
strong: 'G25',
|
||||
lemma: 'ἀγαπάω',
|
||||
transliteration: 'agapao',
|
||||
definition: 'To love, value, welcome, or take pleasure in.',
|
||||
language: 'Greek',
|
||||
},
|
||||
{
|
||||
strong: 'G4100',
|
||||
lemma: 'πιστεύω',
|
||||
transliteration: 'pisteuo',
|
||||
definition: 'To believe, trust, have faith in.',
|
||||
language: 'Greek',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
book: 'Romans',
|
||||
chapter: 8,
|
||||
verse: 28,
|
||||
text: 'And we know that all things work together for good to them that love God, to them who are the called according to his purpose.',
|
||||
tags: [],
|
||||
strongs: [
|
||||
{
|
||||
strong: 'G2822',
|
||||
lemma: 'κλητός',
|
||||
transliteration: 'kletos',
|
||||
definition: 'Called, invited, appointed.',
|
||||
language: 'Greek',
|
||||
},
|
||||
{
|
||||
strong: 'G4286',
|
||||
lemma: 'πρόθεσις',
|
||||
transliteration: 'prothesis',
|
||||
definition: 'Purpose, setting forth, deliberate plan.',
|
||||
language: 'Greek',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
function inTauri() {
|
||||
return Boolean((window as TauriWindow).__TAURI_INTERNALS__)
|
||||
}
|
||||
|
||||
function readBrowserTags(): Record<number, Tag[]> {
|
||||
const raw = localStorage.getItem(browserTagKey)
|
||||
if (!raw) return {}
|
||||
|
||||
try {
|
||||
return JSON.parse(raw) as Record<number, Tag[]>
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function writeBrowserTags(tagsByVerse: Record<number, Tag[]>) {
|
||||
localStorage.setItem(browserTagKey, JSON.stringify(tagsByVerse))
|
||||
}
|
||||
|
||||
function hydrateBrowserVerses() {
|
||||
const tagsByVerse = readBrowserTags()
|
||||
return browserVerses.map((verse) => ({
|
||||
...verse,
|
||||
tags: tagsByVerse[verse.id] ?? [],
|
||||
}))
|
||||
}
|
||||
|
||||
function allBrowserTags() {
|
||||
const unique = new Map<string, Tag>()
|
||||
for (const tags of Object.values(readBrowserTags())) {
|
||||
for (const tag of tags) {
|
||||
unique.set(tag.label.toLowerCase(), tag)
|
||||
}
|
||||
}
|
||||
return [...unique.values()].sort((a, b) => a.label.localeCompare(b.label))
|
||||
}
|
||||
|
||||
export async function getLibraryState(): Promise<LibraryState> {
|
||||
if (inTauri()) {
|
||||
return invoke<LibraryState>('get_library_state')
|
||||
}
|
||||
|
||||
return {
|
||||
verses: hydrateBrowserVerses(),
|
||||
tags: allBrowserTags(),
|
||||
}
|
||||
}
|
||||
|
||||
export async function searchVerses(query: string): Promise<Verse[]> {
|
||||
if (inTauri()) {
|
||||
return invoke<Verse[]>('search_verses', { query })
|
||||
}
|
||||
|
||||
const trimmed = query.trim().toLowerCase()
|
||||
const verses = hydrateBrowserVerses()
|
||||
if (!trimmed) return verses
|
||||
|
||||
if (trimmed.startsWith('tag:')) {
|
||||
const label = trimmed.slice(4)
|
||||
return verses.filter((verse) =>
|
||||
verse.tags.some((tag) => tag.label.toLowerCase() === label),
|
||||
)
|
||||
}
|
||||
|
||||
if (/^[gh]\d+$/.test(trimmed)) {
|
||||
return verses.filter((verse) =>
|
||||
verse.strongs.some((entry) => entry.strong.toLowerCase() === trimmed),
|
||||
)
|
||||
}
|
||||
|
||||
return verses.filter((verse) =>
|
||||
`${verse.book} ${verse.chapter}:${verse.verse} ${verse.text}`
|
||||
.toLowerCase()
|
||||
.includes(trimmed),
|
||||
)
|
||||
}
|
||||
|
||||
export async function addTagToVerse(verseId: number, label: string, color: string) {
|
||||
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())
|
||||
const tag = existing ?? { id: Date.now(), label, color }
|
||||
tagsByVerse[verseId] = existing ? current : [...current, tag]
|
||||
writeBrowserTags(tagsByVerse)
|
||||
|
||||
const verse = hydrateBrowserVerses().find((item) => item.id === verseId)
|
||||
if (!verse) throw new Error(`Verse ${verseId} was not found.`)
|
||||
return verse
|
||||
}
|
||||
Reference in New Issue
Block a user