diff --git a/src/python/app.py b/src/python/app.py index 6db1afb3..80910fc9 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -54,7 +54,7 @@ class App: return asyncio.run_coroutine_threadsafe(coro, self.loop) - def _call_coro(self, coro: Coroutine, uuid: str) -> None: + def _call_coro(self, coro: Coroutine, uuid: str) -> Future: def on_done(future: Future) -> None: result = exception = trace = None @@ -66,21 +66,23 @@ class App: CoroutineDone(uuid, result, exception, trace) - self.run_in_loop(coro).add_done_callback(on_done) + future = self.run_in_loop(coro) + future.add_done_callback(on_done) + return future def call_backend_coro(self, name: str, uuid: str, args: Sequence[str] = (), - ) -> None: - self._call_coro(attrgetter(name)(self.backend)(*args), uuid) + ) -> Future: + return self._call_coro(attrgetter(name)(self.backend)(*args), uuid) def call_client_coro(self, account_id: str, name: str, uuid: str, - args: Sequence[str] = ()) -> None: + args: Sequence[str] = ()) -> Future: client = self.backend.clients[account_id] - self._call_coro(attrgetter(name)(client)(*args), uuid) + return self._call_coro(attrgetter(name)(client)(*args), uuid) def pdb(self, additional_data: Sequence = ()) -> None: diff --git a/src/qml/Pages/AddAccount/SignIn.qml b/src/qml/Pages/AddAccount/SignIn.qml index c8a0357f..a9ef31d2 100644 --- a/src/qml/Pages/AddAccount/SignIn.qml +++ b/src/qml/Pages/AddAccount/SignIn.qml @@ -31,7 +31,7 @@ HBox { signInTimeout.restart() - py.callCoro("login_client", args, userId => { + loginFuture = py.callCoro("login_client", args, userId => { signInTimeout.stop() errorMessage.text = "" button.loading = false @@ -48,9 +48,14 @@ HBox { ) }, type => { - if (type === "CancelledError") return - signInTimeout.stop() + + if (type === "CancelledError") { + loginFuture = null + button.loading = false + return + } + let txt = qsTr("Invalid request or login type") if (type === "MatrixForbidden") @@ -64,11 +69,14 @@ HBox { }) }, - cancel: button => {} + cancel: button => { if (loginFuture) loginFuture.cancel() } }) + property var loginFuture: null + property string signInWith: "username" + readonly property bool canSignIn: serverField.text && idField.text && passwordField.text && ! serverField.error diff --git a/src/qml/Python.qml b/src/qml/Python.qml index 1433f9ca..9ccbd96e 100644 --- a/src/qml/Python.qml +++ b/src/qml/Python.qml @@ -1,15 +1,40 @@ import QtQuick 2.12 -import QtQuick.Controls 2.12 import io.thp.pyotherside 1.5 import "event_handlers.js" as EventHandlers Python { id: py + property bool ready: false property bool startupAnyAccountsSaved: false property var pendingCoroutines: ({}) + + function newQmlFuture() { + return { + _pyFuture: null, + + get pyFuture() { return this._pyFuture }, + + set pyFuture(value) { + this._pyFuture = value + if (this.cancelPending) this.cancel() + }, + + cancelPending: false, + + cancel: function() { + if (! this.pyFuture) { + this.cancelPending = true + return + } + + py.call(py.getattr(this.pyFuture, "cancel")) + }, + } + } + function setattr(obj, attr, value, callback=null) { py.call(py.getattr(obj, "__setattr__"), [attr, value], callback) } @@ -22,27 +47,43 @@ Python { let uuid = name + "." + CppUtils.uuid() pendingCoroutines[uuid] = {onSuccess, onError} - call("APP.call_backend_coro", [name, uuid, args]) + + let qmlFuture = py.newQmlFuture() + + call("APP.call_backend_coro", [name, uuid, args], pyFuture => { + qmlFuture.pyFuture = pyFuture + }) + + return qmlFuture } function callClientCoro( accountId, name, args=[], onSuccess=null, onError=null ) { + let qmlFuture = py.newQmlFuture() + callCoro("wait_until_client_exists", [accountId], () => { let uuid = accountId + "." + name + "." + CppUtils.uuid() pendingCoroutines[uuid] = {onSuccess, onError} - call("APP.call_client_coro", [accountId, name, uuid, args]) + + let call_args = [accountId, name, uuid, args] + + call("APP.call_client_coro", call_args, pyFuture => { + qmlFuture.pyFuture = pyFuture + }) }) + + return qmlFuture } function saveConfig(backend_attribute, data, callback=null) { if (! py.ready) { return } // config not loaded yet - callCoro(backend_attribute + ".write", [data], callback) + return callCoro(backend_attribute + ".write", [data], callback) } function loadSettings(callback=null) { - callCoro("load_settings", [], ([settings, uiState, theme]) => { + return callCoro("load_settings", [], ([settings, uiState, theme]) => { window.settings = settings window.uiState = uiState window.theme = Qt.createQmlObject(theme, window, "theme")