{ config, lib, pkgs, ... }: let calendarAccounts = config.accounts.calendar.accounts; contactAccounts = config.accounts.contact.accounts; # Nextcloud stores VTODOs in the same CalDAV collections as VEVENTs, so a # calendar account can also be exposed to task-list clients (Planify) by # emitting a second EDS source against the same collection. tasksFrom = [ "matt" ]; # Per-calendar accent colors, previously set on the (now removed) # `thunderbird.color` attrs in module.accounts.nix. calendarColors = { family = "#FED3D9"; matt = "#FDF0D7"; joseph-cal = "#CEF4F8"; }; splitUrl = url: let stripped = lib.removePrefix "https://" url; host = lib.head (lib.splitString "/" stripped); in { inherit host; resourcePath = lib.removePrefix host stripped; }; mkAuth = remote: let s = splitUrl remote.url; in { "Security" = { Method = "tls"; }; "Authentication" = { Host = s.host; Port = "443"; User = remote.userName; Method = "plain/password"; RememberPassword = "true"; }; "WebDAV Backend" = { ResourcePath = s.resourcePath; }; "Offline" = { StaySynchronized = "true"; }; "Refresh" = { Enabled = "true"; IntervalMinutes = "30"; }; }; toIni = sections: lib.concatStringsSep "\n" ( lib.mapAttrsToList (section: keys: '' [${section}] ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${v}") keys)} '') sections ); mkCalendarSource = name: account: let uid = "nextcloud-${name}"; in { inherit uid; text = toIni ({ "Data Source" = { DisplayName = name; Enabled = "true"; Parent = "caldav-stub"; }; "Calendar" = { BackendName = "caldav"; Color = calendarColors.${name} or "#3584E4"; Selected = "true"; }; } // mkAuth account.remote); }; mkTaskListSource = name: account: let uid = "nextcloud-${name}-tasks"; in { inherit uid; text = toIni ({ "Data Source" = { DisplayName = "${name} (Tasks)"; Enabled = "true"; Parent = "caldav-stub"; }; "Task List" = { BackendName = "caldav"; Color = calendarColors.${name} or "#3584E4"; Selected = "true"; }; } // mkAuth account.remote); }; mkContactSource = name: account: let uid = "nextcloud-${name}"; in { inherit uid; text = toIni ({ "Data Source" = { DisplayName = name; Enabled = "true"; Parent = "carddav-stub"; }; "Address Book" = { BackendName = "carddav"; }; } // mkAuth account.remote); }; calendarSources = lib.mapAttrsToList mkCalendarSource calendarAccounts; taskListSources = lib.mapAttrsToList mkTaskListSource (lib.filterAttrs (name: _: builtins.elem name tasksFrom) calendarAccounts); contactSources = lib.mapAttrsToList mkContactSource contactAccounts; allSources = calendarSources ++ taskListSources ++ contactSources; sourceFile = src: pkgs.writeText "${src.uid}.source" src.text; in { home.packages = with pkgs; [ gnome-calendar gnome-contacts planify libsecret ]; # evolution-data-server treats files under ~/.config/evolution/sources/ as # writable/removable and rewrites them on connect and on any in-app tweak # (color, visibility, ...). A /nix/store symlink can't be written back to, # so these are copied into place as real files on every activation instead # of managed via xdg.configFile. home.activation.evolutionSources = lib.hm.dag.entryAfter [ "writeBoundary" ] ( lib.concatMapStringsSep "\n" (src: '' install -Dm644 ${sourceFile src} "$HOME/.config/evolution/sources/${src.uid}.source" '') allSources ); }