diff --git a/resources/langs/nheko_de.ts b/resources/langs/nheko_de.ts
index 29a04355..c499874b 100644
--- a/resources/langs/nheko_de.ts
+++ b/resources/langs/nheko_de.ts
@@ -319,7 +319,7 @@
<message>
<location line="+66"/>
<source>Failed to kick %1 from %2: %3</source>
- <translation>Kontte %1 nicht aus %2 entfernen: %3</translation>
+ <translation>Konnte %1 nicht aus %2 entfernen: %3</translation>
</message>
</context>
<context>
diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml
index 33db1b1a..e3aa3e48 100644
--- a/resources/qml/ChatPage.qml
+++ b/resources/qml/ChatPage.qml
@@ -97,6 +97,7 @@ Rectangle {
implicitHeight: chatPage.height
collapsed: parent.collapsed
+ anchors.fill: parent
}
Binding {
diff --git a/resources/qml/MatrixTextField.qml b/resources/qml/MatrixTextField.qml
index af077124..05f2c82f 100644
--- a/resources/qml/MatrixTextField.qml
+++ b/resources/qml/MatrixTextField.qml
@@ -8,29 +8,139 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import im.nheko 1.0
-TextField {
- id: input
- property alias backgroundColor: backgroundRect.color
+ColumnLayout {
+ id: c
+ property color backgroundColor: Nheko.colors.base
+ property alias color: labelC.color
+ property alias textPadding: input.padding
+ property alias text: input.text
+ property alias label: labelC.text
+ property alias placeholderText: input.placeholderText
+ property alias font: input.font
+ property alias echoMode: input.echoMode
+ property alias selectByMouse: input.selectByMouse
- palette: Nheko.colors
- color: Nheko.colors.text
+ Timer {
+ id: timer
+ interval: 350
+ onTriggered: editingFinished()
+ }
+
+ onTextChanged: timer.restart()
+
+ signal textEdited
+ signal accepted
+ signal editingFinished
+
+ function forceActiveFocus() {
+ input.forceActiveFocus();
+ }
+
+ ToolTip.delay: Nheko.tooltipDelay
+ ToolTip.visible: hover.hovered
+
+ spacing: 0
+
+ Item {
+ Layout.fillWidth: true
+ Layout.preferredHeight: labelC.contentHeight
+ Layout.margins: input.padding
+ Layout.bottomMargin: Nheko.paddingSmall
+ visible: labelC.text
+
+ z: 1
+
+ Label {
+ id: labelC
+
+ y: contentHeight + input.padding + Nheko.paddingSmall
+ enabled: false
+
+ palette: Nheko.colors
+ color: Nheko.colors.text
+ font.pixelSize: input.font.pixelSize
+ font.weight: Font.DemiBold
+ font.letterSpacing: input.font.pixelSize * 0.02
+ width: parent.width
+
+ state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
+
+ states: State {
+ name: "focused"
+
+ PropertyChanges {
+ target: labelC
+ y: 0
+ }
+
+ PropertyChanges {
+ target: input
+ opacity: 1
+ }
+
+ }
+
+ transitions: Transition {
+ from: ""
+ to: "focused"
+ reversible: true
+
+ NumberAnimation {
+ target: labelC
+ properties: "y"
+ duration: 210
+ easing.type: Easing.InCubic
+ alwaysRunToEnd: true
+ }
+
+ NumberAnimation {
+ target: input
+ properties: "opacity"
+ duration: 210
+ easing.type: Easing.InCubic
+ alwaysRunToEnd: true
+ }
+
+ }
+ }
+ }
+
+ TextField {
+ id: input
+ Layout.fillWidth: true
+
+ palette: Nheko.colors
+ color: labelC.color
+ opacity: labelC.text ? 0 : 1
+
+ onTextEdited: c.textEdited()
+ onAccepted: c.accepted()
+ onEditingFinished: c.editingFinished()
+
+
+ background: Rectangle {
+ id: backgroundRect
+
+ color: labelC.text ? "transparent" : backgroundColor
+ }
+
+ }
Rectangle {
id: blueBar
- anchors.top: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
+ Layout.fillWidth: true
+
color: Nheko.colors.highlight
height: 1
- width: parent.width
Rectangle {
id: blackBar
- anchors.verticalCenter: blueBar.verticalCenter
+ anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
- height: parent.height + 1
+ height: parent.height*2
width: 0
color: Nheko.colors.text
@@ -50,11 +160,12 @@ TextField {
to: "focused"
reversible: true
+
NumberAnimation {
target: blackBar
properties: "width"
- duration: 500
- easing.type: Easing.InOutQuad
+ duration: 310
+ easing.type: Easing.InCubic
alwaysRunToEnd: true
}
@@ -64,10 +175,8 @@ TextField {
}
- background: Rectangle {
- id: backgroundRect
-
- color: Nheko.colors.base
+ HoverHandler {
+ id: hover
+ enabled: c.ToolTip.text
}
-
}
diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml
index e6286bc6..fb3818df 100644
--- a/resources/qml/PrivacyScreen.qml
+++ b/resources/qml/PrivacyScreen.qml
@@ -5,6 +5,7 @@
import QtGraphicalEffects 1.0
import QtQuick 2.12
+import QtQuick.Window 2.2
import im.nheko 1.0
Item {
@@ -15,7 +16,7 @@ Item {
Connections {
function onFocusChanged() {
- if (TimelineManager.isWindowFocused) {
+ if (MainWindow.active) {
screenSaverTimer.stop();
screenSaver.state = "Invisible";
} else {
@@ -32,7 +33,7 @@ Item {
id: screenSaverTimer
interval: screenTimeout * 1000
- running: true
+ running: !MainWindow.active
onTriggered: {
screenSaver.state = "Visible";
}
diff --git a/resources/qml/QuickSwitcher.qml b/resources/qml/QuickSwitcher.qml
index 174951a0..8747c47d 100644
--- a/resources/qml/QuickSwitcher.qml
+++ b/resources/qml/QuickSwitcher.qml
@@ -11,7 +11,6 @@ Popup {
id: quickSwitcher
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
- property int textMargin: Math.round(textHeight / 8)
background: null
width: Math.round(parent.width / 2)
@@ -34,7 +33,6 @@ Popup {
anchors.fill: parent
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
- padding: textMargin
color: Nheko.colors.text
onTextEdited: {
completerPopup.completer.searchString = text;
@@ -60,7 +58,7 @@ Popup {
id: completerPopup
x: roomTextInput.x
- y: roomTextInput.y + quickSwitcher.textHeight + quickSwitcher.textMargin
+ y: roomTextInput.y + quickSwitcher.textHeight
visible: roomTextInput.length > 0
width: parent.width
completerName: "room"
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index 6e7b683f..da205950 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -385,7 +385,7 @@ Page {
header: ColumnLayout {
spacing: 0
- Rectangle {
+ Pane {
id: userInfoPanel
function openUserProfile() {
@@ -396,12 +396,15 @@ Page {
userProfile.show();
}
- color: Nheko.colors.window
+
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
- Layout.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium
+ //Layout.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium
+ padding: Nheko.paddingMedium
Layout.minimumHeight: 40
+ background: Rectangle {color: Nheko.colors.window}
+
InputDialog {
id: statusDialog
@@ -442,14 +445,12 @@ Page {
gesturePolicy: TapHandler.ReleaseWithinBounds
}
- RowLayout {
+ contentItem: RowLayout {
id: userInfoGrid
property var profile: Nheko.currentUser
spacing: Nheko.paddingMedium
- anchors.fill: parent
- anchors.margins: Nheko.paddingMedium
Avatar {
id: avatar
@@ -614,19 +615,17 @@ Page {
Layout.fillWidth: true
}
- Rectangle {
- color: Nheko.colors.window
+ Pane {
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
- Layout.preferredHeight: buttonRow.implicitHeight
Layout.minimumHeight: 40
- RowLayout {
- id: buttonRow
+ horizontalPadding: Nheko.paddingMedium
+ verticalPadding: 0
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.margins: Nheko.paddingMedium
+ background: Rectangle {color: Nheko.colors.window}
+ contentItem: RowLayout {
+ id: buttonRow
ImageButton {
Layout.fillWidth: true
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index e4b164e4..88d3e7c6 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -9,6 +9,7 @@ import "./dialogs"
import "./emoji"
import "./pages"
import "./voip"
+import "./ui"
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.15
@@ -17,10 +18,12 @@ import QtQuick.Window 2.15
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
-Page {
+Pane {
id: timelineRoot
palette: Nheko.colors
+ background: null
+ padding: 0
FontMetrics {
id: fontMetrics
@@ -154,10 +157,14 @@ Page {
}
Shortcut {
+ sequence: StandardKey.Quit
+ onActivated: Qt.quit()
+ }
+
+ Shortcut {
sequence: "Ctrl+K"
onActivated: {
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
- TimelineManager.focusTimeline();
quickSwitch.open();
}
}
@@ -165,7 +172,6 @@ Page {
Shortcut {
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
sequences: ["Alt+A", "Ctrl+Shift+A"]
- context: Qt.ApplicationShortcut
onActivated: Rooms.nextRoomWithActivity()
}
@@ -366,9 +372,51 @@ Page {
id: mainWindow
anchors.fill: parent
- initialItem: ChatPage {
- //anchors.fill: parent
+ initialItem: welcomePage
+ }
+
+ Component {
+ id: welcomePage
+
+ WelcomePage {
+ }
+ }
+
+ Component {
+ id: chatPage
+
+ ChatPage {
+ }
+ }
+
+ Component {
+ id: loginPage
+
+ LoginPage {
+ }
+ }
+
+ Component {
+ id: registerPage
+
+ RegisterPage {
+ }
+ }
+
+ Snackbar { id: snackbar }
+
+ Connections {
+ function onSwitchToChatPage() {
+ mainWindow.replace(null, chatPage);
+ }
+ function onSwitchToLoginPage(error) {
+ mainWindow.replace(welcomePage, {}, loginPage, {"error": error}, StackView.PopTransition);
+ }
+ function onShowNotification(msg) {
+ snackbar.showNotification(msg);
+ console.log("New snack: " + msg);
}
+ target: MainWindow
}
}
diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml
index 1c0115e2..c9a8d0d2 100644
--- a/resources/qml/TopBar.qml
+++ b/resources/qml/TopBar.qml
@@ -12,7 +12,7 @@ import im.nheko 1.0
import "./delegates"
-Rectangle {
+Pane {
id: topBar
property bool showBackButton: false
@@ -28,7 +28,11 @@ Rectangle {
Layout.fillWidth: true
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
z: 3
- color: Nheko.colors.window
+
+ padding: 0
+ background: Rectangle {
+ color: Nheko.colors.window
+ }
TapHandler {
onSingleTapped: {
@@ -65,248 +69,250 @@ Rectangle {
grabPermissions: PointerHandler.TakeOverForbidden | PointerHandler.CanTakeOverFromAnything
}
- GridLayout {
- id: topLayout
+ contentItem: Item {
+ GridLayout {
+ id: topLayout
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.margins: Nheko.paddingMedium
- anchors.verticalCenter: parent.verticalCenter
- columnSpacing: Nheko.paddingSmall
- rowSpacing: Nheko.paddingSmall
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: Nheko.paddingMedium
+ anchors.verticalCenter: parent.verticalCenter
+ columnSpacing: Nheko.paddingSmall
+ rowSpacing: Nheko.paddingSmall
- ImageButton {
- id: backToRoomsButton
+ ImageButton {
+ id: backToRoomsButton
- Layout.column: 0
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
- Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
- Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- visible: showBackButton
- image: ":/icons/icons/ui/angle-arrow-left.svg"
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Back to room list")
- onClicked: Rooms.resetCurrentRoom()
- }
+ Layout.column: 0
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
+ Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
+ visible: showBackButton
+ image: ":/icons/icons/ui/angle-arrow-left.svg"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Back to room list")
+ onClicked: Rooms.resetCurrentRoom()
+ }
- Avatar {
- Layout.column: 1
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
- width: Nheko.avatarSize
- height: Nheko.avatarSize
- url: avatarUrl.replace("mxc://", "image://MxcImage/")
- roomid: roomId
- userid: isDirect ? directChatOtherUserId : ""
- displayName: roomName
- enabled: false
- }
+ Avatar {
+ Layout.column: 1
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ width: Nheko.avatarSize
+ height: Nheko.avatarSize
+ url: avatarUrl.replace("mxc://", "image://MxcImage/")
+ roomid: roomId
+ userid: isDirect ? directChatOtherUserId : ""
+ displayName: roomName
+ enabled: false
+ }
- Label {
- Layout.fillWidth: true
- Layout.column: 2
- Layout.row: 0
- color: Nheko.colors.text
- font.pointSize: fontMetrics.font.pointSize * 1.1
- text: roomName
- maximumLineCount: 1
- elide: Text.ElideRight
- textFormat: Text.RichText
- }
+ Label {
+ Layout.fillWidth: true
+ Layout.column: 2
+ Layout.row: 0
+ color: Nheko.colors.text
+ font.pointSize: fontMetrics.font.pointSize * 1.1
+ text: roomName
+ maximumLineCount: 1
+ elide: Text.ElideRight
+ textFormat: Text.RichText
+ }
- MatrixText {
- id: roomTopicC
- Layout.fillWidth: true
- Layout.column: 2
- Layout.row: 1
- Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
- selectByMouse: false
- enabled: false
- clip: true
- text: roomTopic
- }
+ MatrixText {
+ id: roomTopicC
+ Layout.fillWidth: true
+ Layout.column: 2
+ Layout.row: 1
+ Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
+ selectByMouse: false
+ enabled: false
+ clip: true
+ text: roomTopic
+ }
- EncryptionIndicator {
- Layout.column: 3
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
- Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- sourceSize.height: Layout.preferredHeight * Screen.devicePixelRatio
- sourceSize.width: Layout.preferredWidth * Screen.devicePixelRatio
- visible: isEncrypted
- encrypted: isEncrypted
- trust: trustlevel
- ToolTip.text: {
- if (!encrypted)
- return qsTr("This room is not encrypted!");
+ EncryptionIndicator {
+ Layout.column: 3
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
+ Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
+ sourceSize.height: Layout.preferredHeight * Screen.devicePixelRatio
+ sourceSize.width: Layout.preferredWidth * Screen.devicePixelRatio
+ visible: isEncrypted
+ encrypted: isEncrypted
+ trust: trustlevel
+ ToolTip.text: {
+ if (!encrypted)
+ return qsTr("This room is not encrypted!");
- switch (trust) {
- case Crypto.Verified:
- return qsTr("This room contains only verified devices.");
- case Crypto.TOFU:
- return qsTr("This room contains verified devices and devices which have never changed their master key.");
- default:
- return qsTr("This room contains unverified devices!");
+ switch (trust) {
+ case Crypto.Verified:
+ return qsTr("This room contains only verified devices.");
+ case Crypto.TOFU:
+ return qsTr("This room contains verified devices and devices which have never changed their master key.");
+ default:
+ return qsTr("This room contains unverified devices!");
+ }
}
}
- }
- ImageButton {
- id: pinButton
+ ImageButton {
+ id: pinButton
- property bool pinsShown: !Settings.hiddenPins.includes(roomId)
+ property bool pinsShown: !Settings.hiddenPins.includes(roomId)
- visible: !!room && room.pinnedMessages.length > 0
- Layout.column: 4
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
- Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
- Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Show or hide pinned messages")
- onClicked: {
- var ps = Settings.hiddenPins;
- if (pinsShown) {
- ps.push(roomId);
- } else {
- const index = ps.indexOf(roomId);
- if (index > -1) {
- ps.splice(index, 1);
+ visible: !!room && room.pinnedMessages.length > 0
+ Layout.column: 4
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
+ Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
+ image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Show or hide pinned messages")
+ onClicked: {
+ var ps = Settings.hiddenPins;
+ if (pinsShown) {
+ ps.push(roomId);
+ } else {
+ const index = ps.indexOf(roomId);
+ if (index > -1) {
+ ps.splice(index, 1);
+ }
}
+ Settings.hiddenPins = ps;
}
- Settings.hiddenPins = ps;
+
}
- }
+ ImageButton {
+ id: roomOptionsButton
- ImageButton {
- id: roomOptionsButton
+ visible: !!room
+ Layout.column: 5
+ Layout.row: 0
+ Layout.rowSpan: 2
+ Layout.alignment: Qt.AlignVCenter
+ Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
+ Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
+ image: ":/icons/icons/ui/options.svg"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Room options")
+ onClicked: roomOptionsMenu.open(roomOptionsButton)
- visible: !!room
- Layout.column: 5
- Layout.row: 0
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignVCenter
- Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
- Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- image: ":/icons/icons/ui/options.svg"
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Room options")
- onClicked: roomOptionsMenu.open(roomOptionsButton)
+ Platform.Menu {
+ id: roomOptionsMenu
- Platform.Menu {
- id: roomOptionsMenu
+ Platform.MenuItem {
+ visible: room ? room.permissions.canInvite() : false
+ text: qsTr("Invite users")
+ onTriggered: TimelineManager.openInviteUsers(roomId)
+ }
- Platform.MenuItem {
- visible: room ? room.permissions.canInvite() : false
- text: qsTr("Invite users")
- onTriggered: TimelineManager.openInviteUsers(roomId)
- }
+ Platform.MenuItem {
+ text: qsTr("Members")
+ onTriggered: TimelineManager.openRoomMembers(room)
+ }
- Platform.MenuItem {
- text: qsTr("Members")
- onTriggered: TimelineManager.openRoomMembers(room)
- }
+ Platform.MenuItem {
+ text: qsTr("Leave room")
+ onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
+ }
- Platform.MenuItem {
- text: qsTr("Leave room")
- onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
- }
+ Platform.MenuItem {
+ text: qsTr("Settings")
+ onTriggered: TimelineManager.openRoomSettings(roomId)
+ }
- Platform.MenuItem {
- text: qsTr("Settings")
- onTriggered: TimelineManager.openRoomSettings(roomId)
}
}
- }
-
- ScrollView {
- id: pinnedMessages
+ ScrollView {
+ id: pinnedMessages
- Layout.row: 2
- Layout.column: 2
- Layout.columnSpan: 3
+ Layout.row: 2
+ Layout.column: 2
+ Layout.columnSpan: 3
- Layout.fillWidth: true
- Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
+ Layout.fillWidth: true
+ Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
- visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
- clip: true
+ visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
+ clip: true
- palette: Nheko.colors
- ScrollBar.horizontal.visible: false
+ palette: Nheko.colors
+ ScrollBar.horizontal.visible: false
- ListView {
+ ListView {
- spacing: Nheko.paddingSmall
- model: room ? room.pinnedMessages : undefined
- delegate: RowLayout {
- required property string modelData
+ spacing: Nheko.paddingSmall
+ model: room ? room.pinnedMessages : undefined
+ delegate: RowLayout {
+ required property string modelData
- width: ListView.view.width
- height: implicitHeight
+ width: ListView.view.width
+ height: implicitHeight
- Reply {
- property var e: room ? room.getDump(modelData, "") : {}
- Layout.fillWidth: true
- Layout.preferredHeight: height
+ Reply {
+ property var e: room ? room.getDump(modelData, "") : {}
+ Layout.fillWidth: true
+ Layout.preferredHeight: height
- userColor: TimelineManager.userColor(e.userId, Nheko.colors.window)
- blurhash: e.blurhash ?? ""
- body: e.body ?? ""
- formattedBody: e.formattedBody ?? ""
- eventId: e.eventId ?? ""
- filename: e.filename ?? ""
- filesize: e.filesize ?? ""
- proportionalHeight: e.proportionalHeight ?? 1
- type: e.type ?? MtxEvent.UnknownMessage
- typeString: e.typeString ?? ""
- url: e.url ?? ""
- originalWidth: e.originalWidth ?? 0
- isOnlyEmoji: e.isOnlyEmoji ?? false
- userId: e.userId ?? ""
- userName: e.userName ?? ""
- encryptionError: e.encryptionError ?? ""
- }
+ userColor: TimelineManager.userColor(e.userId, Nheko.colors.window)
+ blurhash: e.blurhash ?? ""
+ body: e.body ?? ""
+ formattedBody: e.formattedBody ?? ""
+ eventId: e.eventId ?? ""
+ filename: e.filename ?? ""
+ filesize: e.filesize ?? ""
+ proportionalHeight: e.proportionalHeight ?? 1
+ type: e.type ?? MtxEvent.UnknownMessage
+ typeString: e.typeString ?? ""
+ url: e.url ?? ""
+ originalWidth: e.originalWidth ?? 0
+ isOnlyEmoji: e.isOnlyEmoji ?? false
+ userId: e.userId ?? ""
+ userName: e.userName ?? ""
+ encryptionError: e.encryptionError ?? ""
+ }
- ImageButton {
- id: deletePinButton
+ ImageButton {
+ id: deletePinButton
- Layout.preferredHeight: 16
- Layout.preferredWidth: 16
- Layout.alignment: Qt.AlignTop | Qt.AlignLeft
- visible: room.permissions.canChange(MtxEvent.PinnedEvents)
+ Layout.preferredHeight: 16
+ Layout.preferredWidth: 16
+ Layout.alignment: Qt.AlignTop | Qt.AlignLeft
+ visible: room.permissions.canChange(MtxEvent.PinnedEvents)
- hoverEnabled: true
- image: ":/icons/icons/ui/dismiss.svg"
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Unpin")
+ hoverEnabled: true
+ image: ":/icons/icons/ui/dismiss.svg"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Unpin")
- onClicked: room.unpin(modelData)
+ onClicked: room.unpin(modelData)
+ }
}
- }
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
+ ScrollHelper {
+ flickable: parent
+ anchors.fill: parent
+ enabled: !Settings.mobileMode
+ }
}
}
}
- }
- CursorShape {
- anchors.fill: parent
- anchors.bottomMargin: pinnedMessages.visible ? pinnedMessages.height : 0
- cursorShape: Qt.PointingHandCursor
+ CursorShape {
+ anchors.fill: parent
+ anchors.bottomMargin: pinnedMessages.visible ? pinnedMessages.height : 0
+ cursorShape: Qt.PointingHandCursor
+ }
}
}
diff --git a/resources/qml/components/FlatButton.qml b/resources/qml/components/FlatButton.qml
index 8ca3f104..72184d28 100644
--- a/resources/qml/components/FlatButton.qml
+++ b/resources/qml/components/FlatButton.qml
@@ -12,7 +12,7 @@ import im.nheko 1.0
Button {
id: control
- implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.5)
+ implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70)
implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight)
hoverEnabled: true
@@ -42,7 +42,7 @@ Button {
background: Rectangle {
//height: control.contentItem.implicitHeight * 2
//width: control.contentItem.implicitWidth * 2
- radius: height / 6
+ radius: height / 8
color: Qt.lighter(Nheko.colors.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index c00a0bdb..90dc9ac4 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -21,7 +21,6 @@ ApplicationWindow {
minimumHeight: stack.implicitHeight
width: stack.implicitWidth
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(dialog)
StackView {
id: stack
diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml
index 3ba04d94..eb420fce 100644
--- a/resources/qml/dialogs/ImagePackEditorDialog.qml
+++ b/resources/qml/dialogs/ImagePackEditorDialog.qml
@@ -12,8 +12,6 @@ import QtQuick.Layouts 1.12
import im.nheko 1.0
ApplicationWindow {
- //Component.onCompleted: Nheko.reparent(win)
-
id: win
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
@@ -175,39 +173,37 @@ ApplicationWindow {
}
}
- MatrixText {
- visible: imagePack.roomid
- text: qsTr("State key")
- }
-
MatrixTextField {
+ id: statekeyField
+
visible: imagePack.roomid
Layout.fillWidth: true
+ Layout.columnSpan: 2
+ label: qsTr("State key")
text: imagePack.statekey
onTextEdited: imagePack.statekey = text
}
- MatrixText {
- text: qsTr("Packname")
- }
-
MatrixTextField {
Layout.fillWidth: true
+ Layout.columnSpan: 2
+ label: qsTr("Packname")
text: imagePack.packname
onTextEdited: imagePack.packname = text
}
- MatrixText {
- text: qsTr("Attribution")
- }
-
MatrixTextField {
Layout.fillWidth: true
+ Layout.columnSpan: 2
+ label: qsTr("Attribution")
text: imagePack.attribution
onTextEdited: imagePack.attribution = text
}
MatrixText {
+ Layout.margins: statekeyField.textPadding
+ font.weight: Font.DemiBold
+ font.letterSpacing: font.pixelSize * 0.02
text: qsTr("Use as Emoji")
}
@@ -218,6 +214,9 @@ ApplicationWindow {
}
MatrixText {
+ Layout.margins: statekeyField.textPadding
+ font.weight: Font.DemiBold
+ font.letterSpacing: font.pixelSize * 0.02
text: qsTr("Use as Sticker")
}
@@ -253,27 +252,28 @@ ApplicationWindow {
Layout.alignment: Qt.AlignHCenter
}
- MatrixText {
- text: qsTr("Shortcode")
- }
-
MatrixTextField {
Layout.fillWidth: true
+ Layout.columnSpan: 2
+ label: qsTr("Shortcode")
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode)
}
- MatrixText {
- text: qsTr("Body")
- }
-
MatrixTextField {
+ id: bodyField
+
Layout.fillWidth: true
+ Layout.columnSpan: 2
+ label: qsTr("Body")
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
}
MatrixText {
+ Layout.margins: bodyField.textPadding
+ font.weight: Font.DemiBold
+ font.letterSpacing: font.pixelSize * 0.02
text: qsTr("Use as Emoji")
}
@@ -284,6 +284,9 @@ ApplicationWindow {
}
MatrixText {
+ Layout.margins: bodyField.textPadding
+ font.weight: Font.DemiBold
+ font.letterSpacing: font.pixelSize * 0.02
text: qsTr("Use as Sticker")
}
@@ -294,6 +297,9 @@ ApplicationWindow {
}
MatrixText {
+ Layout.margins: bodyField.textPadding
+ font.weight: Font.DemiBold
+ font.letterSpacing: font.pixelSize * 0.02
text: qsTr("Remove from pack")
}
diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml
index fa079855..18c32c41 100644
--- a/resources/qml/dialogs/ImagePackSettingsDialog.qml
+++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml
@@ -28,7 +28,6 @@ ApplicationWindow {
color: Nheko.colors.base
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(win)
Component {
id: packEditor
diff --git a/resources/qml/dialogs/InputDialog.qml b/resources/qml/dialogs/InputDialog.qml
index 63ca3181..cf1474dc 100644
--- a/resources/qml/dialogs/InputDialog.qml
+++ b/resources/qml/dialogs/InputDialog.qml
@@ -18,7 +18,6 @@ ApplicationWindow {
modality: Qt.NonModal
flags: Qt.Dialog
- Component.onCompleted: Nheko.reparent(inputDialog)
width: 350
height: fontMetrics.lineSpacing * 7
diff --git a/resources/qml/dialogs/InviteDialog.qml b/resources/qml/dialogs/InviteDialog.qml
index 917bc856..e7dd4e3a 100644
--- a/resources/qml/dialogs/InviteDialog.qml
+++ b/resources/qml/dialogs/InviteDialog.qml
@@ -37,7 +37,6 @@ ApplicationWindow {
palette: Nheko.colors
color: Nheko.colors.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(inviteDialogRoot)
Shortcut {
sequence: "Ctrl+Enter"
diff --git a/resources/qml/dialogs/JoinRoomDialog.qml b/resources/qml/dialogs/JoinRoomDialog.qml
index 9ce6bcf1..e49f538d 100644
--- a/resources/qml/dialogs/JoinRoomDialog.qml
+++ b/resources/qml/dialogs/JoinRoomDialog.qml
@@ -17,7 +17,6 @@ ApplicationWindow {
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
palette: Nheko.colors
color: Nheko.colors.window
- Component.onCompleted: Nheko.reparent(joinRoomRoot)
width: 350
height: fontMetrics.lineSpacing * 7
diff --git a/resources/qml/dialogs/PhoneNumberInputDialog.qml b/resources/qml/dialogs/PhoneNumberInputDialog.qml
index 399b11d5..9c36c98f 100644
--- a/resources/qml/dialogs/PhoneNumberInputDialog.qml
+++ b/resources/qml/dialogs/PhoneNumberInputDialog.qml
@@ -19,7 +19,6 @@ ApplicationWindow {
modality: Qt.NonModal
flags: Qt.Dialog
- Component.onCompleted: Nheko.reparent(inputDialog)
width: 350
height: fontMetrics.lineSpacing * 7
diff --git a/resources/qml/dialogs/RawMessageDialog.qml b/resources/qml/dialogs/RawMessageDialog.qml
index 34104394..774b078b 100644
--- a/resources/qml/dialogs/RawMessageDialog.qml
+++ b/resources/qml/dialogs/RawMessageDialog.qml
@@ -17,7 +17,6 @@ ApplicationWindow {
palette: Nheko.colors
color: Nheko.colors.window
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(rawMessageRoot)
Shortcut {
sequence: StandardKey.Cancel
diff --git a/resources/qml/dialogs/ReadReceipts.qml b/resources/qml/dialogs/ReadReceipts.qml
index aced4374..da87996e 100644
--- a/resources/qml/dialogs/ReadReceipts.qml
+++ b/resources/qml/dialogs/ReadReceipts.qml
@@ -22,7 +22,6 @@ ApplicationWindow {
palette: Nheko.colors
color: Nheko.colors.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(readReceiptsRoot)
Shortcut {
sequence: StandardKey.Cancel
diff --git a/resources/qml/dialogs/RoomDirectory.qml b/resources/qml/dialogs/RoomDirectory.qml
index f458ac51..36c29a0b 100644
--- a/resources/qml/dialogs/RoomDirectory.qml
+++ b/resources/qml/dialogs/RoomDirectory.qml
@@ -22,7 +22,6 @@ ApplicationWindow {
color: Nheko.colors.window
modality: Qt.WindowModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(roomDirectoryWindow)
title: qsTr("Explore Public Rooms")
Shortcut {
@@ -189,7 +188,6 @@ ApplicationWindow {
Layout.fillWidth: true
selectByMouse: true
font.pixelSize: fontMetrics.font.pixelSize
- padding: Nheko.paddingMedium
color: Nheko.colors.text
placeholderText: qsTr("Search for public rooms")
onTextChanged: searchTimer.restart()
@@ -200,7 +198,6 @@ ApplicationWindow {
Layout.minimumWidth: 0.3 * header.width
Layout.maximumWidth: 0.3 * header.width
- padding: Nheko.paddingMedium
color: Nheko.colors.text
placeholderText: qsTr("Choose custom homeserver")
onTextChanged: publicRooms.setMatrixServer(text)
diff --git a/resources/qml/dialogs/RoomMembers.qml b/resources/qml/dialogs/RoomMembers.qml
index 89cce414..55d5488b 100644
--- a/resources/qml/dialogs/RoomMembers.qml
+++ b/resources/qml/dialogs/RoomMembers.qml
@@ -24,7 +24,6 @@ ApplicationWindow {
palette: Nheko.colors
color: Nheko.colors.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(roomMembersRoot)
Shortcut {
sequence: StandardKey.Cancel
diff --git a/resources/qml/dialogs/RoomSettings.qml b/resources/qml/dialogs/RoomSettings.qml
index c9f2b1a1..48d2e2b7 100644
--- a/resources/qml/dialogs/RoomSettings.qml
+++ b/resources/qml/dialogs/RoomSettings.qml
@@ -23,7 +23,6 @@ ApplicationWindow {
color: Nheko.colors.window
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(roomSettingsDialog)
title: qsTr("Room Settings")
Shortcut {
diff --git a/resources/qml/dialogs/UserProfile.qml b/resources/qml/dialogs/UserProfile.qml
index 29ce2c3f..73c4e68b 100644
--- a/resources/qml/dialogs/UserProfile.qml
+++ b/resources/qml/dialogs/UserProfile.qml
@@ -13,9 +13,6 @@ import QtQuick.Window 2.13
import im.nheko 1.0
ApplicationWindow {
- // this does not work in ApplicationWindow, just in Window
- //transientParent: Nheko.mainwindow()
-
id: userProfileDialog
property var profile
@@ -29,7 +26,6 @@ ApplicationWindow {
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- Component.onCompleted: Nheko.reparent(userProfileDialog)
Shortcut {
sequence: StandardKey.Cancel
diff --git a/resources/qml/pages/LoginPage.qml b/resources/qml/pages/LoginPage.qml
new file mode 100644
index 00000000..4d3a52b3
--- /dev/null
+++ b/resources/qml/pages/LoginPage.qml
@@ -0,0 +1,182 @@
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.15
+import im.nheko 1.0
+import "../components/"
+import "../ui/"
+import "../"
+
+Item {
+ id: loginPage
+ property int maxExpansion: 400
+
+ property string error: login.error
+
+ Login {
+ id: login
+ }
+
+ ScrollView {
+ id: scroll
+
+ clip: false
+ palette: Nheko.colors
+ ScrollBar.horizontal.visible: false
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ height: Math.min(loginPage.height, col.implicitHeight)
+ anchors.margins: Nheko.paddingLarge
+
+ contentWidth: availableWidth
+
+ ColumnLayout {
+ id: col
+
+ spacing: Nheko.paddingMedium
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: Math.min(loginPage.maxExpansion, scroll.width- Nheko.paddingLarge*2)
+
+ Image {
+ Layout.alignment: Qt.AlignHCenter
+ source: "qrc:/logos/login.png"
+ height: 128
+ width: 128
+ }
+
+ RowLayout {
+ spacing: Nheko.paddingLarge
+
+ Layout.fillWidth: true
+ MatrixTextField {
+ id: matrixIdLabel
+ label: qsTr("Matrix ID")
+ placeholderText: qsTr("e.g @joe:matrix.org")
+ onEditingFinished: login.mxid = text
+
+ ToolTip.text: qsTr("Your login name. A mxid should start with @ followed by the user id. After the user id you need to include your server name after a :.\nYou can also put your homeserver address there, if your server doesn't support .well-known lookup.\nExample: @user:server.my\nIf Nheko fails to discover your homeserver, it will show you a field to enter the server manually.")
+ Keys.forwardTo: [pwBtn, ssoBtn]
+ }
+
+
+ Spinner {
+ height: matrixIdLabel.height/2
+ Layout.alignment: Qt.AlignBottom
+
+ visible: running
+ running: login.lookingUpHs
+ foreground: Nheko.colors.mid
+ }
+ }
+
+ MatrixText {
+ textFormat: Text.PlainText
+ color: Nheko.theme.error
+ text: login.mxidError
+ visible: text
+ }
+
+ MatrixTextField {
+ id: passwordLabel
+ Layout.fillWidth: true
+ label: qsTr("Password")
+ echoMode: TextInput.Password
+ ToolTip.text: qsTr("Your password.")
+ visible: login.passwordSupported
+ Keys.forwardTo: [pwBtn, ssoBtn]
+ }
+
+ MatrixTextField {
+ id: deviceNameLabel
+ Layout.fillWidth: true
+ label: qsTr("Device name")
+ placeholderText: login.initialDeviceName()
+ ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
+ Keys.forwardTo: [pwBtn, ssoBtn]
+ }
+
+ MatrixTextField {
+ id: hsLabel
+ enabled: visible
+ visible: login.homeserverNeeded
+
+ Layout.fillWidth: true
+ label: qsTr("Homeserver address")
+ placeholderText: qsTr("server.my:8787")
+ text: login.homeserver
+ onEditingFinished: login.homeserver = text
+ ToolTip.text: qsTr("The address that can be used to contact you homeservers client API.\nExample: https://server.my:8787")
+ Keys.forwardTo: [pwBtn, ssoBtn]
+ }
+
+ Item {
+ height: Nheko.avatarSize
+ Layout.fillWidth: true
+
+ Spinner {
+ height: parent.height
+ anchors.centerIn: parent
+
+ visible: running
+ running: login.loggingIn
+ foreground: Nheko.colors.mid
+ }
+ }
+
+ MatrixText {
+ textFormat: Text.PlainText
+ color: Nheko.theme.error
+ text: loginPage.error
+ visible: text
+ }
+
+ FlatButton {
+ id: pwBtn
+ visible: login.passwordSupported
+ enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("LOGIN")
+ function pwLogin() {
+ login.onLoginButtonClicked(Login.Password, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text)
+ }
+ onClicked: pwBtn.pwLogin()
+ Keys.onEnterPressed: pwBtn.pwLogin()
+ Keys.onReturnPressed: pwBtn.pwLogin()
+ Keys.enabled: pwBtn.enabled && login.passwordSupported
+ }
+ FlatButton {
+ id: ssoBtn
+ visible: login.ssoSupported
+ enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("SSO LOGIN")
+ function ssoLogin() {
+ login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text)
+ }
+ onClicked: ssoBtn.ssoLogin()
+ Keys.onEnterPressed: ssoBtn.ssoLogin()
+ Keys.onReturnPressed: ssoBtn.ssoLogin()
+ Keys.enabled: ssoBtn.enabled && !login.passwordSupported
+ }
+
+ }
+ }
+
+ ImageButton {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.margins: Nheko.paddingMedium
+ width: Nheko.avatarSize
+ height: Nheko.avatarSize
+ image: ":/icons/icons/ui/angle-arrow-left.svg"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Back")
+ onClicked: mainWindow.pop()
+ }
+}
diff --git a/resources/qml/pages/RegisterPage.qml b/resources/qml/pages/RegisterPage.qml
new file mode 100644
index 00000000..44836ccb
--- /dev/null
+++ b/resources/qml/pages/RegisterPage.qml
@@ -0,0 +1,215 @@
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.15
+import im.nheko 1.0
+import "../components/"
+import "../ui/"
+import "../"
+
+Item {
+ id: registrationPage
+ property int maxExpansion: 400
+
+ property string error: regis.error
+
+ Registration {
+ id: regis
+ }
+
+ ScrollView {
+ id: scroll
+
+ clip: false
+ palette: Nheko.colors
+ ScrollBar.horizontal.visible: false
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ height: Math.min(registrationPage.height, col.implicitHeight)
+ anchors.margins: Nheko.paddingLarge
+
+ contentWidth: availableWidth
+
+ ColumnLayout {
+ id: col
+
+ spacing: Nheko.paddingMedium
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: Math.min(registrationPage.maxExpansion, scroll.width- Nheko.paddingLarge*2)
+
+ Image {
+ Layout.alignment: Qt.AlignHCenter
+ source: "qrc:/logos/login.png"
+ height: 128
+ width: 128
+ }
+
+ RowLayout {
+ spacing: Nheko.paddingLarge
+
+ Layout.fillWidth: true
+ MatrixTextField {
+ id: hsLabel
+ label: qsTr("Homeserver")
+ placeholderText: qsTr("your.server")
+ onEditingFinished: regis.setServer(text)
+
+ ToolTip.text: qsTr("A server that allows registration. Since matrix is decentralized, you need to first find a server you can register on or host your own.")
+ }
+
+
+ Spinner {
+ height: hsLabel.height/2
+ Layout.alignment: Qt.AlignBottom
+
+ visible: running
+ running: regis.lookingUpHs
+ foreground: Nheko.colors.mid
+ }
+ }
+
+ MatrixText {
+ textFormat: Text.PlainText
+ color: Nheko.theme.error
+ text: regis.hsError
+ visible: text
+ }
+
+ RowLayout {
+ spacing: Nheko.paddingLarge
+
+ visible: regis.supported
+
+ Layout.fillWidth: true
+ MatrixTextField {
+ id: usernameLabel
+ Layout.fillWidth: true
+ label: qsTr("Username")
+ ToolTip.text: qsTr("The username must not be empty, and must contain only the characters a-z, 0-9, ., _, =, -, and /.")
+ onEditingFinished: regis.checkUsername(text)
+ }
+ Spinner {
+ height: usernameLabel.height/2
+ Layout.alignment: Qt.AlignBottom
+
+ visible: running
+ running: regis.lookingUpUsername
+ foreground: Nheko.colors.mid
+ }
+
+ Image {
+ width: usernameLabel.height/2
+ height: width
+ Layout.preferredHeight: usernameLabel.height/2
+ Layout.preferredWidth: usernameLabel.height/2
+ Layout.alignment: Qt.AlignBottom
+ source: regis.usernameAvailable ? ("image://colorimage/:/icons/icons/ui/checkmark.svg?green") : ("image://colorimage/:/icons/icons/ui/dismiss.svg?"+Nheko.theme.error)
+ visible: regis.usernameAvailable || regis.usernameUnavailable
+ ToolTip.visible: ma.hovered
+ ToolTip.text: qsTr("Back")
+ sourceSize.height: height * Screen.devicePixelRatio
+ sourceSize.width: width * Screen.devicePixelRatio
+ HoverHandler {
+ id: ma
+ }
+ }
+ }
+
+ MatrixText {
+ textFormat: Text.PlainText
+ color: Nheko.theme.error
+ text: regis.usernameError
+ visible: text
+ }
+
+
+ MatrixTextField {
+ visible: regis.supported
+ id: passwordLabel
+ Layout.fillWidth: true
+ label: qsTr("Password")
+ echoMode: TextInput.Password
+ ToolTip.text: qsTr("Please choose a secure password. The exact requirements for password strength may depend on your server.")
+ }
+
+ MatrixTextField {
+ visible: regis.supported
+ id: passwordConfirmationLabel
+ Layout.fillWidth: true
+ label: qsTr("Password confirmation")
+ echoMode: TextInput.Password
+ }
+
+ MatrixText {
+ visible: regis.supported
+ textFormat: Text.PlainText
+ color: Nheko.theme.error
+ text: passwordLabel.text != passwordConfirmationLabel.text ? qsTr("Your passwords do not match!") : ""
+ }
+
+ MatrixTextField {
+ visible: regis.supported
+ id: deviceNameLabel
+ Layout.fillWidth: true
+ label: qsTr("Device name")
+ placeholderText: regis.initialDeviceName()
+ ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
+ }
+
+ Item {
+ height: Nheko.avatarSize
+ Layout.fillWidth: true
+
+ Spinner {
+ height: parent.height
+ anchors.centerIn: parent
+
+ visible: running
+ running: regis.registering
+ foreground: Nheko.colors.mid
+ }
+ }
+
+ MatrixText {
+ textFormat: Text.PlainText
+ color: Nheko.theme.error
+ text: registrationPage.error
+ visible: text
+ }
+
+ FlatButton {
+ id: regisBtn
+ visible: regis.supported
+ enabled: usernameLabel.text && passwordLabel.text && passwordLabel.text == passwordConfirmationLabel.text
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("REGISTER")
+ function register() {
+ regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text)
+ }
+ onClicked: regisBtn.register()
+ Keys.onEnterPressed: regisBtn.register()
+ Keys.onReturnPressed: regisBtn.register()
+ Keys.enabled: regisBtn.enabled && regis.supported
+ }
+ }
+ }
+
+ ImageButton {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.margins: Nheko.paddingMedium
+ width: Nheko.avatarSize
+ height: Nheko.avatarSize
+ image: ":/icons/icons/ui/angle-arrow-left.svg"
+ ToolTip.visible: hovered
+ ToolTip.text: qsTr("Back")
+ onClicked: mainWindow.pop()
+ }
+}
+
diff --git a/resources/qml/pages/WelcomePage.qml b/resources/qml/pages/WelcomePage.qml
new file mode 100644
index 00000000..e1ecc31d
--- /dev/null
+++ b/resources/qml/pages/WelcomePage.qml
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.15
+import im.nheko 1.0
+import "../components/"
+
+ColumnLayout {
+ Item {
+ Layout.fillHeight: true
+ }
+
+ Image {
+ Layout.alignment: Qt.AlignHCenter
+ source: "qrc:/logos/splash.png"
+ height: 256
+ width: 256
+ }
+
+ Label {
+ Layout.margins: Nheko.paddingLarge
+ Layout.bottomMargin: 0
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+ text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
+ color: Nheko.colors.text
+ font.pointSize: fontMetrics.font.pointSize*2
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Label {
+ Layout.margins: Nheko.paddingLarge
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+ text: qsTr("Enjoy your stay!")
+ color: Nheko.colors.text
+ font.pointSize: fontMetrics.font.pointSize*1.5
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+ FlatButton {
+ Layout.margins: Nheko.paddingLarge
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("REGISTER")
+ onClicked: {
+ mainWindow.push(registerPage);
+ }
+ }
+ FlatButton {
+ Layout.margins: Nheko.paddingLarge
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("LOGIN")
+ onClicked: {
+ mainWindow.push(loginPage);
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ }
+ Item {
+ Layout.fillHeight: true
+ }
+}
diff --git a/resources/qml/ui/Snackbar.qml b/resources/qml/ui/Snackbar.qml
new file mode 100644
index 00000000..80c0d888
--- /dev/null
+++ b/resources/qml/ui/Snackbar.qml
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import im.nheko 1.0
+
+Popup {
+ id: snackbar
+
+ property var messages: []
+ property string currentMessage: ""
+
+ function showNotification(msg) {
+ messages.push(msg);
+ currentMessage = messages[0];
+ if (!visible) {
+ open();
+ dismissTimer.start();
+ }
+ }
+
+ Timer {
+ id: dismissTimer
+ interval: 10000
+ onTriggered: snackbar.close()
+ }
+
+ onAboutToHide: {
+ messages.shift();
+ }
+ onClosed: {
+ if (messages.length > 0) {
+ currentMessage = messages[0];
+ open();
+ dismissTimer.restart();
+ }
+ }
+
+ parent: Overlay.overlay
+ opacity: 0
+ y: -100
+ x: (parent.width - width)/2
+ padding: Nheko.paddingLarge
+
+ contentItem: Label {
+ color: Nheko.colors.light
+ width: Math.max(Overlay.overlay? Overlay.overlay.width/2 : 0, 400)
+ text: snackbar.currentMessage
+ font.bold: true
+ }
+
+ background: Rectangle {
+ radius: Nheko.paddingLarge
+ color: Nheko.colors.dark
+ opacity: 0.8
+ }
+
+ enter: Transition {
+ NumberAnimation {
+ target: snackbar
+ property: "opacity"
+ from: 0.0
+ to: 1.0
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ target: snackbar
+ properties: "y"
+ from: -100
+ to: 100
+ duration: 1000
+ easing.type: Easing.OutCubic
+ }
+ }
+ exit: Transition {
+ NumberAnimation {
+ target: snackbar
+ property: "opacity"
+ from: 1.0
+ to: 0.0
+ duration: 300
+ easing.type: Easing.InCubic
+ }
+ NumberAnimation {
+ target: snackbar
+ properties: "y"
+ to: -100
+ from: 100
+ duration: 300
+ easing.type: Easing.InCubic
+ }
+ }
+}
+
+
diff --git a/resources/res.qrc b/resources/res.qrc
index a2ee393f..2fba5f4c 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -110,6 +110,9 @@
<file>qml/TypingIndicator.qml</file>
<file>qml/NotificationWarning.qml</file>
<file>qml/pages/UserSettingsPage.qml</file>
+ <file>qml/pages/WelcomePage.qml</file>
+ <file>qml/pages/LoginPage.qml</file>
+ <file>qml/pages/RegisterPage.qml</file>
<file>qml/components/AdaptiveLayout.qml</file>
<file>qml/components/AdaptiveLayoutElement.qml</file>
<file>qml/components/AvatarListTile.qml</file>
@@ -154,6 +157,7 @@
<file>qml/ui/NhekoSlider.qml</file>
<file>qml/ui/Ripple.qml</file>
<file>qml/ui/Spinner.qml</file>
+ <file>qml/ui/Snackbar.qml</file>
<file>qml/ui/animations/BlinkAnimation.qml</file>
<file>qml/ui/media/MediaControls.qml</file>
<file>qml/voip/ActiveCallBar.qml</file>
|