Home is now Overview | My Home | Phone. Overview leads with quick-action tiles (focus, Do Not Disturb, health, snapshots, storage), keeps the findings card — updates fold in, the reclaim-space prompt is gone on purpose — and adds glance cards, the next calendar event, and weather. My Home groups every light by Home Assistant area: the helper gained an `areas` command (one REST template render, no websocket), and the rooms degrade to a flat list on setups without areas. The favorites editor and connection card moved intact. Phone gains a vitals strip — battery and cell signal read from KDE Connect's plugin D-Bus objects, where absence is data, not an error — beside ring, clipboard, send-a-file, and the BlueBubbles handoff. The retired home-phone id resolves to my-home forever via a new alias map in SettingsRoutes (with a hasOwnProperty guard so prototype names cannot leak into settingsPage). Storage no longer claims 0 B free — the old page read a field the disks helper never emitted. Contracts updated alongside; per the new workflow, the full suite runs once at the end of the redesign (see the test backlog note). Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
259 lines
9.3 KiB
QML
259 lines
9.3 KiB
QML
// Phone — the continuity half of the old Home & Phone page, promoted to a tab
|
|
// of Home and given vitals.
|
|
//
|
|
// The vitals strip reads battery and cell signal from KDE Connect's plugin
|
|
// D-Bus objects, which exist only while the phone is paired and reachable — a
|
|
// missing datum renders as an em-dash, never an error. Actions mirror Control
|
|
// Center's semantics exactly: KDE Connect actions need the phone nearby;
|
|
// Messages needs only BlueBubbles.
|
|
|
|
import QtQuick
|
|
import QtQuick.Dialogs
|
|
import qs.config
|
|
import qs.services
|
|
|
|
SettingsPage {
|
|
id: root
|
|
objectName: "phone-page"
|
|
title: "Phone"
|
|
lede: {
|
|
if (!KdeConnect.available)
|
|
return "KDE Connect is unavailable — open System Health for the service.";
|
|
if (!KdeConnect.preferredPhone)
|
|
return "Pair a phone with KDE Connect to reach it from here.";
|
|
return String(KdeConnect.preferredPhone.name)
|
|
+ (KdeConnect.phoneReachable ? " · nearby on Wi-Fi" : " · not nearby");
|
|
}
|
|
|
|
readonly property bool actionsReady: KdeConnect.phoneReachable && !KdeConnect.transferActive
|
|
|
|
function localPath(selectedUrl: url): string {
|
|
const value = String(selectedUrl);
|
|
if (!value.startsWith("file://"))
|
|
return "";
|
|
return decodeURIComponent(value.slice(7));
|
|
}
|
|
|
|
FileDialog {
|
|
id: fileDialog
|
|
title: KdeConnect.preferredPhone
|
|
? "Send a file to " + KdeConnect.preferredPhone.name : "Send a file"
|
|
fileMode: FileDialog.OpenFile
|
|
nameFilters: ["All files (*)"]
|
|
onAccepted: {
|
|
const path = root.localPath(selectedFile);
|
|
if (path !== "")
|
|
KdeConnect.sendFile(path);
|
|
}
|
|
}
|
|
|
|
// ── Vitals ──────────────────────────────────────────────────────────────
|
|
|
|
Row {
|
|
id: vitals
|
|
|
|
width: parent.width
|
|
spacing: 12
|
|
|
|
Repeater {
|
|
model: [
|
|
{
|
|
value: KdeConnect.phoneBattery
|
|
? KdeConnect.phoneBattery.charge + "%" : "—",
|
|
caption: KdeConnect.phoneBattery
|
|
? (KdeConnect.phoneBattery.charging ? "Battery · charging" : "Battery")
|
|
: "Battery"
|
|
},
|
|
{
|
|
value: KdeConnect.phoneSignal
|
|
? String(KdeConnect.phoneSignal.networkType) : "—",
|
|
caption: KdeConnect.phoneSignal
|
|
? "Signal · " + KdeConnect.phoneSignal.strength + " of 4"
|
|
: "Signal"
|
|
},
|
|
{
|
|
value: KdeConnect.phoneReachable
|
|
? "Nearby"
|
|
: (KdeConnect.pairedCount > 0 ? "Away" : "Unpaired"),
|
|
caption: KdeConnect.phoneReachable
|
|
? "Reachable on Wi-Fi"
|
|
: (KdeConnect.pairedCount > 0
|
|
? "Actions wait for the phone to reconnect"
|
|
: "Pair a device to continue")
|
|
}
|
|
]
|
|
|
|
delegate: Rectangle {
|
|
id: vital
|
|
|
|
required property var modelData
|
|
|
|
width: (vitals.width - vitals.spacing * 2) / 3
|
|
height: 66
|
|
radius: 12
|
|
color: Theme.alpha(Theme.fg, 0.045)
|
|
border.width: 1
|
|
border.color: Theme.alpha(Theme.fg, 0.06)
|
|
|
|
Column {
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.leftMargin: 14
|
|
anchors.rightMargin: 14
|
|
spacing: 3
|
|
|
|
Text {
|
|
text: String(vital.modelData.value)
|
|
color: Theme.fg
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeLarge + 3
|
|
font.weight: Font.DemiBold
|
|
font.features: Theme.tabularFigures
|
|
}
|
|
|
|
Text {
|
|
width: parent.width
|
|
text: String(vital.modelData.caption)
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Reach it ────────────────────────────────────────────────────────────
|
|
|
|
SettingsCard {
|
|
title: "Reach it"
|
|
subtitle: KdeConnect.transferActive
|
|
? "Sending " + KdeConnect.transferFileName + "…"
|
|
: "Everything you actually do weekly, one card."
|
|
|
|
SettingRow {
|
|
label: "Ring"
|
|
detail: "Even when silenced"
|
|
controlWidth: 110
|
|
|
|
SettingsButton {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Ring"
|
|
enabled: root.actionsReady && KdeConnect.supports("ring")
|
|
onClicked: KdeConnect.ring()
|
|
}
|
|
}
|
|
|
|
SettingRow {
|
|
label: "Clipboard"
|
|
detail: "Send what you just copied"
|
|
controlWidth: 110
|
|
|
|
SettingsButton {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Send"
|
|
enabled: root.actionsReady && KdeConnect.supports("clipboard")
|
|
onClicked: KdeConnect.sendClipboard()
|
|
}
|
|
}
|
|
|
|
SettingRow {
|
|
label: "Send a file"
|
|
detail: KdeConnect.recentExchange
|
|
? "Last sent " + KdeConnect.recentExchange.fileName
|
|
: "Lands in the phone's downloads"
|
|
divider: KdeConnect.transferActive
|
|
controlWidth: 110
|
|
|
|
SettingsButton {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Choose…"
|
|
enabled: root.actionsReady && KdeConnect.supports("share")
|
|
onClicked: fileDialog.open()
|
|
}
|
|
}
|
|
|
|
SettingRow {
|
|
visible: KdeConnect.transferActive
|
|
label: "Sending " + KdeConnect.transferFileName
|
|
detail: "To " + KdeConnect.transferDeviceName
|
|
divider: false
|
|
controlWidth: 110
|
|
|
|
SettingsButton {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Cancel"
|
|
tone: "danger"
|
|
onClicked: KdeConnect.cancelTransfer()
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Messages ────────────────────────────────────────────────────────────
|
|
|
|
SettingsCard {
|
|
title: "Messages"
|
|
subtitle: "Kept independent from phone connectivity."
|
|
|
|
SettingRow {
|
|
label: "iMessage"
|
|
detail: "Opens BlueBubbles"
|
|
divider: false
|
|
controlWidth: 204
|
|
|
|
Row {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 12
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: SystemSettings.bluebubblesAvailable ? "Installed" : "Unavailable"
|
|
color: SystemSettings.bluebubblesAvailable ? Theme.ok : Theme.fgMuted
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
}
|
|
|
|
SettingsButton {
|
|
id: openBlueBubblesButton
|
|
text: "Open"
|
|
enabled: SystemSettings.bluebubblesAvailable
|
|
activeFocusOnTab: enabled
|
|
border.width: activeFocus ? 2 : 1
|
|
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
|
onClicked: SystemSettings.openApplication("bluebubbles")
|
|
Keys.onReturnPressed: if (enabled) SystemSettings.openApplication("bluebubbles")
|
|
Keys.onSpacePressed: if (enabled) SystemSettings.openApplication("bluebubbles")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Device ──────────────────────────────────────────────────────────────
|
|
|
|
SettingsCard {
|
|
title: "Device"
|
|
|
|
ActionRow {
|
|
label: "Connection"
|
|
detail: "Ask the service to look again"
|
|
action: "Refresh"
|
|
onTriggered: KdeConnect.refresh()
|
|
}
|
|
|
|
ActionRow {
|
|
label: "Device settings"
|
|
detail: "Pairing, plugins and service health"
|
|
action: "Open"
|
|
divider: false
|
|
onTriggered: ShellState.openSettings("connectivity")
|
|
}
|
|
}
|
|
}
|