6b0fe4b381
- Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
110 lines
2.8 KiB
Python
110 lines
2.8 KiB
Python
# Copyright: Ankitects Pty Ltd and contributors
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import List, Optional, Sequence
|
|
|
|
import aqt
|
|
from anki.collection import Config
|
|
from anki.lang import TR
|
|
from aqt import AnkiQt
|
|
from aqt.main import PerformOpOptionalSuccessCallback
|
|
from aqt.qt import *
|
|
from aqt.utils import getText, tooltip, tr
|
|
|
|
|
|
def set_due_date_dialog(
|
|
*,
|
|
mw: aqt.AnkiQt,
|
|
parent: QDialog,
|
|
card_ids: List[int],
|
|
config_key: Optional[Config.String.Key.V],
|
|
) -> None:
|
|
if not card_ids:
|
|
return
|
|
|
|
default_text = (
|
|
mw.col.get_config_string(config_key) if config_key is not None else ""
|
|
)
|
|
prompt = "\n".join(
|
|
[
|
|
tr(TR.SCHEDULING_SET_DUE_DATE_PROMPT, cards=len(card_ids)),
|
|
tr(TR.SCHEDULING_SET_DUE_DATE_PROMPT_HINT),
|
|
]
|
|
)
|
|
(days, success) = getText(
|
|
prompt=prompt,
|
|
parent=parent,
|
|
default=default_text,
|
|
title=tr(TR.ACTIONS_SET_DUE_DATE),
|
|
)
|
|
if not success or not days.strip():
|
|
return
|
|
|
|
mw.perform_op(
|
|
lambda: mw.col.sched.set_due_date(card_ids, days, config_key),
|
|
success=lambda _: tooltip(
|
|
tr(TR.SCHEDULING_SET_DUE_DATE_DONE, cards=len(card_ids)),
|
|
parent=parent,
|
|
),
|
|
)
|
|
|
|
|
|
def forget_cards(*, mw: aqt.AnkiQt, parent: QDialog, card_ids: List[int]) -> None:
|
|
if not card_ids:
|
|
return
|
|
|
|
mw.perform_op(
|
|
lambda: mw.col.sched.schedule_cards_as_new(card_ids),
|
|
success=lambda _: tooltip(
|
|
tr(TR.SCHEDULING_FORGOT_CARDS, cards=len(card_ids)), parent=parent
|
|
),
|
|
)
|
|
|
|
|
|
def suspend_cards(
|
|
*,
|
|
mw: AnkiQt,
|
|
card_ids: Sequence[int],
|
|
success: PerformOpOptionalSuccessCallback = None,
|
|
) -> None:
|
|
mw.perform_op(lambda: mw.col.sched.suspend_cards(card_ids), success=success)
|
|
|
|
|
|
def suspend_note(
|
|
*,
|
|
mw: AnkiQt,
|
|
note_id: int,
|
|
success: PerformOpOptionalSuccessCallback = None,
|
|
) -> None:
|
|
mw.taskman.run_in_background(
|
|
lambda: mw.col.card_ids_of_note(note_id),
|
|
lambda future: suspend_cards(mw=mw, card_ids=future.result(), success=success),
|
|
)
|
|
|
|
|
|
def unsuspend_cards(*, mw: AnkiQt, card_ids: Sequence[int]) -> None:
|
|
mw.perform_op(lambda: mw.col.sched.unsuspend_cards(card_ids))
|
|
|
|
|
|
def bury_cards(
|
|
*,
|
|
mw: AnkiQt,
|
|
card_ids: Sequence[int],
|
|
success: PerformOpOptionalSuccessCallback = None,
|
|
) -> None:
|
|
mw.perform_op(lambda: mw.col.sched.bury_cards(card_ids), success=success)
|
|
|
|
|
|
def bury_note(
|
|
*,
|
|
mw: AnkiQt,
|
|
note_id: int,
|
|
success: PerformOpOptionalSuccessCallback = None,
|
|
) -> None:
|
|
mw.taskman.run_in_background(
|
|
lambda: mw.col.card_ids_of_note(note_id),
|
|
lambda future: bury_cards(mw=mw, card_ids=future.result(), success=success),
|
|
)
|