Spectral/qml/main.qml

568 lines
15 KiB
QML

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import Qt.labs.settings 1.0
import Qt.labs.platform 1.0 as Platform
import Spectral.Panel 2.0
import Spectral.Component 2.0
import Spectral.Page 2.0
import Spectral.Effect 2.0
import Spectral 0.1
import Spectral.Setting 0.1
ApplicationWindow {
readonly property bool inPortrait: window.width < window.height
Material.theme: MPalette.theme
Material.background: MPalette.background
width: 960
height: 640
minimumWidth: 480
minimumHeight: 360
id: window
visible: true
title: qsTr("Spectral")
background: Rectangle {
color: MSettings.darkTheme ? "#303030" : "#FFFFFF"
}
Platform.SystemTrayIcon {
visible: MSettings.showTray
iconSource: "qrc:/assets/img/icon.png"
menu: Platform.Menu {
Platform.MenuItem {
text: qsTr("Hide Window")
onTriggered: hideWindow()
}
Platform.MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit()
}
}
onActivated: showWindow()
}
Controller {
id: spectralController
quitOnLastWindowClosed: !MSettings.showTray
onNotificationClicked: {
roomListForm.enteredRoom = spectralController.connection.room(roomId)
roomForm.goToEvent(eventId)
showWindow()
}
onErrorOccured: {
roomListForm.errorControl.error = error
roomListForm.errorControl.detail = detail
roomListForm.errorControl.visible = true
}
onSyncDone: roomListForm.errorControl.visible = false
}
Shortcut {
sequence: StandardKey.Quit
onActivated: Qt.quit()
}
Dialog {
property bool busy: false
width: 360
x: (window.width - width) / 2
y: (window.height - height) / 2
id: loginDialog
parent: ApplicationWindow.overlay
title: "Login"
contentItem: ColumnLayout {
AutoTextField {
Layout.fillWidth: true
id: serverField
placeholderText: "Server Address"
text: "https://matrix.org"
}
AutoTextField {
Layout.fillWidth: true
id: usernameField
placeholderText: "Username"
onAccepted: passwordField.forceActiveFocus()
}
AutoTextField {
Layout.fillWidth: true
id: passwordField
placeholderText: "Password"
echoMode: TextInput.Password
onAccepted: loginDialog.doLogin()
}
}
footer: DialogButtonBox {
Button {
text: "Cancel"
flat: true
enabled: !loginDialog.busy
onClicked: loginDialog.close()
}
Button {
text: "OK"
flat: true
enabled: !loginDialog.busy
onClicked: loginDialog.doLogin()
}
ToolTip {
id: loginButtonTooltip
}
}
onVisibleChanged: {
if (visible) spectralController.onErrorOccured.connect(showError)
else spectralController.onErrorOccured.disconnect(showError)
}
function showError(error, detail) {
loginDialog.busy = false
loginButtonTooltip.text = error + ": " + detail
loginButtonTooltip.open()
}
function doLogin() {
if (!(serverField.text.startsWith("http") && serverField.text.includes("://"))) {
loginButtonTooltip.text = "Server address should start with http(s)://"
loginButtonTooltip.open()
return
}
loginDialog.busy = true
spectralController.loginWithCredentials(serverField.text, usernameField.text, passwordField.text)
spectralController.connectionAdded.connect(function(conn) {
busy = false
loginDialog.close()
})
}
}
Dialog {
anchors.centerIn: parent
width: 480
id: detailDialog
contentItem: Column {
id: detailColumn
spacing: 0
Repeater {
model: AccountListModel{
controller: spectralController
}
delegate: Item {
width: detailColumn.width
height: 72
RowLayout {
anchors.fill: parent
anchors.margins: 12
spacing: 12
Avatar {
Layout.preferredWidth: height
Layout.fillHeight: true
source: user.avatarMediaId
hint: user.displayName || "No Name"
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Label {
Layout.fillWidth: true
text: user.displayName || "No Name"
color: MPalette.foreground
font.pixelSize: 16
font.bold: true
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
Label {
Layout.fillWidth: true
text: connection === spectralController.connection ? "Active" : "Online"
color: MPalette.lighter
font.pixelSize: 13
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
}
Menu {
id: contextMenu
MenuItem {
text: "Logout"
onClicked: spectralController.logout(connection)
}
}
RippleEffect {
anchors.fill: parent
onPrimaryClicked: spectralController.connection = connection
onSecondaryClicked: contextMenu.popup()
}
}
}
RowLayout {
width: parent.width
MenuSeparator {
Layout.fillWidth: true
}
ToolButton {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
contentItem: MaterialIcon {
icon: "\ue145"
color: MPalette.lighter
}
onClicked: loginDialog.open()
}
}
Control {
width: parent.width
contentItem: RowLayout {
MaterialIcon {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
color: MPalette.foreground
icon: "\ue7ff"
}
Label {
Layout.fillWidth: true
color: MPalette.foreground
text: "Start a Chat"
}
}
RippleEffect {
anchors.fill: parent
onPrimaryClicked: joinRoomDialog.open()
}
}
Control {
width: parent.width
contentItem: RowLayout {
MaterialIcon {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
color: MPalette.foreground
icon: "\ue7fc"
}
Label {
Layout.fillWidth: true
color: MPalette.foreground
text: "Create a Room"
}
}
RippleEffect {
anchors.fill: parent
onPrimaryClicked: createRoomDialog.open()
}
}
MenuSeparator {
width: parent.width
}
Control {
width: parent.width
contentItem: RowLayout {
MaterialIcon {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
color: MPalette.foreground
icon: "\ue3a9"
}
Label {
Layout.fillWidth: true
color: MPalette.foreground
text: "Night Mode"
}
Switch {
id: darkThemeSwitch
checked: MSettings.darkTheme
onCheckedChanged: MSettings.darkTheme = checked
}
}
RippleEffect {
anchors.fill: parent
onPrimaryClicked: darkThemeSwitch.checked = !darkThemeSwitch.checked
}
}
Control {
width: parent.width
contentItem: RowLayout {
MaterialIcon {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
color: MPalette.foreground
icon: "\ue5d2"
}
Label {
Layout.fillWidth: true
color: MPalette.foreground
text: "Enable System Tray"
}
Switch {
id: trayIconSwitch
checked: MSettings.showTray
onCheckedChanged: MSettings.showTray = checked
}
}
RippleEffect {
anchors.fill: parent
onPrimaryClicked: trayIconSwitch.checked = !trayIconSwitch.checked
}
}
Control {
width: parent.width
contentItem: RowLayout {
MaterialIcon {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
color: MPalette.foreground
icon: "\ue7f5"
}
Label {
Layout.fillWidth: true
color: MPalette.foreground
text: "Enable Notifications"
}
Switch {
id: notificationsSwitch
checked: MSettings.showNotification
onCheckedChanged: MSettings.showNotification = checked
}
}
RippleEffect {
anchors.fill: parent
onPrimaryClicked: notificationsSwitch.checked = !notificationsSwitch.checked
}
}
}
}
Dialog {
anchors.centerIn: parent
width: 360
id: joinRoomDialog
title: "Start a Chat"
contentItem: ColumnLayout {
AutoTextField {
Layout.fillWidth: true
id: identifierField
placeholderText: "Room Alias/User ID"
}
}
standardButtons: Dialog.Ok | Dialog.Cancel
onAccepted: {
var identifier = identifierField.text
var firstChar = identifier.charAt(0)
if (firstChar == "@") {
spectralController.createDirectChat(spectralController.connection, identifier)
} else if (firstChar == "!" || firstChar == "#") {
spectralController.joinRoom(spectralController.connection, identifier)
}
}
}
Dialog {
anchors.centerIn: parent
width: 360
id: createRoomDialog
title: "Create a Room"
contentItem: ColumnLayout {
AutoTextField {
Layout.fillWidth: true
id: roomNameField
placeholderText: "Room Name"
}
AutoTextField {
Layout.fillWidth: true
id: roomTopicField
placeholderText: "Room Topic"
}
}
standardButtons: Dialog.Ok | Dialog.Cancel
onAccepted: spectralController.createRoom(spectralController.connection, roomNameField.text, roomTopicField.text)
}
Drawer {
width: Math.min((inPortrait ? 0.67 : 0.3) * window.width, 360)
height: window.height
modal: inPortrait
interactive: inPortrait
position: inPortrait ? 0 : 1
visible: !inPortrait
id: roomListDrawer
RoomListPanel {
anchors.fill: parent
id: roomListForm
clip: true
controller: spectralController
onLeaveRoom: roomForm.saveReadMarker(room)
}
}
RoomPanel {
anchors.fill: parent
anchors.leftMargin: !inPortrait ? roomListDrawer.width : undefined
anchors.rightMargin: !inPortrait && roomDrawer.visible ? roomDrawer.width : undefined
id: roomForm
clip: true
currentRoom: roomListForm.enteredRoom
}
RoomDrawer {
width: Math.min((inPortrait ? 0.67 : 0.3) * window.width, 360)
height: window.height
modal: inPortrait
interactive: inPortrait
edge: Qt.RightEdge
id: roomDrawer
room: roomListForm.enteredRoom
}
Binding {
target: imageProvider
property: "connection"
value: spectralController.connection
}
function showWindow() {
window.show()
window.raise()
window.requestActivate()
}
function hideWindow() {
window.hide()
}
Component.onCompleted: {
spectralController.initiated.connect(function() {
if (spectralController.accountCount == 0) loginDialog.open()
})
}
}