Family Chat (2.0): a built-in messenger for the family, phase 1
build-container / build (push) Successful in 7m9s
build-container / build (push) Successful in 7m9s
- Chat tab: direct chats and groups, text, attachments, replies, reactions, edit and delete, unread counts, search, drafts, mute and archive - Live delivery over server-sent events; phone push for people who are not in the app (muted chats stay quiet) - chat.js keeps conversations and files in data/chat; push.notify sends one person a notification - Roadmap updated: phase 1 done, combined inbox with Signal still to do Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
74592c25a5
commit
8283d10b41
14 files changed
+407
-27
No files matched your search
@@ -23,7 +23,7 @@ function keys() {
|
||||
return vapid;
|
||||
}
|
||||
|
||||
const DEFAULTS = { mail: true, mailSeen: null, tasks: true, tasksAt: '08:00', events: true, eventMins: 15, tz: 'UTC', subs: [], sent: [], tasksDay: '' };
|
||||
const DEFAULTS = { chat: true, mail: true, mailSeen: null, tasks: true, tasksAt: '08:00', events: true, eventMins: 15, tz: 'UTC', subs: [], sent: [], tasksDay: '' };
|
||||
const file = dir => path.join(dir, 'push.json');
|
||||
const load = dir => { try { return { ...DEFAULTS, ...JSON.parse(fs.readFileSync(file(dir), 'utf8')) }; } catch { return { ...DEFAULTS, subs: [], sent: [] }; } };
|
||||
const save = (dir, o) => atomicWrite(file(dir), JSON.stringify(o));
|
||||
@@ -113,7 +113,7 @@ function start() { if (!timer) { timer = setInterval(() => tick().catch(e => con
|
||||
|
||||
async function handle(req, res) {
|
||||
const p = new URL(req.url, 'http://x').pathname, dir = users.dir(), st = load(dir);
|
||||
const pub = () => ({ mail: st.mail, tasks: st.tasks, tasksAt: st.tasksAt, events: st.events, eventMins: st.eventMins, devices: st.subs.length });
|
||||
const pub = () => ({ chat: st.chat, mail: st.mail, tasks: st.tasks, tasksAt: st.tasksAt, events: st.events, eventMins: st.eventMins, devices: st.subs.length });
|
||||
if (p === '/api/push/key' && req.method === 'GET') return json(res, 200, { publicKey: keys().publicKey, ...pub() });
|
||||
if (req.method !== 'POST') return json(res, 405, { error: 'Method not allowed' });
|
||||
const b = JSON.parse((await readBody(req)) || '{}');
|
||||
@@ -129,6 +129,7 @@ async function handle(req, res) {
|
||||
if (typeof b.tasks === 'boolean') st.tasks = b.tasks;
|
||||
if (typeof b.events === 'boolean') st.events = b.events;
|
||||
if (typeof b.mail === 'boolean') st.mail = b.mail;
|
||||
if (typeof b.chat === 'boolean') st.chat = b.chat;
|
||||
if (/^([01]\d|2[0-3]):[0-5]\d$/.test(b.tasksAt || '')) st.tasksAt = b.tasksAt;
|
||||
if ([5, 10, 15, 30, 60].includes(b.eventMins)) st.eventMins = b.eventMins;
|
||||
if (b.tz && validTz(b.tz)) st.tz = b.tz;
|
||||
@@ -141,4 +142,6 @@ async function handle(req, res) {
|
||||
}
|
||||
json(res, 404, { error: 'Not found' });
|
||||
}
|
||||
module.exports = { handle, start, tick, localNow };
|
||||
/** A notification for one person's devices (a chat message, say), unless they turned that kind off. */
|
||||
async function notify(uid, payload) { const dir = users.dirOf(uid), st = load(dir); if (!st.subs.length || st.chat === false) return; await send(dir, st, payload); }
|
||||
module.exports = { handle, start, tick, localNow, notify };
|
||||
Reference in new issue
Block a user