Add a public server list to the initial login page

This commit is contained in:
miruka
2020-08-19 00:17:24 -04:00
parent 1a6273681d
commit 2fa8b2c5f9
13 changed files with 402 additions and 83 deletions

View File

@@ -3,8 +3,9 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
HFlickableColumnPage {
HColumnPage {
implicitWidth: Math.min(parent.width, theme.controls.box.defaultWidth)
padding: theme.spacing
background: Rectangle {
color: theme.controls.box.background

View File

@@ -2,12 +2,13 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../.."
import "../../Base"
import "../../Base/Buttons"
import "../../PythonBridge"
HBox {
id: page
id: box
property string acceptedUserUrl: ""
property string acceptedUrl: ""
@@ -15,22 +16,35 @@ HBox {
property string saveName: "serverBrowser"
property var saveProperties: ["acceptedUserUrl"]
property string loadingIconStep: "server-ping-bad"
property Future connectFuture: null
property Future fetchServersFuture: null
signal accepted()
function takeFocus() { serverField.item.forceActiveFocus() }
function takeFocus() { serverField.item.field.forceActiveFocus() }
function fetchServers() {
fetchServersFuture = py.callCoro("fetch_homeservers", [], () => {
fetchServersFuture = null
}, (type, args, error, traceback) => {
fetchServersFuture = null
// TODO
print( traceback)
})
}
function connect() {
if (connectFuture) connectFuture.cancel()
connectTimeout.restart()
const args = [serverField.item.cleanText]
const args = [serverField.item.field.cleanText]
connectFuture = py.callCoro("server_info", args, ([url, flows]) => {
connectTimeout.stop()
errorMessage.text = ""
connectFuture = null
serverField.errorLabel.text = ""
connectFuture = null
if (! (
flows.includes("m.login.password") ||
@@ -39,7 +53,7 @@ HBox {
flows.includes("m.login.token")
)
)) {
errorMessage.text =
serverField.errorLabel.text =
qsTr("No supported sign-in method for this homeserver.")
return
}
@@ -63,106 +77,137 @@ HBox {
py.showError(type, traceback, uuid)
errorMessage.text = text
serverField.errorLabel.text = text
})
}
function cancel() {
if (page.connectFuture) return
connectTimeout.stop()
connectFuture.cancel()
connectFuture = null
padding: 0
implicitWidth: theme.controls.box.defaultWidth * 1.25
contentHeight: Math.min(
window.height,
Math.max(
serverList.contentHeight,
// busyIndicatorLoader.height + theme.spacing * 2, TODO
)
)
header: HLabel {
text: qsTr(
"Choose a homeserver to create your account on, or the " +
"server on which you made an account to sign in to:"
)
wrapMode: HLabel.Wrap
padding: theme.spacing
}
footer: HLabeledItem {
id: serverField
footer: AutoDirectionLayout {
ApplyButton {
id: applyButton
enabled: serverField.item.cleanText && ! serverField.item.error
text: qsTr("Connect")
icon.name: "server-connect"
loading: page.connectFuture !== null
disableWhileLoading: false
onClicked: page.connect()
}
readonly property bool knownServerChosen:
serverList.model.find(item.cleanText) !== null
CancelButton {
id: cancelButton
enabled: page.connectFuture !== null
onClicked: page.cancel()
label.text: qsTr("Homeserver address:")
label.topPadding: theme.spacing
label.leftPadding: label.topPadding
label.rightPadding: label.topPadding
errorLabel.leftPadding: label.topPadding
errorLabel.rightPadding: label.topPadding
errorLabel.bottomPadding: label.topPadding
Layout.fillWidth: true
Layout.margins: theme.spacing
HRowLayout {
readonly property alias field: field
readonly property alias apply: apply
width: parent.width
HTextField {
id: field
readonly property string cleanText:
text.toLowerCase().trim().replace(/\/+$/, "")
error: text && ! /https?:\/\/.+/.test(cleanText)
defaultText: window.getState(
box, "acceptedUserUrl", "",
)
placeholderText: "https://example.org"
Layout.fillWidth: true
Layout.fillHeight: true
}
HButton {
id: apply
enabled: field.cleanText && ! field.error
icon.name: "apply"
icon.color: theme.colors.positiveBackground
loading: box.connectFuture !== null
disableWhileLoading: false
onClicked: box.connect()
Layout.fillHeight: true
}
}
}
onKeyboardAccept: if (applyButton.enabled) page.connect()
onKeyboardCancel: if (cancelButton.enabled) page.cancel()
onKeyboardAccept: if (serverField.item.apply.enabled) box.connect()
onAccepted: window.saveState(this)
Timer {
id: connectTimeout
interval: 30 * 1000
onTriggered: {
errorMessage.text =
serverField.errorLabel.text =
serverField.knownServerChosen ?
qsTr("This homeserver seems unavailable. Verify your inter" +
"net connection or try again in a few minutes.") :
"net connection or try again later.") :
qsTr("This homeserver seems unavailable. Verify the " +
"entered address, your internet connection or try " +
"again in a few minutes.")
"again later.")
}
}
HLabeledItem {
id: serverField
// 2019-11-11 https://www.hello-matrix.net/public_servers.php
readonly property var knownServers: [
"https://matrix.org",
"https://chat.weho.st",
"https://tchncs.de",
"https://chat.privacytools.io",
"https://hackerspaces.be",
"https://matrix.allmende.io",
"https://feneas.org",
"https://junta.pl",
"https://perthchat.org",
"https://matrix.tedomum.net",
"https://converser.eu",
"https://ru-matrix.org",
"https://matrix.sibnsk.net",
"https://alternanet.fr",
]
readonly property bool knownServerChosen:
knownServers.includes(item.cleanText)
label.text: qsTr("Homeserver:")
Layout.fillWidth: true
HTextField {
readonly property string cleanText:
text.toLowerCase().trim().replace(/\/+$/, "")
width: parent.width
error: ! /https?:\/\/.+/.test(cleanText)
defaultText:
window.getState(page, "acceptedUserUrl", "https://matrix.org")
}
Timer {
interval: 1000
running: fetchServersFuture === null && serverList.count === 0
repeat: true
triggeredOnStart: true
onTriggered: box.fetchServers()
}
HLabel {
id: errorMessage
wrapMode: HLabel.Wrap
horizontalAlignment: Text.AlignHCenter
color: theme.colors.errorText
Timer {
interval: theme.animationDuration * 2
running: true
repeat: true
onTriggered:
box.loadingIconStep = "server-ping-" + (
box.loadingIconStep === "server-ping-bad" ? "medium" :
box.loadingIconStep === "server-ping-medium" ? "good" :
"bad"
)
}
visible: Layout.maximumHeight > 0
Layout.maximumHeight: text ? implicitHeight : 0
Behavior on Layout.maximumHeight { HNumberAnimation {} }
HListView {
id: serverList
clip: true
model: ModelStore.get("homeservers")
delegate: ServerDelegate {
width: serverList.width
loadingIconStep: box.loadingIconStep
onClicked: {
serverField.item.field.text = model.id
serverField.item.apply.clicked()
}
}
Layout.fillWidth: true
Layout.fillHeight: true
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
import "../../Base/HTile"
HTile {
id: root
property string loadingIconStep
contentOpacity: model.status === "Failed" ? 0.3 : 1 // XXX
rightPadding: 0
contentItem: ContentRow {
tile: root
spacing: 0
HIcon {
id: signalIcon
svgName:
model.status === "Failed" ? "server-ping-fail" :
model.status === "Pinging" ? root.loadingIconStep :
model.ping < 400 ? "server-ping-good" :
model.ping < 800 ? "server-ping-medium" :
"server-ping-bad"
colorize:
model.status === "Failed" ? theme.colors.negativeBackground :
model.status === "Pinging" ? theme.colors.accentBackground :
model.ping < 400 ? theme.colors.positiveBackground :
model.ping < 800 ? theme.colors.middleBackground :
theme.colors.negativeBackground
Layout.fillHeight: true
Layout.rightMargin: theme.spacing
Behavior on colorize { HColorAnimation {} }
}
HColumnLayout {
Layout.rightMargin: theme.spacing
TitleLabel {
text: model.name
}
SubtitleLabel {
tile: root
text: model.country
}
}
TitleRightInfoLabel {
tile: root
font.pixelSize: theme.fontSize.normal
text:
model.stability === -1 ?
"" :
qsTr("%1%").arg(Math.max(0, parseInt(model.stability, 10)))
color:
model.stability >= 95 ? theme.colors.positiveText :
model.stability >= 85 ? theme.colors.warningText :
theme.colors.errorText
}
HButton {
icon.name: "server-visit-website"
toolTip.text: qsTr("Visit website")
backgroundColor: "transparent"
onClicked: Qt.openUrlExternally(model.site_url)
Layout.fillHeight: true
}
}
Behavior on contentOpacity { HNumberAnimation {} }
}

View File

@@ -397,8 +397,8 @@ QtObject {
}
function round(floatNumber) {
return parseFloat(floatNumber.toFixed(2))
function round(floatNumber, decimalDigits=2) {
return parseFloat(floatNumber.toFixed(decimalDigits))
}