Architecture

Source map

Part Purpose
src/askare/config.* Server URL, user name, TLS preference and the sync settings in ~/.config/org.frapps/harbour-askare/settings.conf
src/askare/secretsstore.* The app password in a device-lock collection of Sailfish Secrets
src/askare/loginflow.* Nextcloud login flow v2: ask for a login page, then poll until the server hands over an app password
src/askare/httpclient.* QNetworkAccessManager wrapper: basic auth, custom methods, ETags, TLS preference
src/askare/davclient.* CalDAV PROPFIND, REPORT, PUT and DELETE, and the order they run in
src/askare/davxml.* Readers for the DAV responses: principal, home set, calendars, to-dos, errors
src/askare/ics.* VTODO unfold, parse, rewrite, and the date arithmetic reminders need
src/askare/markdown.* The notes, rendered to the subset of HTML a Silica Label draws
src/askare/url.* Server URL normalisation and href joining
src/askare/store.* SQLite copy of lists and tasks, in askare.db
src/askare/session.* QML-facing controller: login, sync, save, delete
src/askare/calendarmodel.* Task lists, with their colour and open count
src/askare/taskmodel.* The tasks of one view, grouped and ordered for the list
src/askare/reminders.* Watches stored tasks and reports a reminder whose moment has passed
qml/pages/* Silica UI
qml/components/* Shared QML such as the SupportMe dialog
qml/modules/Opal/* Bundled Opal About, SupportMe, and LinkHandler modules

src/main.cpp publishes four context properties - Askare (the session), Calendars, Tasks and Reminders - and the pages bind to those. It also adds qml/modules to the QML import path so the bundled Opal modules resolve at runtime. Networking and ICS handling stay in C++; QML holds no request and parses no calendar data.

CalDAV rules

A sync walks the server in a fixed order: PROPFIND for the current user principal, PROPFIND for its calendar home set, PROPFIND for the calendars under it, then one REPORT per calendar for its VTODOs. Only afterwards does it upload what the device changed, with PUT and DELETE.

Collection hrefs keep their trailing slash, through Url::pathWithSlash(), so a task href built from a calendar href matches what the server returns.

A change is uploaded with If-Match carrying the ETag the task was last read at, and a new task with If-None-Match: *, so neither a task changed on both sides nor a name already taken is overwritten. A task the server refuses stays marked dirty and is offered again on the next run; only a run that uploaded something may pull afterwards, so a rejected task cannot drive an endless loop.

Reminders are a VALARM inside the VTODO, with a trigger relative to the end of the task, which is how Nextcloud Tasks writes them. Subtasks are a RELATED-TO naming the UID of the parent, which resolves only within one calendar - hence a subtask is pinned to the list of its parent.

Sandbox layout

Sailjail gives the app three private directories, named after the OrganizationName and ApplicationName of the desktop file:

  • ~/.config/org.frapps/harbour-askare - settings.conf
  • ~/.local/share/org.frapps/harbour-askare - askare.db
  • ~/.cache/org.frapps/harbour-askare - whatever the web view leaves behind; nothing the app writes itself

The permissions are Internet, Secrets and WebView; no document, picture or calendar directory is asked for. The config and share directories are declared for the system backup through X-HarbourBackup.

Reminders without a service

Reminders checks the stored tasks every minute while the app runs, and each check notes the moment it announced, so a reminder fires once and an unchanged due date stays silent. That timer only ticks while the device is awake, so qml/harbour-askare.qml holds a BackgroundJob that wakes the app every five minutes for as long as a reminder is still ahead - Reminders.armed says whether one is.


This site uses Just the Docs, a documentation theme for Jekyll.