Version history, notes on highlights and bookmarks, "you've studied this", prayer journal
Build and Push Docker Image / build-and-push (push) Successful in 5m13s
Build and Push Docker Image / build-and-push (push) Successful in 5m13s
- Earlier versions of a study: the server keeps compressed copies (about one per ten minutes of editing, the latest 30); Export shows what differs and puts one back (the replaced copy is kept) - Notes on highlights and bookmarks: synced, merged without loss, in exports and backups - Reader verse panel lists your studies and chunks that cover the verse, and opens them - Prayer journal: requests, who for, linked verses, days prayed, answers; synced, in backups and the account export; daily prayer reminder through ntfy - TODO.md lists the ideas not built yet Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54cb1c217d
commit
207bfcb919
45 files changed
+1669
-75
No files matched your search
@@ -15,7 +15,7 @@ test('a non-object body is rejected', () => {
|
||||
|
||||
test('good bookmarks and highlights pass through unchanged', () => {
|
||||
const body = { bookmarks: { 'JHN-3-16': '#fef08a', '1SA-3-5': '#bbf7d0' }, highlights: [hl()] };
|
||||
expect(cleanMarks(body)).toEqual(body);
|
||||
expect(cleanMarks(body)).toEqual({ ...body, bookmarkNotes: {} });
|
||||
});
|
||||
|
||||
test('malformed bookmarks are dropped, good ones kept', () => {
|
||||
@@ -38,7 +38,7 @@ test('highlights are validated field by field and de-duplicated by id', () => {
|
||||
});
|
||||
|
||||
test('missing sections become empty ones, and unknown fields are not stored', () => {
|
||||
expect(cleanMarks({})).toEqual({ bookmarks: {}, highlights: [] });
|
||||
expect(cleanMarks({})).toEqual({ bookmarks: {}, highlights: [], bookmarkNotes: {} });
|
||||
expect(cleanMarks({ highlights: [{ ...hl(), evil: '<script>' }] }).highlights[0]).not.toHaveProperty('evil');
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ test('the database keeps one row per user and updatedAt only moves forward', asy
|
||||
db.initDb();
|
||||
db.createUser({ id: 'u1', email: 'a@example.test', passwordHash: 'x' });
|
||||
db.createUser({ id: 'u2', email: 'b@example.test', passwordHash: 'x' });
|
||||
expect(db.getReaderMarks('u1')).toEqual({ bookmarks: {}, highlights: [], updatedAt: 0 });
|
||||
expect(db.getReaderMarks('u1')).toEqual({ bookmarks: {}, highlights: [], bookmarkNotes: {}, updatedAt: 0 });
|
||||
const t1 = db.setReaderMarks('u1', { bookmarks: { 'JHN-3-16': '#fef08a' }, highlights: [] });
|
||||
const t2 = db.setReaderMarks('u1', { bookmarks: {}, highlights: [hl()] });
|
||||
expect(t2).toBeGreaterThan(t1);
|
||||
@@ -70,3 +70,17 @@ test('a highlight keeps the translation it was made in and whether it covers the
|
||||
expect(out[1].translation).toBeUndefined();
|
||||
expect(out[1].whole).toBeUndefined();
|
||||
});
|
||||
|
||||
test('notes on highlights and bookmarks are kept, trimmed, and dropped when empty or malformed', async () => {
|
||||
const { cleanMarks } = await import('./readerMarks.js');
|
||||
const base = { book: 'JHN', chapter: 3, verse: 16, startOffset: 0, endOffset: 40, color: '#fef08a' };
|
||||
const out = cleanMarks({
|
||||
bookmarks: { 'JHN-3-16': '#fef08a' },
|
||||
bookmarkNotes: { 'JHN-3-16': 'Memorise this', 'JHN-3-17': ' ', 'bad key': 'x', 'ROM-8-1': 5 },
|
||||
highlights: [{ ...base, id: 'a', note: 'The heart of the gospel' }, { ...base, id: 'b', note: ' ' }, { ...base, id: 'c', note: 'x'.repeat(900) }],
|
||||
});
|
||||
expect(out.bookmarkNotes).toEqual({ 'JHN-3-16': 'Memorise this' });
|
||||
expect(out.highlights[0].note).toBe('The heart of the gospel');
|
||||
expect(out.highlights[1].note).toBeUndefined();
|
||||
expect(out.highlights[2].note).toHaveLength(500);
|
||||
});
|
||||
Reference in new issue
Block a user