Files
DankMaterialShell/quickshell/Modules/Settings/CreateUserTab.qml
T

208 lines
8.1 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Services
import qs.DCommon.Widgets
import qs.Modules.Settings.Widgets
Item {
id: root
property string statusText: ""
property bool statusIsError: false
property bool operationPending: false
property string pendingUsername: ""
property string pendingPassword: ""
property string pendingConfirm: ""
property bool pendingAdmin: false
property bool pendingGreeter: false
function _resetForm() {
pendingUsername = "";
pendingPassword = "";
pendingConfirm = "";
pendingAdmin = false;
pendingGreeter = false;
usernameField.text = "";
passwordField.text = "";
confirmField.text = "";
}
function _passwordsMatch() {
return pendingPassword.length > 0 && pendingPassword === pendingConfirm;
}
function _createCanProceed() {
return !operationPending && UsersService.isValidUsername(pendingUsername) && !UsersService.userExists(pendingUsername) && _passwordsMatch();
}
Connections {
target: UsersService
function onOperationCompleted(op, username, success, message) {
root.operationPending = false;
root.statusIsError = !success;
if (success) {
root.statusText = message + (username ? (" · " + username) : "");
if (op === "create")
root._resetForm();
} else {
root.statusText = (username ? (username + ": ") : "") + message;
}
}
}
SettingsPage {
SettingsRow {
visible: !PolkitService.polkitAvailable
subtitle: I18n.tr("Polkit integration is disabled. User management requires Polkit to elevate privileges.")
subtitleColor: Theme.error
}
SettingsCard {
width: parent.width
iconName: "person_add"
settingKey: "createUser"
visible: PolkitService.polkitAvailable
SettingsRow {
body: Column {
width: parent.width
spacing: Theme.spacingXS
DTextField {
id: usernameField
expressive: true
leftIconName: "person"
labelText: I18n.tr("Username")
width: parent.width
placeholderText: I18n.tr("e.g. alice")
isError: usernameInvalid
readonly property bool usernameInvalid: text.length > 0 && (!UsersService.isValidUsername(text) || UsersService.userExists(text))
onTextEdited: {
root.pendingUsername = text.trim();
}
}
StyledText {
width: parent.width
visible: usernameField.text.length > 0 && !UsersService.isValidUsername(usernameField.text)
text: I18n.tr("Username must start with a lowercase letter or underscore and contain only lowercase letters, digits, hyphens, or underscores.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
wrapMode: Text.WordWrap
}
StyledText {
width: parent.width
visible: usernameField.text.length > 0 && UsersService.isValidUsername(usernameField.text) && UsersService.userExists(usernameField.text)
text: I18n.tr("A user with that name already exists.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
wrapMode: Text.WordWrap
}
}
}
SettingsRow {
body: Column {
width: parent.width
spacing: Theme.spacingXS
DTextField {
id: passwordField
expressive: true
leftIconName: "lock"
labelText: I18n.tr("Password")
width: parent.width
echoMode: TextInput.Password
showPasswordToggle: true
onTextEdited: root.pendingPassword = text
}
}
}
SettingsRow {
body: Column {
width: parent.width
spacing: Theme.spacingXS
DTextField {
id: confirmField
expressive: true
leftIconName: "lock"
labelText: I18n.tr("Confirm password")
width: parent.width
echoMode: TextInput.Password
showPasswordToggle: true
isError: confirmMismatch
readonly property bool confirmMismatch: text.length > 0 && text !== passwordField.text
onTextEdited: root.pendingConfirm = text
}
StyledText {
width: parent.width
visible: confirmField.text.length > 0 && confirmField.text !== passwordField.text
text: I18n.tr("Passwords do not match.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
}
}
}
SettingsToggleRow {
settingKey: "createUserAdmin"
tags: ["user", "admin", "sudo", "wheel"]
text: I18n.tr("Grant administrator privileges")
description: I18n.tr("Adds them to the %1 group so they can use sudo", "create user admin toggle description, %1 is a group name").arg(UsersService.adminGroup)
checked: root.pendingAdmin
onToggled: checked => root.pendingAdmin = checked
}
SettingsToggleRow {
settingKey: "createUserGreeter"
tags: ["user", "greeter", "login", "sync"]
text: I18n.tr("Allow greeter login access")
description: I18n.tr("Adds them to the %1 group so they can run dms-greeter sync --profile", "create user greeter toggle description, %1 is a group name").arg(UsersService.greeterGroup)
checked: root.pendingGreeter
onToggled: checked => root.pendingGreeter = checked
}
SettingsRow {
body: Row {
width: parent.width
spacing: Theme.spacingM
DButton {
text: root.operationPending ? I18n.tr("Working...", "create user button text while the operation runs") : I18n.tr("Add user", "button and settings page title, creates a new user account")
iconName: "person_add"
backgroundColor: Theme.primary
textColor: Theme.primaryText
enabled: root._createCanProceed()
onClicked: {
if (!root._createCanProceed())
return;
root.operationPending = true;
root.statusText = "";
UsersService.createUser(root.pendingUsername, root.pendingPassword, root.pendingAdmin, root.pendingGreeter, null);
}
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: root.statusText
color: root.statusIsError ? Theme.error : Theme.primary
font.pixelSize: Theme.fontSizeSmall
wrapMode: Text.WordWrap
width: parent.width - parent.children[0].width - Theme.spacingM
}
}
}
}
}
}