Skip to content

Commit

Permalink
[BUILD-917] Update chatbot upsell (#1055)
Browse files Browse the repository at this point in the history
* Add sleeping chatbot icons

* Remove ankihub_ai_upsell message handling

* Retrieve premium info from API

* Add get_user_details()

* Check if user is trialing

* Remove unused import

* Fix tests
  • Loading branch information
abdnh authored Jan 15, 2025
1 parent 74ede28 commit 6126ae5
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 31 deletions.
11 changes: 9 additions & 2 deletions ankihub/ankihub_client/ankihub_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,12 +1259,15 @@ def media_upload_finished(self, ah_did: uuid.UUID) -> None:
if response.status_code != 204:
raise AnkiHubHTTPError(response)

def owned_deck_ids(self) -> List[uuid.UUID]:
def get_user_details(self) -> Dict[str, Any]:
response = self._send_request("GET", API.ANKIHUB, "/users/me")
if response.status_code != 200:
raise AnkiHubHTTPError(response)

data = response.json()
return response.json()

def owned_deck_ids(self) -> List[uuid.UUID]:
data = self.get_user_details()
result = [uuid.UUID(deck["id"]) for deck in data["created_decks"]]
return result

Expand All @@ -1290,6 +1293,10 @@ def send_daily_card_review_summaries(
if response.status_code != 201:
raise AnkiHubHTTPError(response)

def is_premium_or_trialing(self) -> bool:
data = self.get_user_details()
return data["is_premium"] or data["is_trialing"]


class ThreadLocalSession:
def __init__(self):
Expand Down
20 changes: 1 addition & 19 deletions ankihub/gui/js_message_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
from jinja2 import Template

from ..db import ankihub_db
from ..settings import url_plans_page, url_view_note
from ..settings import url_view_note
from .operations.scheduling import suspend_notes, unsuspend_notes
from .utils import show_dialog

VIEW_NOTE_PYCMD = "ankihub_view_note"
VIEW_NOTE_BUTTON_ID = "ankihub-view-note-button"
Expand All @@ -27,7 +26,6 @@
UNSUSPEND_NOTES_PYCMD = "ankihub_unsuspend_notes"
SUSPEND_NOTES_PYCMD = "ankihub_suspend_notes"
GET_NOTE_SUSPENSION_STATES_PYCMD = "ankihub_get_note_suspension_states"
ANKIHUB_UPSELL = "ankihub_ai_upsell"
COPY_TO_CLIPBOARD_PYCMD = "ankihub_copy_to_clipboard"
OPEN_LINK_PYCMD = "ankihub_open_link"

Expand Down Expand Up @@ -95,22 +93,6 @@ def _on_js_message(handled: Tuple[bool, Any], message: str, context: Any) -> Any
web=reviewer_sidebar.content_webview,
)
return (True, None)
elif message == ANKIHUB_UPSELL:

def on_button_clicked(button_index: int) -> None:
if button_index == 1:
openLink(url_plans_page())

show_dialog(
text="Upgrade your membership to <b>Premium</b> to access this feature 🌟",
title="Your trial has ended!",
buttons=[
("Cancel", aqt.QDialogButtonBox.ButtonRole.RejectRole),
("Upgrade", aqt.QDialogButtonBox.ButtonRole.ActionRole),
],
default_button_idx=1,
callback=on_button_clicked,
)
elif message.startswith(COPY_TO_CLIPBOARD_PYCMD):
kwargs = parse_js_message_kwargs(message)
content = kwargs.get("content")
Expand Down
9 changes: 1 addition & 8 deletions ankihub/gui/reviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@
from ..gui.webview import AuthenticationRequestInterceptor, CustomWebPage # noqa: F401
from ..main.utils import mh_tag_to_resource_title_and_slug
from ..settings import config, url_login, url_mh_integrations_preview
from .js_message_handling import (
ANKIHUB_UPSELL,
VIEW_NOTE_PYCMD,
parse_js_message_kwargs,
)
from .js_message_handling import VIEW_NOTE_PYCMD, parse_js_message_kwargs
from .utils import get_ah_did_of_deck_or_ancestor_deck, using_qt5
from .web.templates import (
get_empty_state_html,
Expand Down Expand Up @@ -619,9 +615,6 @@ def _on_js_message(handled: Tuple[bool, Any], message: str, context: Any) -> Any
openLink(url)

return True, None
elif message == ANKIHUB_UPSELL:
reviewer_sidebar.close_sidebar()
return True, None

return handled

Expand Down
14 changes: 14 additions & 0 deletions ankihub/gui/web/media/_chatbot_icon_sleeping.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions ankihub/gui/web/media/_chatbot_icon_sleeping_dark_theme.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion ankihub/gui/web/reviewer_buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class AnkiHubReviewerButtons {
constructor() {
this.theme = "{{ THEME }}";
this.isPremiumOrTrialing = "{{ IS_PREMIUM_OR_TRIALING }}" == "True";
this.isAnKingDeck = null;
this.bbCount = 0;
this.faCount = 0;
Expand Down Expand Up @@ -35,7 +36,8 @@ class AnkiHubReviewerButtons {
},
{
name: "chatbot",
iconPath: "/_chatbot_icon.svg",
iconPath: this.isPremiumOrTrialing ? "/_chatbot_icon.svg" : "/_chatbot_icon_sleeping.svg",
iconPathDarkTheme: this.isPremiumOrTrialing ? null : "/_chatbot_icon_sleeping_dark_theme.svg",
active: false,
tooltip: "AI Chatbot"
},
Expand Down
4 changes: 4 additions & 0 deletions ankihub/gui/web/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from jinja2 import Environment, FileSystemLoader, select_autoescape

from ...addon_ankihub_client import AddonAnkiHubClient as AnkiHubClient

TEMPLATES_PATH = (pathlib.Path(__file__).parent).absolute()

env = Environment(
Expand All @@ -24,10 +26,12 @@ def get_header_webview_html(


def get_reviewer_buttons_js(theme: str, enabled_buttons: List[str]) -> str:
client = AnkiHubClient()
return env.get_template("reviewer_buttons.js").render(
{
"THEME": theme,
"ENABLED_BUTTONS": ",".join(enabled_buttons),
"IS_PREMIUM_OR_TRIALING": str(client.is_premium_or_trialing()),
}
)

Expand Down
8 changes: 7 additions & 1 deletion tests/addon/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6019,9 +6019,15 @@ def mock_using_qt5_to_return_false(mocker: MockerFixture):
mocker.patch("ankihub.gui.reviewer.using_qt5", return_value=False)


@pytest.fixture
def mock_user_details(mocker: MockerFixture):
user_details = {"is_premium": True, "is_trialing": False}
mocker.patch.object(AnkiHubClient, "get_user_details", return_value=user_details)


# The mock_using_qt5_to_return_false fixture is used to test the AnkiHub AI feature on Qt5,
# even though the feature is disabled on Qt5. (In CI we are only running test on Qt5.)
@pytest.mark.usefixtures("mock_using_qt5_to_return_false")
@pytest.mark.usefixtures("mock_using_qt5_to_return_false", "mock_user_details")
class TestAnkiHubAIInReviewer:
@pytest.mark.sequential
@pytest.mark.parametrize(
Expand Down

0 comments on commit 6126ae5

Please sign in to comment.