Don't pass Python Future objects to QML

Returning a Future doesn't work on Windows for some reason
(https://github.com/thp/pyotherside/issues/116).

Instead of using these objects from QML to cancel running coroutines,
call a Python QMLBridge function that takes a coroutine UUID and will
take care of the cancelling.
This commit is contained in:
miruka
2020-09-28 23:06:50 -04:00
parent 53d2ab17af
commit ee1091b4dc
20 changed files with 161 additions and 178 deletions

View File

@@ -7,7 +7,6 @@ import QtQuick.Layouts 1.12
import "../.."
import "../../Base"
import "../../Base/Buttons"
import "../../PythonBridge"
import "../../ShortcutBundles"
HColumnPage {
@@ -18,19 +17,19 @@ HColumnPage {
property bool enableFlickShortcuts:
SwipeView ? SwipeView.isCurrentItem : true
property Future loadFuture: null
property string loadFutureId: ""
function takeFocus() {} // TODO
function loadDevices() {
loadFuture = py.callClientCoro(userId, "devices_info", [], devices => {
loadFutureId = py.callClientCoro(userId, "devices_info",[],devices => {
deviceList.uncheckAll()
deviceList.model.clear()
for (const device of devices)
deviceList.model.append(device)
loadFuture = null
loadFutureId = ""
deviceList.sectionItemCounts = getSectionItemCounts()
if (page.enabled && ! deviceList.currentItem)
@@ -187,7 +186,7 @@ HColumnPage {
height: width
source: "../../Base/HBusyIndicator.qml"
active: page.loadFuture
active: page.loadFutureId
opacity: active ? 1 : 0
Behavior on opacity { HNumberAnimation { factor: 2 } }

View File

@@ -21,26 +21,26 @@ HBox {
property var saveProperties: ["acceptedUserUrl", "knownHttps"]
property string loadingIconStep: "server-ping-bad"
property Future connectFuture: null
property Future fetchServersFuture: null
property string connectFutureId: ""
property string fetchServersFutureId: ""
signal accepted()
function takeFocus() { serverField.item.field.forceActiveFocus() }
function fetchServers() {
if (fetchServersFuture) fetchServersFuture.cancel()
if (fetchServersFutureId) py.cancelCoro(fetchServersFutureId)
fetchServersFuture = py.callCoro("fetch_homeservers", [], () => {
fetchServersFuture = null
fetchServersFutureId = py.callCoro("fetch_homeservers", [], () => {
fetchServersFutureId = ""
}, (type, args, error, traceback) => {
fetchServersFuture = null
fetchServersFutureId = ""
print( traceback) // TODO: display error graphically
})
}
function connect() {
if (connectFuture) connectFuture.cancel()
if (connectFutureId) py.cancelCoro(connectFutureId)
connectTimeout.restart()
const typedUrl = serverField.item.field.cleanText
@@ -49,10 +49,10 @@ HBox {
if (box.knownHttps)
args[0] = args[0].replace(/^(https?:\/\/)?/, "https://")
connectFuture = py.callCoro("server_info", args, ([url, flows]) => {
connectFutureId = py.callCoro("server_info", args, ([url, flows]) => {
connectTimeout.stop()
serverField.errorLabel.text = ""
connectFuture = null
connectFutureId = ""
if (! (
flows.includes("m.login.password") ||
@@ -75,7 +75,7 @@ HBox {
console.error(traceback)
connectTimeout.stop()
connectFuture = null
connectFutureId = ""
let text = qsTr("Unexpected error: %1 [%2]").arg(type).arg(args)
@@ -194,7 +194,7 @@ HBox {
enabled: field.cleanText && ! field.error
icon.name: "server-connect-to-address"
icon.color: theme.colors.positiveBackground
loading: box.connectFuture !== null
loading: box.connectFutureId !== ""
disableWhileLoading: false
onClicked: box.connect()
@@ -209,7 +209,7 @@ HBox {
onAccepted: window.saveState(this)
Component.onDestruction:
if (fetchServersFuture) fetchServersFuture.cancel()
if (fetchServersFutureId) py.cancelCoro(fetchServersFutureId)
Timer {
id: connectTimeout
@@ -230,7 +230,7 @@ HBox {
Timer {
interval: 1000
running:
fetchServersFuture === null &&
fetchServersFutureId === "" &&
ModelStore.get("homeservers").count === 0
repeat: true
@@ -292,7 +292,7 @@ HBox {
height: width
source: "../../Base/HBusyIndicator.qml"
active: box.fetchServersFuture && ! serverList.count
active: box.fetchServersFutureId && ! serverList.count
opacity: active ? 1 : 0
Behavior on opacity { HNumberAnimation { factor: 2 } }

View File

@@ -14,7 +14,7 @@ HFlickableColumnPage {
property string serverUrl
property string displayUrl: serverUrl
property var loginFuture: null
property string loginFutureId: null
readonly property int security:
serverUrl.startsWith("https://") ?
@@ -35,8 +35,8 @@ HFlickableColumnPage {
signal exitRequested()
function finishSignIn(receivedUserId) {
errorMessage.text = ""
page.loginFuture = null
errorMessage.text = ""
page.loginFutureId = ""
py.callCoro(
rememberAccount.checked ?
@@ -53,13 +53,13 @@ HFlickableColumnPage {
}
function cancel() {
if (! page.loginFuture) {
if (! page.loginFutureId) {
page.exitRequested()
return
}
page.loginFuture.cancel()
page.loginFuture = null
py.cancelCoro(page.loginFutureId)
page.loginFutureId = ""
}
@@ -72,7 +72,7 @@ HFlickableColumnPage {
text: qsTr("Sign in")
icon.name: "sign-in"
loading: page.loginFuture !== null
loading: page.loginFutureId !== ""
disableWhileLoading: false
}
@@ -83,7 +83,7 @@ HFlickableColumnPage {
onKeyboardAccept: if (applyButton.enabled) applyButton.clicked()
onKeyboardCancel: page.cancel()
Component.onDestruction: if (loginFuture) loginFuture.cancel()
Component.onDestruction: if (loginFutureId) py.cancelCoro(loginFutureId)
HButton {
icon.name: "sign-in-" + (

View File

@@ -12,17 +12,17 @@ SignInBase {
function takeFocus() { idField.item.forceActiveFocus() }
function signIn() {
if (page.loginFuture) page.loginFuture.cancel()
if (page.loginFutureId) page.loginFutureId = ""
errorMessage.text = ""
page.loginFuture = py.callCoro(
page.loginFutureId = py.callCoro(
"password_auth",
[idField.item.text.trim(), passField.item.text, page.serverUrl],
page.finishSignIn,
(type, args, error, traceback, uuid) => {
page.loginFuture = null
page.loginFutureId = ""
let txt = qsTr(
"Invalid request, login type or unknown error: %1",

View File

@@ -14,23 +14,23 @@ SignInBase {
function startSignIn() {
errorMessage.text = ""
page.loginFuture = py.callCoro("start_sso_auth", [serverUrl], url => {
page.loginFutureId = py.callCoro("start_sso_auth",[serverUrl], url => {
urlArea.text = url
urlArea.cursorPosition = 0
Qt.openUrlExternally(url)
page.loginFuture = py.callCoro("continue_sso_auth", [], userId => {
page.loginFuture = null
page.loginFutureId = py.callCoro("continue_sso_auth",[],userId => {
page.loginFutureId = ""
page.finishSignIn(userId)
})
})
}
function cancel() {
if (loginFuture) {
page.loginFuture.cancel()
page.loginFuture = null
if (loginFutureId) {
py.cancelCoro(page.loginFutureId)
page.loginFutureId = ""
}
page.exitRequested()

View File

@@ -13,7 +13,7 @@ import "Timeline"
HColumnPage {
id: chatPage
property var loadMembersFuture: null
property string loadMembersFutureId: ""
readonly property alias roomHeader: roomHeader
readonly property alias eventList: eventList
@@ -29,16 +29,17 @@ HColumnPage {
padding: 0
column.spacing: 0
Component.onDestruction: if (loadMembersFuture) loadMembersFuture.cancel()
Component.onDestruction:
if (loadMembersFutureId) py.cancelCoro(loadMembersFutureId)
Timer {
interval: 200
running: ! chat.roomInfo.inviter_id && ! chat.roomInfo.left
onTriggered: loadMembersFuture = py.callClientCoro(
onTriggered: loadMembersFutureId = py.callClientCoro(
chat.userId,
"load_all_room_members",
[chat.roomId],
() => { loadMembersFuture = null },
() => { loadMembersFutureId = "" },
)
}

View File

@@ -7,13 +7,12 @@ import "../../../.."
import "../../../../Base"
import "../../../../Base/HTile"
import "../../../../Popups"
import "../../../../PythonBridge"
HTile {
id: member
property bool colorName: hovered
property Future getPresenceFuture: null
property string getPresenceFutureId: ""
backgroundColor: theme.chat.roomPane.listView.member.background
contentOpacity:
@@ -155,11 +154,15 @@ HTile {
Component.onCompleted:
if (model.presence === "offline" && model.last_active_at < new Date(1))
getPresenceFuture = py.callClientCoro(
chat.userId, "get_offline_presence", [model.id],
getPresenceFutureId = py.callClientCoro(
chat.userId,
"get_offline_presence",
[model.id],
() => { getPresenceFutureId = "" }
)
Component.onDestruction: if (getPresenceFuture) getPresenceFuture.cancel()
Component.onDestruction:
if (getPresenceFutureId) py.cancelCoro(getPresenceFutureId)
Behavior on contentOpacity { HNumberAnimation {} }
Behavior on spacing { HNumberAnimation {} }

View File

@@ -6,7 +6,6 @@ import QtQuick.Layouts 1.12
import "../../../.."
import "../../../../Base"
import "../../../../Base/Buttons"
import "../../../../PythonBridge"
HListView {
id: root
@@ -21,8 +20,8 @@ HListView {
property bool powerLevelFieldFocused: false
property Future setPowerFuture: null
property Future getPresenceFuture: null
property string setPowerFutureId: ""
property string getPresenceFutureId: ""
function loadDevices() {
py.callClientCoro(userId, "member_devices", [member.id], devices => {
@@ -246,14 +245,14 @@ HListView {
ApplyButton {
id: applyButton
enabled: ! powerLevel.item.fieldOverMaximum
loading: setPowerFuture !== null
loading: setPowerFutureId !== ""
text: ""
onClicked: {
setPowerFuture = py.callClientCoro(
setPowerFutureId = py.callClientCoro(
userId,
"room_set_member_power",
[roomId, member.id, powerLevel.item.level],
() => { setPowerFuture = null }
() => { setPowerFutureId = "" }
)
}
@@ -264,8 +263,8 @@ HListView {
CancelButton {
text: ""
onClicked: {
setPowerFuture.cancel()
setPowerFuture = null
py.cancelCoro(setPowerFutureId)
setPowerFutureId = ""
powerLevel.item.reset()
}
@@ -289,14 +288,18 @@ HListView {
if (member.presence === "offline" &&
member.last_active_at < new Date(1))
{
getPresenceFuture =
py.callClientCoro(userId, "get_offline_presence", [member.id])
getPresenceFutureId = py.callClientCoro(
userId,
"get_offline_presence",
[member.id],
() => { getPresenceFutureId = "" }
)
}
}
Component.onDestruction: {
if (setPowerFuture) setPowerFuture.cancel()
if (getPresenceFuture) getPresenceFuture.cancel()
if (setPowerFutureId) py.cancelCoro(setPowerFutureId)
if (getPresenceFutureId) py.cancelCoro(getPresenceFutureId)
}
Keys.onEnterPressed: Keys.onReturnPressed(event)

View File

@@ -10,7 +10,7 @@ import "../../../Base/Buttons"
HFlickableColumnPage {
id: settingsView
property var saveFuture: null
property string saveFutureId: ""
readonly property bool anyChange:
nameField.item.changed || topicArea.item.area.changed ||
@@ -20,7 +20,7 @@ HFlickableColumnPage {
readonly property Item keybindFocusItem: nameField.item
function save() {
if (saveFuture) saveFuture.cancel()
if (saveFutureId) py.cancelCoro(saveFutureId)
const args = [
chat.roomId,
@@ -35,17 +35,17 @@ HFlickableColumnPage {
forbidGuestsCheckBox.checked : undefined,
]
function onDone() { saveFuture = null }
function onDone() { saveFutureId = "" }
saveFuture = py.callClientCoro(
saveFutureId = py.callClientCoro(
chat.userId, "room_set", args, onDone, onDone,
)
}
function cancel() {
if (saveFuture) {
saveFuture.cancel()
saveFuture = null
if (saveFutureId) {
py.cancelCoro(saveFutureId)
saveFutureId = ""
}
nameField.item.reset()
@@ -66,14 +66,14 @@ HFlickableColumnPage {
ApplyButton {
id: applyButton
enabled: anyChange
loading: saveFuture !== null
loading: saveFutureId !== ""
disableWhileLoading: false
onClicked: save()
}
CancelButton {
enabled: anyChange || saveFuture !== null
enabled: anyChange || saveFutureId !== ""
onClicked: cancel()
}
}

View File

@@ -6,12 +6,11 @@ import QtQuick.Layouts 1.12
import Clipboard 0.1
import "../../.."
import "../../../Base"
import "../../../PythonBridge"
HColumnLayout {
id: eventDelegate
property var fetchProfilesFuture: null
property string fetchProfilesFutureId: ""
// Remember timeline goes from newest message at index 0 to oldest
readonly property var previousModel: eventList.model.get(model.index + 1)
@@ -72,16 +71,16 @@ HColumnLayout {
onCursorShapeChanged: eventList.cursorShape = cursorShape
Component.onCompleted: if (model.fetch_profile)
fetchProfilesFuture = py.callClientCoro(
fetchProfilesFutureId = py.callClientCoro(
chat.userId,
"get_event_profiles",
[chat.roomId, model.id],
// The if avoids segfault if eventDelegate is already destroyed
() => { if (eventDelegate) fetchProfilesFuture = null }
() => { if (eventDelegate) fetchProfilesFutureId = "" }
)
Component.onDestruction:
if (fetchProfilesFuture) fetchProfilesFuture.cancel()
if (fetchProfilesFutureId) py.cancelCoro(fetchProfilesFutureId)
ListView.onRemove: eventList.uncheck(model.id)

View File

@@ -218,8 +218,8 @@ Rectangle {
HListView {
id: eventList
property Future updateMarkerFuture: null
property Future loadPastEventsFuture: null
property string updateMarkerFutureId: ""
property string loadPastEventsFutureId: ""
property bool moreToLoad: true
property bool ownEventsOnLeft:
@@ -355,13 +355,13 @@ Rectangle {
}
function loadPastEvents() {
loadPastEventsFuture = py.callClientCoro(
loadPastEventsFutureId = py.callClientCoro(
chat.userId,
"load_past_events",
[chat.roomId],
more => {
moreToLoad = more
loadPastEventsFuture = null
moreToLoad = more
loadPastEventsFutureId = ""
}
)
}
@@ -519,7 +519,7 @@ Rectangle {
footer: Item {
width: eventList.width
height: (button.height + theme.spacing * 2) * opacity
opacity: eventList.loadPastEventsFuture ? 1 : 0
opacity: eventList.loadPastEventsFutureId ? 1 : 0
visible: opacity > 0
Behavior on opacity { HNumberAnimation {} }
@@ -554,14 +554,14 @@ Rectangle {
interval: 200
running:
eventList.shouldLoadPastEvents &&
! eventList.loadPastEventsFuture
! eventList.loadPastEventsFutureId
triggeredOnStart: true
onTriggered: eventList.loadPastEvents()
}
Component.onDestruction: {
if (loadPastEventsFuture) loadPastEventsFuture.cancel()
if (loadPastEventsFutureId) py.cancelCoro(loadPastEventsFutureId)
}
MouseArea {
@@ -585,7 +585,7 @@ Rectangle {
interval: Math.max(100, window.settings.markRoomReadMsecDelay)
running:
! eventList.updateMarkerFuture &&
! eventList.updateMarkerFutureId &&
(
chat.roomInfo.unreads ||
chat.roomInfo.highlights ||
@@ -600,11 +600,11 @@ Rectangle {
const item = eventList.model.get(i)
if (item.sender !== chat.userId) {
eventList.updateMarkerFuture = py.callCoro(
eventList.updateMarkerFutureId = py.callCoro(
"update_room_read_marker",
[chat.roomId, item.event_id],
() => { eventList.updateMarkerFuture = null },
() => { eventList.updateMarkerFuture = null },
() => { eventList.updateMarkerFutureId = "" },
() => { eventList.updateMarkerFutureId = "" },
)
return
}