pragma Singleton import QtQuick import Quickshell import Quickshell.Io // The liturgical day, from the `typikon` command. // // Computed locally, so this needs no network and cannot go stale. It only has // to be re-read when the date changes, which a clock at minute precision // notices without polling a subprocess every few seconds. Singleton { id: root // Set this to a jurisdiction key — "rocor", "antiochian", "oca" — or leave // it empty to use whatever `typikon --set-default` wrote. property string jurisdiction: "" // Today plus the next six, so the week ahead is visible at a glance. property int days: 7 // Empty means today. Set it to an ISO date to look at a different one -- // useful for seeing what Great Week will ask of you before it arrives. property string forDate: "" property bool available: false property string error: "" property var week: [] property var today: ({}) readonly property string date: today.date ?? "" readonly property string weekday: today.weekday ?? "" readonly property string churchDate: today.churchDate ?? "" readonly property string season: today.season ?? "" readonly property string cycle: today.cycle ?? "" readonly property string feast: today.feast ?? "" readonly property string title: today.title ?? "" readonly property string rank: today.rank ?? "" readonly property string fast: today.fast ?? "none" readonly property string fastLabel: today.fastLabel ?? "" readonly property string emoji: today.emoji ?? "" readonly property string reason: today.reason ?? "" readonly property string obligation: today.obligation ?? "" readonly property string pascha: today.pascha ?? "" readonly property int daysToPascha: today.daysToPascha ?? 0 readonly property var commemorations: today.commemorations ?? [] readonly property bool obligatory: obligation === "obligatory" // A request made while a run is in flight is queued, not dropped. Dropping // it meant a jurisdiction or date set a moment after startup never took // effect at all -- the answer stayed yesterday's until midnight. property bool _queued: false function refresh() { if (proc.running) { root._queued = true; return; } proc.running = true; } SystemClock { id: clock; precision: SystemClock.Minutes } // Re-read when the day rolls over, not on a timer. The answer changes at // midnight and at no other moment. readonly property string _day: Qt.formatDateTime(clock.date, "yyyy-MM-dd") on_DayChanged: root.refresh() onJurisdictionChanged: root.refresh() onForDateChanged: root.refresh() Component.onCompleted: root.refresh() Process { id: proc onExited: if (root._queued) { root._queued = false; root.refresh(); } command: { const c = ["typikon", "--json", "--days", String(root.days)]; if (root.jurisdiction !== "") c.push("--jurisdiction", root.jurisdiction); if (root.forDate !== "") c.push("--date", root.forDate); return c; } stdout: StdioCollector { onStreamFinished: { try { const parsed = JSON.parse(this.text); root.week = parsed.days ?? [parsed]; root.today = root.week.length ? root.week[0] : {}; root.available = true; root.error = ""; } catch (e) { root.available = false; root.error = "could not read typikon"; } } } stderr: StdioCollector { onStreamFinished: { if (this.text.trim() !== "") { root.available = false; root.error = this.text.trim(); } } } } }