diff --git a/notes.js b/notes.js
index e7e5ecb..4393147 100644
--- a/notes.js
+++ b/notes.js
@@ -66,5 +66,5 @@ function search(me, q) {
return out.slice(0, 8);
}
/** Notes and lists shared with the whole family, for the family board. */
-const familyNotes = () => all().filter(n => n.share === 'family' && !n.archived).sort((a, b) => (b.pinned - a.pinned) || b.updated - a.updated).slice(0, 20).map(n => ({ id: n.id, type: n.type, title: n.title || (n.type === 'list' ? 'Checklist' : 'Note'), pinned: n.pinned, total: n.items.length, done: n.items.filter(i => i.done).length, next: (n.items.find(i => !i.done) || {}).text || '', body: n.type === 'note' ? n.body.slice(0, 120) : '' }));
+const familyNotes = () => all().filter(n => n.share === 'family' && !n.archived).sort((a, b) => (b.pinned - a.pinned) || b.updated - a.updated).slice(0, 20).map(n => ({ id: n.id, type: n.type, title: n.title || (n.type === 'list' ? 'Checklist' : 'Note'), pinned: n.pinned, total: n.items.length, done: n.items.filter(i => i.done).length, next: (n.items.find(i => !i.done) || {}).text || '', items: n.type === 'list' ? n.items.slice(0, 80).map(i => ({ text: i.text, done: !!i.done, by: i.addedBy || '' })) : [], body: n.type === 'note' ? n.body.slice(0, 400) : '' }));
module.exports = { handle, search, familyNotes };
diff --git a/public/board.html b/public/board.html
index e8a43e1..b2f50a4 100644
--- a/public/board.html
+++ b/public/board.html
@@ -67,6 +67,7 @@
.err { color: var(--danger); min-height: 20px; }
/* touch screen: things scroll by dragging, with no scroll bars */
.card, .chl, #ovb div { scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; } .card::-webkit-scrollbar, .chl::-webkit-scrollbar, #ovb div::-webkit-scrollbar { display: none; }
+ .nt { margin-top: 6px; } .nti { padding: 3px 0; border: 0; font-size: 16px; } .nti .chk { width: 20px; height: 20px; border: 2px solid var(--line); border-radius: 6px; display: grid; place-items: center; flex: none; font-size: 13px; } .nti.dn .chk { background: var(--accent); border-color: var(--accent); color: #fff; } .nti.dn .g { text-decoration: line-through; opacity: .6; } .ntbody { margin: 2px 0 6px; color: var(--muted); white-space: pre-wrap; font-size: 15px; }
.pbtn { width: 34px; height: 34px; border-radius: 50%; background: var(--accent); color: var(--accent-t, #fff); border: 0; font-size: 24px; line-height: 1; cursor: pointer; flex: none; }
#ovb input[type=text], #ovb input[type=date], #ovb input[type=time], #ovb select { width: 100%; min-height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--line); background: var(--panel2); color: var(--text); font-size: 17px; }
.pill { display: inline-flex; align-items: center; gap: 6px; margin: 3px 6px 3px 0; padding: 6px 12px; border-radius: 16px; background: var(--panel2); border: 1px solid var(--line); color: var(--text); font: inherit; font-size: 15px; cursor: pointer; } .pill.pool { border-color: var(--accent); } .pill small { color: var(--muted); font-size: 12px; } .pill.more { background: none; border-style: dashed; color: var(--muted); cursor: default; }
diff --git a/public/board.js b/public/board.js
index 5df6d66..16a4ee8 100644
--- a/public/board.js
+++ b/public/board.js
@@ -26,7 +26,7 @@ function draw() {
+ card('Today and tomorrow', day('Today', t0) + day('Tomorrow', t1), 'event')
+ card('This week', weekCard(d.week)) + card('Shopping list', d.shopping.map(i => `
${esc(i.text)}${i.by ? `${esc(i.by)}` : ''}
`).join('') || none('Nothing to buy'), 'shopping')
+ card('Family tasks', d.tasks.map(t => `${esc(t.title)}${t.by ? ` ${esc(t.by)}` : ''}${t.due === t0 ? 'Today' : esc(t.due)}
`).join('') || none('Nothing due'), 'task')
- + card('Family notes and lists', d.notes.map(n => `${esc(n.title)}${n.type === 'list' ? ` ${n.done} of ${n.total} done${n.next ? ' · next: ' + esc(n.next) : ''}` : ` ${esc(n.body)}`}
`).join('') || none('No shared notes'), d.notes.some(n => n.type === 'list') ? 'item' : '');
+ + card('Family notes and lists', d.notes.map(n => `${esc(n.title)}${n.type === 'list' ? ` ${n.done} of ${n.total} done` : ''}
${n.type === 'list' ? n.items.map(i => `
${i.done ? '✓' : ''}${esc(i.text)}${i.by ? ` ${esc(i.by)}` : ''}
`).join('') || '
Empty
' : `
${esc(n.body)}
`}
`).join('') || none('No shared notes'), d.notes.some(n => n.type === 'list') ? 'item' : '');
wireChores();
}
function poolAll() {
diff --git a/public/chat-view.js b/public/chat-view.js
index b2e4dac..d94172d 100644
--- a/public/chat-view.js
+++ b/public/chat-view.js
@@ -83,7 +83,7 @@ function chBubble(m, c, prev) {
const reacts = {}; Object.values(m.react || {}).forEach(e => { reacts[e] = (reacts[e] || 0) + 1; });
const day = !prev || new Date(prev.ts).toDateString() !== new Date(m.ts).toDateString() ? `${new Date(m.ts).toLocaleDateString([], { weekday: 'long', month: 'long', day: 'numeric' })}
` : '';
if (m.sys) return `${day}${esc(m.text)}
`;
- const tick = m.out && m.status ? `${m.status === 'sent' ? '✓' : '✓✓'}` : '';
+ const tick = m.out && m.status ? `${m.status === 'sent' ? '✓' : '✓✓'}` : '';
return `${day}${m.fwd ? `
↪ Forwarded from ${esc(m.fwd.name)}` : ''}${c.type === 'group' && !m.out ? `
${esc(m.name)}` : ''}
${m.poll ? chPoll(m) : ''}${m.quote ? `
${esc(m.quote.name)}${esc(m.quote.text || 'Message')}
` : ''}${att}
${m.deleted ? '
This message was deleted' : m.text ? `
${chMention(sgLink(esc(m.text)).replace(/\n/g, '
'))}
` : ''}
diff --git a/public/signal-view.js b/public/signal-view.js
index 2e98215..c6362c8 100644
--- a/public/signal-view.js
+++ b/public/signal-view.js
@@ -104,7 +104,7 @@ function sgBubble(m, c, prev) {
return `${day}
${c.type === 'group' && !m.out ? `
${esc(m.name || nm(m.from))}` : ''}
${m.quote ? `
${esc(nm(m.quote.author))}${esc(m.quote.text || '')}
` : ''}${att}
${m.deleted ? '
This message was deleted' : m.text ? `
${sgLink(esc(m.text)).replace(/\n/g, '
')}
` : ''}
-
${m.edited ? 'edited · ' : ''}${new Date(m.ts).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}${tick ? ` ${tick}` : ''}
+
${m.edited ? 'edited · ' : ''}${new Date(m.ts).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}${tick ? ` ${tick}` : ''}
${Object.keys(reacts).length ? `
${Object.entries(reacts).map(([e, n]) => e + (n > 1 ? ' ' + n : '')).join(' ')}` : ''}
`;
}
async function sgDrawThread(first) {
diff --git a/public/style.css b/public/style.css
index 87bf6bc..4c1e655 100644
--- a/public/style.css
+++ b/public/style.css
@@ -369,8 +369,8 @@ body:not(.editing) #view { padding-top: 8px; }
.sgb .who { display: block; font-weight: 700; font-size: 12px; margin-bottom: 2px; }
.sgt a { color: inherit; text-decoration: underline; }
.sgmeta { display: block; text-align: right; font-size: 11px; opacity: .7; margin-top: 2px; }
-.sgmeta .rd { color: #8be28b; font-weight: 700; }
-.sgb.out .sgmeta .rd { color: #fff; }
+.sgmeta .sgrd { color: #8be28b; font-weight: 700; }
+.sgb.out .sgmeta .sgrd { color: #fff; }
.sgimg { display: block; max-width: 100%; max-height: 320px; border-radius: 10px; margin-bottom: 4px; }
.sgb audio, .sgb video { max-width: 100%; display: block; margin-bottom: 4px; }
.sgfile { display: flex; align-items: center; gap: 8px; text-decoration: underline; margin-bottom: 4px; }
diff --git a/public/sw.js b/public/sw.js
index 6442389..ac7f04b 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -1,5 +1,5 @@
// App-shell cache only; Seafile/API traffic always goes to the network.
-const CACHE = 'hub-v83';
+const CACHE = 'hub-v84';
const SHARE = 'hub-share'; // files shared into the app from other apps, held until the person picks a folder
const SHELL = ['/icon.svg', '/manifest.webmanifest', '/icons/icon-192.png', '/icons/apple-touch-icon.png'];