diff --git a/TODO.md b/TODO.md index 5b3e4775..c52bfeff 100644 --- a/TODO.md +++ b/TODO.md @@ -4,8 +4,6 @@ - Handle upload errors: non existent path, path is a dir, file too big, etc - Show real progression for mxc thumbnail loadings, uploads and downloads - - Login & upload: use new exception python/qml mechanism - - Support m.file thumbnails - Generate video thumbnails - GIFs can use the video player @@ -201,6 +199,12 @@ - Support "Empty room (was ...)" after peer left - Previewing room without joining + - thumbnail API shouldn't require auth + - get content repo config API + - add the `resume()` method + + - See if we can turn all the Error classes into actual exceptions + - Distribution - Include python dependencies in binary with rcc? - README.md diff --git a/src/python/backend.py b/src/python/backend.py index 284db60a..2dd007b7 100644 --- a/src/python/backend.py +++ b/src/python/backend.py @@ -8,7 +8,7 @@ import hsluv import nio from .app import App -from .matrix_client import MatrixClient +from .matrix_client import MatrixClient, MatrixError from .models.items import Account, Device, Event, Member, Room, Upload from .models.model_store import ModelStore @@ -51,7 +51,7 @@ class Backend: password: str, device_id: Optional[str] = None, homeserver: str = "https://matrix.org", - ) -> Tuple[bool, str]: + ) -> str: client = MatrixClient( self, user=user, homeserver=homeserver, device_id=device_id, @@ -59,13 +59,13 @@ class Backend: try: await client.login(password) - except RuntimeError as err: # XXX raise + except MatrixError: await client.close() - return (False, err.args[0].message) + raise self.clients[client.user_id] = client self.models[Account][client.user_id] = Account(client.user_id) - return (True, client.user_id) + return client.user_id async def resume_client(self, diff --git a/src/python/matrix_client.py b/src/python/matrix_client.py index 2a45e6d3..639ddcd4 100644 --- a/src/python/matrix_client.py +++ b/src/python/matrix_client.py @@ -41,6 +41,8 @@ class MatrixError(Exception): @classmethod def from_nio(cls, response: nio.ErrorResponse) -> "MatrixError": + # Check for the M_CODE first: some errors for an API share the same + # http code, but have different M_CODEs (e.g. POST /login 403). for subcls in cls.__subclasses__(): if subcls.m_code == response.status_code: return subcls() @@ -58,6 +60,12 @@ class MatrixForbidden(MatrixError): m_code: str = "M_FORBIDDEN" +@dataclass +class MatrixUserDeactivated(MatrixError): + http_code: int = 403 + m_code: str = "M_USER_DEACTIVATED" + + @dataclass class MatrixNotFound(MatrixError): http_code: int = 404 @@ -159,7 +167,7 @@ class MatrixClient(nio.AsyncClient): ) if isinstance(response, nio.LoginError): - raise RuntimeError(response) + raise MatrixError.from_nio(response) asyncio.ensure_future(self.start()) diff --git a/src/qml/Pages/SignIn.qml b/src/qml/Pages/SignIn.qml index c09c0ff3..22044225 100644 --- a/src/qml/Pages/SignIn.qml +++ b/src/qml/Pages/SignIn.qml @@ -34,23 +34,33 @@ HPage { undefined, serverField.text, ] - py.callCoro("login_client", args, ([success, data]) => { - if (! success) { - errorMessage.text = qsTr(data) - button.loading = false - return - } + loginTimeout.restart() + + py.callCoro("login_client", args, userId => { + loginTimeout.stop() + errorMessage.text = "" + button.loading = false py.callCoro( "saved_accounts." + (rememberAccount.checked ? "add": "delete"), [data] ) + pageLoader.showPage( "AccountSettings/AccountSettings", {userId: data} ) + }, type => { + loginTimeout.stop() + let txt = qsTr("Invalid request or login type") - errorMessage.text = "" + if (type === "MatrixForbidden") + txt = qsTr("Invalid username or password") + + if (type === "MatrixUserDeactivated") + txt = qsTr("This account was deactivated") + + errorMessage.text = txt button.loading = false }) }, @@ -58,6 +68,17 @@ HPage { forgot: button => {} }) + Timer { + id: loginTimeout + interval: 30 * 1000 + onTriggered: { + errorMessage.text = qsTr( + "This server seems unavailable. Verify your internet " + + "connection or try again in a few minutes." + ) + } + } + HRowLayout { spacing: signInBox.horizontalSpacing * 1.25 Layout.alignment: Qt.AlignHCenter