calendar
All repositories: gitoria
5.0 KB
// lib/events.hl — THE EVENTS (ticket calendar#2). One hl:mpackdb table, storage/mpackdb/events.db, pk @id, index owner.// A record IS a WorldAPI Event (https://worldapi.org/standards/event): the `$`-keys `$type $version $kind $title// $description $start $end $timezone $location $status $visibility $created $updated`, plus `owner` (the users @id)// and the @id. Appointment: `$start`/`$end` local wall-clock `YYYY-MM-DDTHH:MM:SS` in `$timezone`. All-day:// `$kind = 'allday'`, `$start` a date, `$end` the EXCLUSIVE end date (left out for one day). Every event is// `$visibility = 'private'`: every function takes the owner's user id and reaches only that user's events.// Every write to the table is here. Looked up in lib/events-helpers.hl: build (page fields → record), viewOf (record →// what the page shows), isLocalId, byStart. The default reminders are lib/settings.hl.import { MPackDB } from 'hl:mpackdb'import { timestamp } from 'hl:time'import { storageDir, countOfList } from './util.hl'import { build, viewOf, isLocalId, byStart } from './events-helpers.hl'import { defaultsOf } from './settings.hl'import { before } from '../components/month-view/dates.hl'import { describe, shift } from '../components/month-view/reminders.hl'import { serverToday } from '../components/month-view/clock.hl'static eventsTable = new MPackDB(file = storageDir + '/events.db', primaryKey = '@id', indexes = ['owner'])static ownEvent = (userId, id) => {if (id == null || hlTypeName(id) != 'String' || id == '') { return null }e = eventsTable.fetch(id)if (e == null || e.owner != userId) { return null }return e}// the owner's events that touch first..last (dates), by startstatic eventsBetween = (userId, first, last) => {found = eventsTable.find('owner', userId)n = countOfList(found)views = []let i = 0while (i < n) {let v = viewOf(found[i])if (!before(v.last, first) && !before(last, v.first)) { views.push(v) }i = i + 1}return byStart(views)}static realId = (userId, id) => {if (!isLocalId(id)) { return id }found = eventsTable.find('owner', userId)n = countOfList(found)let i = 0while (i < n) {if (found[i].ref == id) { return found[i].id }i = i + 1}return null}// create, or change when the fields name an event (real or temporary id)static saveEvent = (userId, b) => {if (b != null && hlTypeName(b) == 'Hybrid' && b.id != null && b.id != '') {id = realId(userId, b.id)if (id != null) { return changeEvent(userId, id, b) }if (!isLocalId(b.id)) { return changeEvent(userId, b.id, b) }}return createEvent(userId, b)}static createEvent = (userId, b) => {x = build(b)if (x.error != null) { return x }r = x.recordt = timestamp()r['$created'] = tr['$updated'] = tr.owner = userIdif (isLocalId(b.id)) { r.ref = b.id }let id = eventsTable.put(r)if (id == null) { return { error = 'could not store the event: ' + eventsTable.lastError() } }return { id = id }}static changeEvent = (userId, id, b) => {old = ownEvent(userId, id)if (old == null) { return { error = 'no such event' } }x = build(b)if (x.error != null) { return x }r = x.recordr['$created'] = old['$created']r['$updated'] = timestamp()r.owner = userIdif (old.ref != null) { r.ref = old.ref }r.id = ideventsTable.update(id, r)return { id = id }}static deleteEvent = (userId, given) => {id = realId(userId, given)old = ownEvent(userId, id)if (old == null) { return { error = 'no such event' } }eventsTable.delete(id)return { ok = true }}// ---- the reminders still to come ------------------------------------------------------------------------// the owner's events starting first..last with the reminders that apply (own, else the defaults); timed and all-daystatic upcoming = (userId, first, last) => {defaults = defaultsOf(userId)found = eventsTable.find('owner', userId)n = countOfList(found)out = []let i = 0while (i < n) {let v = viewOf(found[i])if (!before(v.first, first) && !before(last, v.first)) {let list = { id = v.id title = v.title allDay = v.allDay startDate = v.startDate startTime = v.startTime place = v.place zone = v.zone rows = v.custom ? v.rows : defaults }out.push(list)}i = i + 1}return out}// the events with the reminders that apply, from two days back to nine days ahead (what the browser plans its notifications from)static soonOf = (userId) => {t = serverToday()return upcoming(userId, shift(t, -2), shift(t, 9))}// ---- what the faces answer (components/calendar.hl) -----------------------------------------------------// the events of the six weeks on screen, the defaults (text and rows) and the reminders still to comestatic calendarView = (userId, first, last) => {return { events = eventsBetween(userId, first, last) defaults = describe(defaultsOf(userId)) soon = soonOf(userId) defaultRows = defaultsOf(userId) }}// the settings editor: the default reminders as rows, their text and the reminders still to comestatic settingsView = (userId) => {return { rows = defaultsOf(userId) defaults = describe(defaultsOf(userId)) soon = soonOf(userId) defaultRows = defaultsOf(userId) }}
Branches
- mainmain branch
Latest commits
- 3b2a4cd0calendar mission 001 (3/4): code order — let only where reassigned (147 dropped incl. components/month-view; 110 left: 67 reassigned, 43 loop-bound); gates 78/0 (3 of 4 runs; 1 known 'next month' flake) + 18/0, live-data run = step 2mre
- f5ce6b1dcalendar mission 001 (2/4): code order — topics, map, thin faces: lib/util.hl, lib/events(-helpers).hl (+calendarView/settingsView/soonOf), lib/settings.hl, lib/users.hl (+userOfLoginCode, tagOf), lib/api(-helpers).hl; project.hl = map; login route takes &req/&sessions (failed-login reason now kept); gates 78/0 + 18/0mre
- c6fbd011calendar mission 001 (1/4): code order — files moved: lib/events.hl, lib/users.hl, components/styles.hl (imports only); gates 78/0 + 18/0, live-data run identicalmre
- d6c59290calendar: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gates 78/0 + 18/0mre
- 7bd0337ccalendar: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gates 78/0 + 18/0mre
- ff41310ccalendar: Hybriel master 8efba065 (#126 memory, #48 lambda copy; audit: no & needed)mre
- 14ba08c7antcolony#40: mission references point to the moved missionsmre
- 99c73346antcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
- 76edaa62calendar: Hybriel master ff51cf46 (re-vendor round, static workaround removed)mre
- 90a3fc2cdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- 6722b72ddeploy.sh: never send .git or .gitignore to Byrodinmre
- be099807State of 2026-09-27, before the move to gitoriamre