192 lines
7.7 KiB
QML
192 lines
7.7 KiB
QML
// What the blocker is doing right now, and what it will do.
|
||
//
|
||
// The rules themselves are server-side (edit them in the web UI or below); this
|
||
// page is about *enforcement on this machine* — the helper, the hosts file and
|
||
// the process watcher.
|
||
import QtQuick
|
||
import QtQuick.Controls.Material
|
||
import QtQuick.Layouts
|
||
|
||
import "components"
|
||
|
||
ScrollView {
|
||
id: page
|
||
contentWidth: availableWidth
|
||
clip: true
|
||
|
||
ColumnLayout {
|
||
width: page.availableWidth
|
||
spacing: 16
|
||
|
||
Banner {
|
||
Layout.fillWidth: true
|
||
visible: !controller.helperInstalled
|
||
text: "The privileged helper is not installed, so websites cannot be blocked. "
|
||
+ "Run: sudo desktop/helper/install.sh"
|
||
actionText: ""
|
||
}
|
||
|
||
Banner {
|
||
Layout.fillWidth: true
|
||
visible: controller.blockerError !== ""
|
||
text: controller.blockerError
|
||
}
|
||
|
||
ColumnLayout {
|
||
Layout.fillWidth: true
|
||
Layout.margins: 20
|
||
spacing: 16
|
||
|
||
Card {
|
||
title: controller.blockerActive ? "🛡 Blocking is ON" : "Blocking is off"
|
||
subtitle: controller.blockerReason
|
||
|
||
RowLayout {
|
||
Layout.fillWidth: true
|
||
spacing: 20
|
||
StatTile {
|
||
label: "Domains"
|
||
value: controller.blockedDomains.length
|
||
caption: "redirected to 127.0.0.1"
|
||
}
|
||
StatTile {
|
||
label: "Apps"
|
||
value: controller.blockedApps.length
|
||
caption: "closed on sight"
|
||
}
|
||
StatTile {
|
||
label: "Attempts today"
|
||
value: controller.stats.today ? controller.stats.today.blocked_attempts : 0
|
||
caption: "and counting"
|
||
}
|
||
}
|
||
|
||
RowLayout {
|
||
Layout.fillWidth: true
|
||
Switch {
|
||
text: "Enforce on this machine"
|
||
checked: controller.config.enforce_blocking
|
||
onToggled: controller.setConfig("enforce_blocking", checked)
|
||
}
|
||
Switch {
|
||
text: "Websites"
|
||
checked: controller.config.block_hosts_file
|
||
onToggled: controller.setConfig("block_hosts_file", checked)
|
||
}
|
||
Switch {
|
||
text: "Apps"
|
||
checked: controller.config.block_apps
|
||
onToggled: controller.setConfig("block_apps", checked)
|
||
}
|
||
}
|
||
}
|
||
|
||
Card {
|
||
title: "Blocked websites"
|
||
subtitle: "One hostname per line. `www.` and `m.` are added automatically."
|
||
|
||
TextArea {
|
||
id: domainsArea
|
||
Layout.fillWidth: true
|
||
Layout.minimumHeight: 140
|
||
wrapMode: TextArea.NoWrap
|
||
font.family: "Roboto Mono, monospace"
|
||
text: (controller.settings.blocker ? controller.settings.blocker.blocked_domains : []).join("\n")
|
||
placeholderText: "youtube.com\nreddit.com\ntwitter.com"
|
||
}
|
||
RowLayout {
|
||
PrimaryButton {
|
||
text: "Save websites"
|
||
onClicked: {
|
||
var list = domainsArea.text.split("\n").map(function (s) { return s.trim() })
|
||
.filter(function (s) { return s.length > 0 })
|
||
controller.patchSettings({ blocker: { blocked_domains: list } })
|
||
}
|
||
}
|
||
Item { Layout.fillWidth: true }
|
||
}
|
||
}
|
||
|
||
Card {
|
||
title: "Blocked applications"
|
||
subtitle: "Process names, matched loosely: `steam` also catches `steamwebhelper`."
|
||
|
||
TextArea {
|
||
id: appsArea
|
||
Layout.fillWidth: true
|
||
Layout.minimumHeight: 100
|
||
wrapMode: TextArea.NoWrap
|
||
font.family: "Roboto Mono, monospace"
|
||
text: (controller.settings.blocker ? controller.settings.blocker.blocked_apps : []).join("\n")
|
||
placeholderText: "steam\ndiscord\ntelegram-desktop"
|
||
}
|
||
RowLayout {
|
||
PrimaryButton {
|
||
text: "Save applications"
|
||
onClicked: {
|
||
var list = appsArea.text.split("\n").map(function (s) { return s.trim() })
|
||
.filter(function (s) { return s.length > 0 })
|
||
controller.patchSettings({ blocker: { blocked_apps: list } })
|
||
}
|
||
}
|
||
Item { Layout.fillWidth: true }
|
||
}
|
||
}
|
||
|
||
Card {
|
||
title: "When to block"
|
||
subtitle: "Server-side, so every device agrees."
|
||
|
||
RowLayout {
|
||
Layout.fillWidth: true
|
||
Switch {
|
||
text: "Blocking enabled"
|
||
checked: controller.settings.blocker ? controller.settings.blocker.enabled : false
|
||
onToggled: controller.patchSettings({ blocker: { enabled: checked } })
|
||
}
|
||
ComboBox {
|
||
Layout.preferredWidth: 220
|
||
model: ["During focus intervals", "Always", "On a schedule"]
|
||
currentIndex: {
|
||
var mode = controller.settings.blocker ? controller.settings.blocker.mode : "during_work"
|
||
return mode === "always" ? 1 : (mode === "scheduled" ? 2 : 0)
|
||
}
|
||
onActivated: function (index) {
|
||
var modes = ["during_work", "always", "scheduled"]
|
||
controller.patchSettings({ blocker: { mode: modes[index] } })
|
||
}
|
||
}
|
||
Switch {
|
||
text: "Keep blocking during breaks"
|
||
checked: controller.settings.blocker ? controller.settings.blocker.block_during_breaks : false
|
||
onToggled: controller.patchSettings({ blocker: { block_during_breaks: checked } })
|
||
}
|
||
}
|
||
}
|
||
|
||
Card {
|
||
title: "Today's temptations"
|
||
subtitle: "Every attempt the blocker turned away — this is what the daily report jokes about."
|
||
|
||
Repeater {
|
||
model: controller.stats.today ? controller.stats.today.blocked_top : []
|
||
delegate: RowLayout {
|
||
required property var modelData
|
||
Layout.fillWidth: true
|
||
Label { text: modelData.target; Layout.fillWidth: true }
|
||
Label { text: "× " + modelData.count; font.weight: Font.DemiBold }
|
||
}
|
||
}
|
||
Label {
|
||
visible: !controller.stats.today || controller.stats.today.blocked_top.length === 0
|
||
text: "Nothing yet today. Either the blocker is off, or you are behaving."
|
||
opacity: 0.6
|
||
font.pixelSize: 12
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Component.onCompleted: controller.loadSettings()
|
||
}
|