# Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from __future__ import annotations import functools from concurrent.futures import Future from typing import Callable import aqt import aqt.main from aqt.addons import ( AddonManager, DownloadLogEntry, install_or_update_addon, show_log_to_user, ) from aqt.qt import ( QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QPushButton, Qt, QVBoxLayout, QWidget, qconnect, ) from aqt.utils import disable_help_button, showWarning, tr def ankihub_login( mw: aqt.main.AnkiQt, on_success: Callable[[], None], username: str = "", password: str = "", ) -> None: def on_future_done(fut: Future[str], username: str, password: str) -> None: try: token = fut.result() except Exception as exc: showWarning(str(exc)) return if not token: showWarning(tr.sync_ankihub_login_failed(), parent=mw) ankihub_login(mw, on_success, username, password) return mw.pm.set_ankihub_token(token) mw.pm.set_ankihub_username(username) install_ankihub_addon(mw, mw.addonManager) on_success() def callback(username: str, password: str) -> None: if not username and not password: return if username and password: mw.taskman.with_progress( lambda: mw.col.ankihub_login(id=username, password=password), functools.partial(on_future_done, username=username, password=password), parent=mw, ) else: ankihub_login(mw, on_success, username, password) get_id_and_pass_from_user(mw, callback, username, password) def ankihub_logout( mw: aqt.main.AnkiQt, on_success: Callable[[], None], token: str, ) -> None: def logout() -> None: mw.pm.set_ankihub_username(None) mw.pm.set_ankihub_token(None) mw.col.ankihub_logout(token=token) mw.taskman.with_progress( logout, # We don't need to wait for the response lambda _: on_success(), parent=mw, ) def get_id_and_pass_from_user( mw: aqt.main.AnkiQt, callback: Callable[[str, str], None], username: str = "", password: str = "", ) -> None: diag = QDialog(mw) diag.setWindowTitle("Anki") disable_help_button(diag) diag.setWindowModality(Qt.WindowModality.WindowModal) diag.setMinimumWidth(600) vbox = QVBoxLayout() info_label = QLabel(f"

{tr.sync_ankihub_dialog_heading()}

") info_label.setOpenExternalLinks(True) info_label.setWordWrap(True) vbox.addWidget(info_label) vbox.addSpacing(20) g = QGridLayout() l1 = QLabel(tr.sync_ankihub_username_label()) g.addWidget(l1, 0, 0) user = QLineEdit() user.setText(username) g.addWidget(user, 0, 1) l2 = QLabel(tr.sync_password_label()) g.addWidget(l2, 1, 0) passwd = QLineEdit() passwd.setText(password) passwd.setEchoMode(QLineEdit.EchoMode.Password) g.addWidget(passwd, 1, 1) vbox.addLayout(g) vbox.addSpacing(20) bb = QDialogButtonBox() # type: ignore sign_in_button = QPushButton(tr.sync_sign_in()) sign_in_button.setAutoDefault(True) bb.addButton( QPushButton(tr.actions_cancel()), QDialogButtonBox.ButtonRole.RejectRole, ) bb.addButton( sign_in_button, QDialogButtonBox.ButtonRole.AcceptRole, ) qconnect(bb.accepted, diag.accept) qconnect(bb.rejected, diag.reject) vbox.addWidget(bb) diag.setLayout(vbox) diag.adjustSize() diag.show() user.setFocus() def on_finished(result: int) -> None: if result == QDialog.DialogCode.Rejected: callback("", "") else: callback(user.text().strip(), passwd.text()) qconnect(diag.finished, on_finished) diag.open() def install_ankihub_addon(parent: QWidget, mgr: AddonManager) -> None: def on_done(log: list[DownloadLogEntry]) -> None: if log: show_log_to_user(parent, log, title=tr.sync_ankihub_addon_installation()) install_or_update_addon(parent, mgr, 1322529746, on_done)