From 746a9259db1f312e4cb34b7ee3e09ee186c9bc7c Mon Sep 17 00:00:00 2001 From: miruka Date: Mon, 11 Nov 2019 09:12:31 -0400 Subject: [PATCH] SignIn: cancel previous task when clicking button --- src/python/app.py | 2 -- src/python/backend.py | 2 ++ src/python/utils.py | 33 ++++++++++++++++++++++++++++++++- src/qml/Base/HBox.qml | 10 +++++++--- src/qml/Base/HButton.qml | 10 +++++++++- src/qml/Pages/SignIn.qml | 5 ++++- 6 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/python/app.py b/src/python/app.py index b6a5d109..6db1afb3 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -11,8 +11,6 @@ from typing import Coroutine, Sequence import nio from appdirs import AppDirs -import pyotherside - from . import __about__ from .pyotherside_events import CoroutineDone diff --git a/src/python/backend.py b/src/python/backend.py index 2dd007b7..06917cff 100644 --- a/src/python/backend.py +++ b/src/python/backend.py @@ -7,6 +7,7 @@ import hsluv import nio +from . import utils from .app import App from .matrix_client import MatrixClient, MatrixError from .models.items import Account, Device, Event, Member, Room, Upload @@ -46,6 +47,7 @@ class Backend: # Clients management + @utils.cancel_previous async def login_client(self, user: str, password: str, diff --git a/src/python/utils.py b/src/python/utils.py index 8c7041d8..0a074c87 100644 --- a/src/python/utils.py +++ b/src/python/utils.py @@ -1,17 +1,21 @@ +import asyncio import collections import html import inspect +import logging as log import xml.etree.cElementTree as xml_etree # FIXME: bandit warning from enum import Enum from enum import auto as autostr from pathlib import Path from types import ModuleType -from typing import IO, Any, Dict, Tuple, Type, Union +from typing import IO, Any, Callable, Dict, Tuple, Type, Union import filetype auto = autostr +CANCELLABLE_FUTURES: Dict[Tuple[Any, Callable], asyncio.Future] = {} + class AutoStrEnum(Enum): @staticmethod @@ -83,3 +87,30 @@ def classes_defined_in(module: ModuleType) -> Dict[str, Type]: if not m[0].startswith("_") and m[1].__module__.startswith(module.__name__) } + + +def cancel_previous(async_func): + async def wrapper(*args, **kwargs): + try: + arg0_is_self = inspect.getfullargspec(async_func).args[0] == "self" + except IndexError: + parent_obj = None + else: + parent_obj = args[0] if arg0_is_self else None + + previous = CANCELLABLE_FUTURES.get((parent_obj, async_func)) + if previous: + previous.cancel() + log.info("Cancelled previous coro: %s", previous) + + future = asyncio.ensure_future(async_func(*args, **kwargs)) + CANCELLABLE_FUTURES[parent_obj, async_func] = future + + try: + result = await future + return result + finally: + # Make sure to do this even if an exception happens + del CANCELLABLE_FUTURES[parent_obj, async_func] + + return wrapper diff --git a/src/qml/Base/HBox.qml b/src/qml/Base/HBox.qml index 3080a34c..5f4475f5 100644 --- a/src/qml/Base/HBox.qml +++ b/src/qml/Base/HBox.qml @@ -98,9 +98,13 @@ Rectangle { theme.icons.colorize ) - enabled: (modelData.enabled == undefined ? - true : modelData.enabled) && - ! button.loading + enabled: + modelData.enabled === undefined ? + true : modelData.enabled + + disableWhileLoading: + modelData.disableWhileLoading === undefined ? + true : modelData.disableWhileLoading onClicked: buttonCallbacks[name](button) diff --git a/src/qml/Base/HButton.qml b/src/qml/Base/HButton.qml index 8f1185a2..1350825c 100644 --- a/src/qml/Base/HButton.qml +++ b/src/qml/Base/HButton.qml @@ -12,7 +12,6 @@ Button { iconItem.svgName: loading ? "hourglass" : icon.name icon.color: theme.icons.colorize - enabled: ! loading // Must be explicitely set to display correctly on KDE implicitWidth: Math.max( @@ -32,6 +31,7 @@ Button { readonly property alias label: contentItem.label property color backgroundColor: theme.controls.button.background + property bool disableWhileLoading: true property bool loading: false property bool circle: false @@ -41,6 +41,14 @@ Button { } + Binding { + when: disableWhileLoading && loading + target: button + property: "enabled" + value: false + } + + background: HButtonBackground { button: button buttonTheme: theme.controls.button diff --git a/src/qml/Pages/SignIn.qml b/src/qml/Pages/SignIn.qml index a9ecd02a..3fb72209 100644 --- a/src/qml/Pages/SignIn.qml +++ b/src/qml/Pages/SignIn.qml @@ -19,7 +19,8 @@ HPage { buttonModel: [ { name: "register", text: qsTr("Register"), enabled: false }, - { name: "login", text: qsTr("Login"), enabled: canLogin }, + { name: "login", text: qsTr("Login"), enabled: canLogin, + disableWhileLoading: false }, { name: "forgot", text: qsTr("Forgot?"), enabled: false }, ] @@ -52,6 +53,8 @@ HPage { "AccountSettings/AccountSettings", {userId: data} ) }, type => { + if (type === "CancelledError") return + loginTimeout.stop() let txt = qsTr("Invalid request or login type")