SignIn: cancel previous task when clicking button
This commit is contained in:
		| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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") | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	