211 lines
8.7 KiB
QML
211 lines
8.7 KiB
QML
// What this machine offers to other machines on the network.
|
|
//
|
|
// Every row states what is true right now, including "the software for this is
|
|
// not installed" -- which is the honest answer for file sharing here, and is
|
|
// what the panel this replaces hides behind a switch that silently does
|
|
// nothing.
|
|
//
|
|
// Turning remote login on changes the whole system and prompts. Remote desktop
|
|
// is a user service and does not.
|
|
|
|
import Quickshell
|
|
import QtQuick
|
|
import qs.config
|
|
import qs.services
|
|
|
|
SettingsPage {
|
|
id: root
|
|
|
|
objectName: "sharing"
|
|
title: "Sharing"
|
|
lede: "What this machine offers to other machines on the network."
|
|
|
|
Component.onCompleted: Sharing.refresh()
|
|
|
|
SettingsCard {
|
|
title: "This machine"
|
|
subtitle: "The name other machines see."
|
|
|
|
// The error belongs to the machine, not to a floating banner above the
|
|
// title: a bare row above the first card read as part of the page
|
|
// furniture, which is exactly what an error must not do.
|
|
TextRow {
|
|
width: parent.width
|
|
visible: Sharing.lastError !== ""
|
|
label: "Sharing needs attention"
|
|
detail: Sharing.lastError
|
|
value: ""
|
|
}
|
|
|
|
TextFieldRow {
|
|
label: "Network name"
|
|
detail: "Used for ssh and for anything else that finds this machine by name"
|
|
text: Sharing.hostname
|
|
placeholder: "desktop"
|
|
enabled: !Sharing.busy
|
|
divider: false
|
|
onAccepted: value => Sharing.setHostname(value)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Remote login"
|
|
subtitle: Sharing.remoteLoginOn
|
|
? "Sign in to a terminal on this machine with: ssh " + Sharing.networkName
|
|
: "Sign in to a terminal on this machine over SSH."
|
|
|
|
// Port and the password-sign-in claim used to be two rows of their own
|
|
// below this one. They describe this switch rather than standing beside
|
|
// it, so they read as its detail line -- and the card is three rows
|
|
// shorter for saying the same things.
|
|
//
|
|
// "Keys only" is reported from sshd's configuration rather than assumed:
|
|
// claiming it on a machine that actually accepts passwords would be a
|
|
// security claim this page cannot back up.
|
|
SwitchRow {
|
|
label: "Allow remote login"
|
|
detail: Sharing.remoteLogin?.installed === true
|
|
? "OpenSSH · port " + String(Sharing.remoteLogin?.port ?? "22")
|
|
+ " · password sign-in: " + Sharing.passwordLoginSummary().toLowerCase()
|
|
+ (Sharing.remoteLoginOn ? " · starts at boot" : "")
|
|
: "OpenSSH server is not installed"
|
|
checked: Sharing.remoteLoginOn
|
|
enabled: !Sharing.busy && Sharing.remoteLogin?.installed === true
|
|
divider: Sharing.remoteLoginOn
|
|
onToggled: value => Sharing.setRemoteLogin(value)
|
|
}
|
|
|
|
TextRow {
|
|
visible: Sharing.remoteLoginOn && Sharing.remoteSessions.length === 0
|
|
label: "Nobody is signed in remotely"
|
|
detail: "Remote login is on, and no one is connected from another machine."
|
|
value: ""
|
|
divider: false
|
|
}
|
|
|
|
Repeater {
|
|
model: Sharing.remoteSessions
|
|
|
|
delegate: TextRow {
|
|
required property var modelData
|
|
required property int index
|
|
width: parent.width
|
|
label: String(modelData.user ?? "") + " is signed in from " + String(modelData.from ?? "")
|
|
detail: "Since " + String(modelData.since ?? "") + " · " + String(modelData.line ?? "")
|
|
value: ""
|
|
divider: index < Sharing.remoteSessions.length - 1
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Remote desktop"
|
|
subtitle: "See and control this desktop from another machine."
|
|
|
|
SwitchRow {
|
|
label: "Allow remote desktop"
|
|
detail: Sharing.remoteDesktop?.available === true
|
|
? "RDP · port " + String(Sharing.remoteDesktop?.port ?? "3389") + " · "
|
|
+ (Sharing.remoteDesktopOn
|
|
? "running for your session"
|
|
: (Sharing.remoteDesktop?.hasCredentials === true
|
|
? "not running"
|
|
: "set a username and password before turning this on"))
|
|
: "Remote desktop support is not installed"
|
|
checked: Sharing.remoteDesktopOn
|
|
enabled: !Sharing.busy
|
|
&& Sharing.remoteDesktop?.available === true
|
|
&& Sharing.remoteDesktop?.hasCredentials === true
|
|
onToggled: value => Sharing.setRemoteDesktop(value)
|
|
}
|
|
|
|
TextFieldRow {
|
|
visible: Sharing.remoteDesktop?.available === true
|
|
label: "Port"
|
|
detail: "The RDP port other machines connect to"
|
|
text: String(Sharing.remoteDesktop?.port ?? "")
|
|
placeholder: "3389"
|
|
enabled: !Sharing.busy
|
|
onAccepted: value => Sharing.setRdpPort(value)
|
|
}
|
|
|
|
SwitchRow {
|
|
visible: Sharing.remoteDesktop?.available === true
|
|
label: "View only"
|
|
detail: "Let someone watch this desktop without controlling the pointer or keyboard"
|
|
checked: Sharing.remoteDesktop?.viewOnly === true
|
|
enabled: !Sharing.busy
|
|
onToggled: value => Sharing.setRdpViewOnly(value)
|
|
}
|
|
|
|
// The password is typed into gnome-remote-desktop's own tool in a
|
|
// terminal, never into this page. grdctl prompts for it on a terminal
|
|
// and crashes without one, and passing it as an argument would publish
|
|
// it through /proc to every process on this machine.
|
|
//
|
|
// The row says why rather than naming the mechanism: "opens kitty" is
|
|
// an implementation detail, and the reason -- the password never passes
|
|
// through Panama -- is the part worth reading.
|
|
ActionRow {
|
|
visible: Sharing.remoteDesktop?.available === true
|
|
label: "Credentials"
|
|
detail: Sharing.remoteDesktop?.hasCredentials === true
|
|
? "Stored in the login keyring · set in a terminal so the password never passes through Panama"
|
|
: "None stored yet · set in a terminal so the password never passes through Panama"
|
|
action: "Set…"
|
|
enabled: !Sharing.busy
|
|
onTriggered: Sharing.setRdpCredentials(Quickshell.env("USER") || "")
|
|
}
|
|
|
|
ActionRow {
|
|
visible: Sharing.remoteDesktop?.available === true
|
|
&& Sharing.remoteDesktop?.hasCredentials === true
|
|
label: "Forget the stored credentials"
|
|
detail: "Remote desktop cannot be turned on again until new ones are set"
|
|
action: "Clear"
|
|
enabled: !Sharing.busy
|
|
divider: false
|
|
onTriggered: Sharing.clearRdpCredentials()
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "File and media sharing"
|
|
subtitle: "Sharing folders and media needs software this machine does not necessarily have."
|
|
|
|
// Not a switch, because there is nothing behind it. Naming what
|
|
// installing Samba would unlock is the difference between a dead row
|
|
// and an answer -- the panel this replaces shows a switch that silently
|
|
// does nothing.
|
|
TextRow {
|
|
label: "Share folders on the network"
|
|
detail: Sharing.fileSharing?.installed === true
|
|
? "Samba is installed, so folders can be published to Windows, macOS and Linux machines alike"
|
|
: "Samba is not installed — install it and this becomes a switch. Settings does not install software."
|
|
value: Sharing.fileSharing?.installed === true ? "Available" : "Not installed"
|
|
}
|
|
|
|
SwitchRow {
|
|
visible: Sharing.mediaSharing?.installed === true
|
|
label: "Share music and video to devices"
|
|
// Said before it happens, not after: this advertises on the network
|
|
// to anything that speaks DLNA, with no password in front of it.
|
|
detail: Sharing.mediaSharing?.active === true
|
|
? "Rygel is serving your media to devices on the network"
|
|
: "Publishes your media folders to every device on the network. No password is asked for."
|
|
checked: Sharing.mediaSharing?.active === true
|
|
enabled: !Sharing.busy
|
|
divider: false
|
|
onToggled: value => Sharing.setMediaSharing(value)
|
|
}
|
|
|
|
TextRow {
|
|
visible: Sharing.mediaSharing?.installed !== true
|
|
label: "Share music and video to devices"
|
|
detail: "Needs Rygel, which is not installed."
|
|
value: "Not installed"
|
|
divider: false
|
|
}
|
|
}
|
|
}
|