Reminders can count from a task's start as well as its due date (#72), and each reminder on an all-day task can have its own time (#78). ## Changes - A reminder counts from the start or the due date. Tasks with only a start date get reminders. Defaults in Settings can mix both, globally and per list. - Each reminder on an all-day task can have its own time of day. Reminders without one ring at the all-day time set in Settings. 00:00 can't be stored, so it becomes 00:01. - The editor shows the defaults a new task, or a newly added date, will get, and keeps them in step with dates, list and all-day until you change a reminder. What it shows is what gets saved. Removing the shown defaults now leaves the task without a reminder (Room store only; OpenTasks mode still falls back to the default). - Switching between timed and all-day converts the reminders right away in the editor. Switching straight back restores them. - Labels name the date when a task has both: "15 minutes before the start", "On the due date at 09:00". New reminders preselect the start. - Defaults that have already passed don't ring when the task is saved. - Sync: reminders rounded to whole days by builds before 1.2 pick up their real time of day on the next download. ## Deviations from the issues - The start/due anchor lives in floret-kit (`ReminderLead`, floret-kit#19) rather than being packed into minute values. That PR is additive, so Calendula is unaffected. - `error_reminder_without_due` was renamed to `error_reminder_without_date`, and the "(before start)/(before due)" strings were replaced by full sentences. The stale keys were deleted from the translation files so lint passes, and Weblate needs to offer the new strings to translators. - The dead single-reminder API (`setAlarm`, `alarms()`, `editable()`, `reminderFor`) was removed. - `AlarmLift` is back to its frozen 2→3 migration state. ## Release branch `release/v1.2.0` is merged into this branch. Through it, this PR also brings main's commits the release branch was missing, including the reminder sync from #81 that this work builds on. I resolved conflicts in the data source interface, `TaskAlarmDao`, CHANGELOG, PRIVACY.md and the floret-kit pointer by keeping both sides. Closes #72 Closes #78 Co-authored-by: Weblate <noreply-mt-weblate@weblate.org> Co-authored-by: Sup99999 <alelmnour1o234@proton.me> Co-authored-by: Robin Schanbacher <robin@schanbros.de> Co-authored-by: Knee8811 <mk-weby@protonmail.com> Co-authored-by: Sonja Kraus <klein.felix@pm.me> Co-authored-by: Weblate <noreply@weblate.org> Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/88
548 lines
17 KiB
JSON
548 lines
17 KiB
JSON
{
|
|
"formatVersion": 1,
|
|
"database": {
|
|
"version": 3,
|
|
"identityHash": "725292b38bce25ce9032c4650573ae85",
|
|
"entities": [
|
|
{
|
|
"tableName": "accounts",
|
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `display_name` TEXT NOT NULL, `principal_url` TEXT, `home_set_url` TEXT, `username` TEXT, `last_sync_at` INTEGER, `last_sync_error` TEXT)",
|
|
"fields": [
|
|
{
|
|
"fieldPath": "id",
|
|
"columnName": "id",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "displayName",
|
|
"columnName": "display_name",
|
|
"affinity": "TEXT",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "principalUrl",
|
|
"columnName": "principal_url",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "homeSetUrl",
|
|
"columnName": "home_set_url",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "username",
|
|
"columnName": "username",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "lastSyncAt",
|
|
"columnName": "last_sync_at",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "lastSyncError",
|
|
"columnName": "last_sync_error",
|
|
"affinity": "TEXT"
|
|
}
|
|
],
|
|
"primaryKey": {
|
|
"autoGenerate": true,
|
|
"columnNames": [
|
|
"id"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"tableName": "task_lists",
|
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `account_id` INTEGER, `is_visible` INTEGER NOT NULL DEFAULT 1, `is_synced` INTEGER NOT NULL DEFAULT 1, `owner` TEXT, `is_read_only` INTEGER NOT NULL DEFAULT 0, `sort_order` INTEGER NOT NULL DEFAULT 0, `href` TEXT, `ctag` TEXT, `sync_token` TEXT, `is_dirty` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`account_id`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
|
"fields": [
|
|
{
|
|
"fieldPath": "id",
|
|
"columnName": "id",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "name",
|
|
"columnName": "name",
|
|
"affinity": "TEXT",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "color",
|
|
"columnName": "color",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "accountId",
|
|
"columnName": "account_id",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "isVisible",
|
|
"columnName": "is_visible",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "1"
|
|
},
|
|
{
|
|
"fieldPath": "isSynced",
|
|
"columnName": "is_synced",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "1"
|
|
},
|
|
{
|
|
"fieldPath": "owner",
|
|
"columnName": "owner",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "isReadOnly",
|
|
"columnName": "is_read_only",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "sortOrder",
|
|
"columnName": "sort_order",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "href",
|
|
"columnName": "href",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "ctag",
|
|
"columnName": "ctag",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "syncToken",
|
|
"columnName": "sync_token",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "isDirty",
|
|
"columnName": "is_dirty",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
}
|
|
],
|
|
"primaryKey": {
|
|
"autoGenerate": true,
|
|
"columnNames": [
|
|
"id"
|
|
]
|
|
},
|
|
"indices": [
|
|
{
|
|
"name": "index_task_lists_account_id",
|
|
"unique": false,
|
|
"columnNames": [
|
|
"account_id"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_task_lists_account_id` ON `${TABLE_NAME}` (`account_id`)"
|
|
}
|
|
],
|
|
"foreignKeys": [
|
|
{
|
|
"table": "accounts",
|
|
"onDelete": "SET NULL",
|
|
"onUpdate": "NO ACTION",
|
|
"columns": [
|
|
"account_id"
|
|
],
|
|
"referencedColumns": [
|
|
"id"
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"tableName": "tasks",
|
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `list_id` INTEGER NOT NULL, `uid` TEXT NOT NULL, `href` TEXT, `etag` TEXT, `title` TEXT, `description` TEXT, `location` TEXT, `url` TEXT, `color` INTEGER, `categories` TEXT NOT NULL DEFAULT '', `status` INTEGER NOT NULL DEFAULT 0, `percent_complete` INTEGER, `completed_at` INTEGER, `priority` INTEGER NOT NULL DEFAULT 0, `classification` INTEGER, `dtstart` INTEGER, `due` INTEGER, `duration` TEXT, `is_all_day` INTEGER NOT NULL DEFAULT 0, `timezone` TEXT, `rrule` TEXT, `rdate` TEXT, `exdate` TEXT, `recurrence_id` INTEGER, `master_id` INTEGER, `parent_id` INTEGER, `sort_order` INTEGER NOT NULL DEFAULT 0, `created_at` INTEGER, `last_modified` INTEGER, `sequence` INTEGER NOT NULL DEFAULT 0, `is_dirty` INTEGER NOT NULL DEFAULT 0, `is_deleted` INTEGER NOT NULL DEFAULT 0, `unknown_properties` TEXT, `reminders_off` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`list_id`) REFERENCES `task_lists`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`master_id`) REFERENCES `tasks`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`parent_id`) REFERENCES `tasks`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
|
"fields": [
|
|
{
|
|
"fieldPath": "id",
|
|
"columnName": "id",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "listId",
|
|
"columnName": "list_id",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "uid",
|
|
"columnName": "uid",
|
|
"affinity": "TEXT",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "href",
|
|
"columnName": "href",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "etag",
|
|
"columnName": "etag",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "title",
|
|
"columnName": "title",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "description",
|
|
"columnName": "description",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "location",
|
|
"columnName": "location",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "url",
|
|
"columnName": "url",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "color",
|
|
"columnName": "color",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "categories",
|
|
"columnName": "categories",
|
|
"affinity": "TEXT",
|
|
"notNull": true,
|
|
"defaultValue": "''"
|
|
},
|
|
{
|
|
"fieldPath": "status",
|
|
"columnName": "status",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "percentComplete",
|
|
"columnName": "percent_complete",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "completedAt",
|
|
"columnName": "completed_at",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "priority",
|
|
"columnName": "priority",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "classification",
|
|
"columnName": "classification",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "dtstart",
|
|
"columnName": "dtstart",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "due",
|
|
"columnName": "due",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "duration",
|
|
"columnName": "duration",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "isAllDay",
|
|
"columnName": "is_all_day",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "timezone",
|
|
"columnName": "timezone",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "rrule",
|
|
"columnName": "rrule",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "rdate",
|
|
"columnName": "rdate",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "exdate",
|
|
"columnName": "exdate",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "recurrenceId",
|
|
"columnName": "recurrence_id",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "masterId",
|
|
"columnName": "master_id",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "parentId",
|
|
"columnName": "parent_id",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "sortOrder",
|
|
"columnName": "sort_order",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "createdAt",
|
|
"columnName": "created_at",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "lastModified",
|
|
"columnName": "last_modified",
|
|
"affinity": "INTEGER"
|
|
},
|
|
{
|
|
"fieldPath": "sequence",
|
|
"columnName": "sequence",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "isDirty",
|
|
"columnName": "is_dirty",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "isDeleted",
|
|
"columnName": "is_deleted",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
},
|
|
{
|
|
"fieldPath": "unknownProperties",
|
|
"columnName": "unknown_properties",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "remindersOff",
|
|
"columnName": "reminders_off",
|
|
"affinity": "INTEGER",
|
|
"notNull": true,
|
|
"defaultValue": "0"
|
|
}
|
|
],
|
|
"primaryKey": {
|
|
"autoGenerate": true,
|
|
"columnNames": [
|
|
"id"
|
|
]
|
|
},
|
|
"indices": [
|
|
{
|
|
"name": "index_tasks_list_id_is_deleted",
|
|
"unique": false,
|
|
"columnNames": [
|
|
"list_id",
|
|
"is_deleted"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_tasks_list_id_is_deleted` ON `${TABLE_NAME}` (`list_id`, `is_deleted`)"
|
|
},
|
|
{
|
|
"name": "index_tasks_parent_id",
|
|
"unique": false,
|
|
"columnNames": [
|
|
"parent_id"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_tasks_parent_id` ON `${TABLE_NAME}` (`parent_id`)"
|
|
},
|
|
{
|
|
"name": "index_tasks_master_id_recurrence_id",
|
|
"unique": false,
|
|
"columnNames": [
|
|
"master_id",
|
|
"recurrence_id"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_tasks_master_id_recurrence_id` ON `${TABLE_NAME}` (`master_id`, `recurrence_id`)"
|
|
},
|
|
{
|
|
"name": "index_tasks_is_dirty",
|
|
"unique": false,
|
|
"columnNames": [
|
|
"is_dirty"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_tasks_is_dirty` ON `${TABLE_NAME}` (`is_dirty`)"
|
|
},
|
|
{
|
|
"name": "index_tasks_list_id_uid_recurrence_id",
|
|
"unique": true,
|
|
"columnNames": [
|
|
"list_id",
|
|
"uid",
|
|
"recurrence_id"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_tasks_list_id_uid_recurrence_id` ON `${TABLE_NAME}` (`list_id`, `uid`, `recurrence_id`)"
|
|
}
|
|
],
|
|
"foreignKeys": [
|
|
{
|
|
"table": "task_lists",
|
|
"onDelete": "CASCADE",
|
|
"onUpdate": "NO ACTION",
|
|
"columns": [
|
|
"list_id"
|
|
],
|
|
"referencedColumns": [
|
|
"id"
|
|
]
|
|
},
|
|
{
|
|
"table": "tasks",
|
|
"onDelete": "CASCADE",
|
|
"onUpdate": "NO ACTION",
|
|
"columns": [
|
|
"master_id"
|
|
],
|
|
"referencedColumns": [
|
|
"id"
|
|
]
|
|
},
|
|
{
|
|
"table": "tasks",
|
|
"onDelete": "SET NULL",
|
|
"onUpdate": "NO ACTION",
|
|
"columns": [
|
|
"parent_id"
|
|
],
|
|
"referencedColumns": [
|
|
"id"
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"tableName": "task_alarms",
|
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task_id` INTEGER NOT NULL, `minutes_before` INTEGER NOT NULL, `reference` TEXT NOT NULL DEFAULT 'DUE', `message` TEXT, `uid` TEXT NOT NULL DEFAULT '', `raw` TEXT, FOREIGN KEY(`task_id`) REFERENCES `tasks`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
|
"fields": [
|
|
{
|
|
"fieldPath": "id",
|
|
"columnName": "id",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "taskId",
|
|
"columnName": "task_id",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "minutesBefore",
|
|
"columnName": "minutes_before",
|
|
"affinity": "INTEGER",
|
|
"notNull": true
|
|
},
|
|
{
|
|
"fieldPath": "reference",
|
|
"columnName": "reference",
|
|
"affinity": "TEXT",
|
|
"notNull": true,
|
|
"defaultValue": "'DUE'"
|
|
},
|
|
{
|
|
"fieldPath": "message",
|
|
"columnName": "message",
|
|
"affinity": "TEXT"
|
|
},
|
|
{
|
|
"fieldPath": "uid",
|
|
"columnName": "uid",
|
|
"affinity": "TEXT",
|
|
"notNull": true,
|
|
"defaultValue": "''"
|
|
},
|
|
{
|
|
"fieldPath": "raw",
|
|
"columnName": "raw",
|
|
"affinity": "TEXT"
|
|
}
|
|
],
|
|
"primaryKey": {
|
|
"autoGenerate": true,
|
|
"columnNames": [
|
|
"id"
|
|
]
|
|
},
|
|
"indices": [
|
|
{
|
|
"name": "index_task_alarms_task_id",
|
|
"unique": false,
|
|
"columnNames": [
|
|
"task_id"
|
|
],
|
|
"orders": [],
|
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_task_alarms_task_id` ON `${TABLE_NAME}` (`task_id`)"
|
|
}
|
|
],
|
|
"foreignKeys": [
|
|
{
|
|
"table": "tasks",
|
|
"onDelete": "CASCADE",
|
|
"onUpdate": "NO ACTION",
|
|
"columns": [
|
|
"task_id"
|
|
],
|
|
"referencedColumns": [
|
|
"id"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"setupQueries": [
|
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '725292b38bce25ce9032c4650573ae85')"
|
|
]
|
|
}
|
|
} |