feat: separate saved-photo lifecycle from the confirmed log (#36)
Quality gates / quality (pull_request) Successful in 3m9s
Quality gates / quality (pull_request) Successful in 3m9s
A confirmed log and the image attached to it are separate things the person owns. The journal now reports a saved photo as a label instead of a thumbnail, reveals the image only on explicit request, removes the photo without touching any confirmed field, and deletes a log through a separate two-step decision that takes its photo with it. Delete-all is regression tested to remove the ledger and every stored photo together. - src/photo-lifecycle.js: presence decided by stored image bytes; withoutPhoto/withoutEntry return new lists and never mutate the ledger - app.js: journal rows expose Open-entry controls; entry sheet states presence, offers explicit reveal, remove-photo-keeps-log, and a two-step log delete; privacy copy names photo removal and delete-all - tests: 6 unit cases + browser acceptance (presence, no default thumbnail, explicit reveal, remove keeps log, two-step delete, delete-all leaves no image bytes, Escape dismiss, 44px targets) - record_release_demo.mjs: on-camera photo lifecycle scenes driven by a synthetic 1x1 PNG, with delete-all actually performed and asserted; build_release gates the new captions - CI/release/syntax gates include the new suite Closes #36
This commit is contained in:
1 parent
fe5c3d40c0
commit
ebee52fd8a
11 files changed
+366
-10
No files matched your search
@@ -0,0 +1,26 @@
|
||||
// Photo lifecycle for saved entries. A confirmed log and the image attached to it are separate
|
||||
// things a person owns: presence is reported from the stored image bytes, removing the image never
|
||||
// touches the log, and deleting the log is a separate, explicit decision.
|
||||
const IMAGE_DATA_URL = /^data:image\/[a-z0-9.+-]+(?:;base64)?,[A-Za-z0-9+/=%.-]+$/i;
|
||||
|
||||
export function photoState(entry) {
|
||||
return IMAGE_DATA_URL.test(String(entry?.photoDataUrl || ''))
|
||||
? { hasPhoto: true, label: 'Photo saved' }
|
||||
: { hasPhoto: false, label: '' };
|
||||
}
|
||||
|
||||
export function withoutPhoto(entries, id) {
|
||||
if (!Array.isArray(entries)) return [];
|
||||
if (!entries.some(entry => entry.id === id)) return entries;
|
||||
return entries.map(entry => (entry.id === id ? { ...entry, photoDataUrl: '' } : entry));
|
||||
}
|
||||
|
||||
export function withoutEntry(entries, id) {
|
||||
if (!Array.isArray(entries)) return [];
|
||||
if (!entries.some(entry => entry.id === id)) return entries;
|
||||
return entries.filter(entry => entry.id !== id);
|
||||
}
|
||||
|
||||
export function photoCount(entries) {
|
||||
return Array.isArray(entries) ? entries.filter(entry => photoState(entry).hasPhoto).length : 0;
|
||||
}
|
||||
Reference in new issue
Block a user