aboutsummaryrefslogtreecommitdiffstats
path: root/mail-overview/MailPanel.qml
diff options
context:
space:
mode:
Diffstat (limited to 'mail-overview/MailPanel.qml')
-rw-r--r--mail-overview/MailPanel.qml78
1 files changed, 72 insertions, 6 deletions
diff --git a/mail-overview/MailPanel.qml b/mail-overview/MailPanel.qml
index 7457fb2..0a7f36a 100644
--- a/mail-overview/MailPanel.qml
+++ b/mail-overview/MailPanel.qml
@@ -10,6 +10,7 @@
// GNU General Public License for more details.
import Quickshell
+import Quickshell.Io
import Quickshell.Wayland
import QtQuick
@@ -20,6 +21,45 @@ Scope {
property string monitor: "DP-1"
property bool open: false
+ // mail-watcher's heartbeat, written every 60s. Read once per open: the
+ // drawer is a LazyLoader, so closing and reopening rebuilds the FileView
+ // and reads the current file. Watch is deliberately not used, because the
+ // heartbeat is written by atomic replace (tmpfile + rename) and an inotify
+ // watch held on the old inode dies with it.
+ property bool watcherAlive: false
+ property int watcherDead: 0
+ property int watcherExpected: 0
+
+ // The same staleness rule as mail-watcher's heartbeat_is_healthy: dead
+ // threads or a heartbeat older than 300s mean the watcher needs a look.
+ // Backoff is healthy, so it never turns the dot.
+ function readHeartbeat(payload) {
+ root.watcherAlive = false;
+ root.watcherDead = 0;
+ root.watcherExpected = 0;
+
+ let data = null;
+ try { data = JSON.parse(payload); } catch (e) { return; }
+ if (!data || typeof data.ts !== "string") return;
+
+ const ts = Date.parse(data.ts);
+ if (isNaN(ts) || (Date.now() - ts) / 1000 > 300) return;
+
+ root.watcherAlive = true;
+ root.watcherDead = Number(data.dead) || 0;
+ root.watcherExpected = Number(data.expected) || 0;
+ }
+
+ readonly property color watcherColor:
+ !watcherAlive ? Theme.red
+ : watcherDead > 0 ? Theme.yellow
+ : Theme.green
+
+ readonly property string watcherText:
+ !watcherAlive ? "watcher not running"
+ : watcherDead > 0 ? `${watcherDead} folder(s) dead, check the log`
+ : `watcher ok · ${watcherExpected} folders`
+
readonly property var screenObj:
Quickshell.screens.find(s => s.name === root.monitor) ?? Quickshell.screens[0]
@@ -95,6 +135,13 @@ Scope {
// the drawer.
MouseArea { anchors.fill: parent }
+ FileView {
+ id: heartbeat
+ path: `${Quickshell.env("HOME")}/.local/state/mail-watcher.heartbeat`
+ onLoaded: root.readHeartbeat(text())
+ onLoadFailed: root.readHeartbeat("")
+ }
+
Column {
id: content
anchors { left: parent.left; right: parent.right; top: parent.top; margins: 16 }
@@ -230,17 +277,36 @@ Scope {
Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.12) }
+ // Watcher health, above the button. Green when idling,
+ // yellow when a folder gave up, red when there is no fresh
+ // heartbeat at all.
+ Item {
+ width: parent.width
+ implicitHeight: 22
+
+ Rectangle {
+ id: watcherDot
+ anchors.verticalCenter: parent.verticalCenter
+ width: 8; height: 8; radius: 4
+ color: root.watcherColor
+ }
+
+ Text {
+ anchors { left: watcherDot.right; leftMargin: 10; verticalCenter: parent.verticalCenter }
+ text: root.watcherText
+ color: Theme.subtext
+ font.family: Theme.fontFamily
+ font.pixelSize: Theme.fontSize - 2
+ }
+ }
+
+ Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.12) }
+
Row {
anchors.right: parent.right
spacing: 10
Button {
- text: Accounts.syncing ? "Syncing..." : "Sync now"
- enabled: !Accounts.syncing
- onClicked: Accounts.sync()
- }
-
- Button {
text: "Open qtmaildir"
onClicked: { Accounts.openClient(); root.close(); }
}