From f6212a26003ff1ced81842a49b2b215cdc4b53f5 Mon Sep 17 00:00:00 2001 From: AJ Slater Date: Sun, 18 Oct 2026 21:27:23 -0700 Subject: [PATCH] Remove the four pinned settings from the schema #910 pinned dynamic covers, custom covers and finish-on-last-page on and read-RTL-in-reverse off, and removed their checkboxes, but kept the columns, serializer fields, defaults, telemetry buckets and sidecar export so the toggles could come back with a revert. Nobody needs them back, so they go: - Migration 0057 drops SettingsBrowser.custom_covers/dynamic_covers and SettingsReader.finish_on_last_page/read_rtl_in_reverse. They shipped in 0038, so this is a real RemoveField migration, not a fold edit. - BROWSER_DEFAULTS/READER_DEFAULTS, the browser, reader and session serializers, the telemetry and admin stats buckets, and the user-data sidecar schema/dump/restore lose the keys. The sidecar SCHEMA_VERSION is unchanged: the store only adds columns and restore reads named columns, so a pre-removal sidecar still restores (the legacy restore fixture keeps the two keys as the regression guard). - Dead code the pin left behind goes too: the sort-name fuzzy cover match and parent_route, the custom-cover early return, the unused BrowserCoverInputSerializer, the coverSettings getter and its key lists, setReadRTLInReverse, and the parked browser-settings-covers component. BrowserCoverInputSerializerBase folds into BrowserSettingsSerializerBase, and get_reader_defaults now iterates READER_DEFAULTS. Co-Authored-By: Claude Opus 5.5 --- NEWS.md | 2 + codex/choices/browser.py | 2 - codex/choices/reader.py | 2 - codex/librarian/telemeter/per_user_stats.py | 4 - codex/librarian/telemeter/stats.py | 4 +- .../migrations/0057_remove_pinned_settings.py | 39 ++++++++++ codex/models/settings.py | 27 ------- codex/serializers/admin/stats.py | 11 --- codex/serializers/auth.py | 2 - codex/serializers/browser/settings.py | 22 +----- codex/serializers/reader.py | 2 - codex/settings/db.py | 19 +---- codex/user_data/restore.py | 2 - codex/user_data/schema.sql | 2 - codex/user_data/serializers.py | 2 - codex/views/browser/annotate/cover.py | 68 ++++------------ .../src/components/admin/tabs/stats-tab.vue | 2 - .../drawer/browser-settings-covers.vue | 78 ------------------- .../reader/drawer/reader-settings-scope.vue | 8 -- frontend/src/stores/browser.js | 28 ------- frontend/src/stores/reader.js | 31 +------- frontend/tests/unit/stats-tab.test.js | 4 +- tests/test_settings_defaults.py | 8 +- tests/test_telemeter_privacy.py | 11 --- tests/test_user_data_restore.py | 1 + 25 files changed, 64 insertions(+), 317 deletions(-) create mode 100644 codex/migrations/0057_remove_pinned_settings.py delete mode 100644 frontend/src/components/browser/drawer/browser-settings-covers.vue diff --git a/NEWS.md b/NEWS.md index 69aeed200..596b0047d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,8 @@ border-radius: 128px; - Renamed comics no longer contain colons or other characters Windows and SMB shares can't store. - A Metron API key is no longer overridden by a `~/.netrc` login. + - Dynamic covers, custom covers and finishing a book on its last page are + always on; those settings and "Read RTL Comics LTR" are gone. ## v2.5.4 diff --git a/codex/choices/browser.py b/codex/choices/browser.py index f4b42aa98..6c6aab844 100644 --- a/codex/choices/browser.py +++ b/codex/choices/browser.py @@ -776,8 +776,6 @@ def clean_table_columns(value) -> tuple[dict[str, list[str]], list[str]]: BROWSER_DEFAULTS = MappingProxyType( { - "custom_covers": True, - "dynamic_covers": True, "filters": _DEFAULT_FILTERS, "order_by": "sort_name", "order_reverse": False, diff --git a/codex/choices/reader.py b/codex/choices/reader.py index fcf587c00..d6419a470 100644 --- a/codex/choices/reader.py +++ b/codex/choices/reader.py @@ -24,10 +24,8 @@ ) READER_DEFAULTS = MappingProxyType( { - "finish_on_last_page": True, "fit_to": "W", "reading_direction": "ltr", - "read_rtl_in_reverse": False, "two_pages": False, "page_transition": True, "cache_book": False, diff --git a/codex/librarian/telemeter/per_user_stats.py b/codex/librarian/telemeter/per_user_stats.py index caddaa1f0..bcbd5c3f0 100644 --- a/codex/librarian/telemeter/per_user_stats.py +++ b/codex/librarian/telemeter/per_user_stats.py @@ -53,8 +53,6 @@ "order_by", "view_mode", "table_cover_size", - "dynamic_covers", - "custom_covers", ) # Ordered, unlike SettingsReader.DIRECT_KEYS which is a frozenset. A test pins # the two to the same set so this cannot drift from the model. @@ -62,8 +60,6 @@ "fit_to", "two_pages", "reading_direction", - "read_rtl_in_reverse", - "finish_on_last_page", "page_transition", "cache_book", ) diff --git a/codex/librarian/telemeter/stats.py b/codex/librarian/telemeter/stats.py index 17084b978..52ad1e3dd 100644 --- a/codex/librarian/telemeter/stats.py +++ b/codex/librarian/telemeter/stats.py @@ -62,13 +62,11 @@ ( "top_collection", "order_by", - "dynamic_covers", "view_mode", "table_cover_size", - "custom_covers", ), ), - (SettingsReader, ("finish_on_last_page", "fit_to", "reading_direction")), + (SettingsReader, ("fit_to", "reading_direction")), ) # Sections built by a single collector function, in payload order. _SIMPLE_SECTIONS: Final = ( diff --git a/codex/migrations/0057_remove_pinned_settings.py b/codex/migrations/0057_remove_pinned_settings.py new file mode 100644 index 000000000..8f33e4a75 --- /dev/null +++ b/codex/migrations/0057_remove_pinned_settings.py @@ -0,0 +1,39 @@ +"""Generated by Django 6.1.2 on 2026-10-19 04:16.""" + +from django.db import migrations + + +class Migration(migrations.Migration): + """ + Remove four settings that stopped being user options. + + v2.5.x pinned dynamic covers and custom covers on, finishing a book on its + last page on, and reading RTL comics in reverse off, and dropped their + checkboxes, but kept the columns so the toggles could come back with a + revert. They are gone for good now. All four shipped in 0038, so they + come out here rather than in a fold. Reversible: the browser columns have + defaults and the reader columns are nullable. + """ + + dependencies = [ + ("codex", "0056_match_mode_ask"), + ] + + operations = [ + migrations.RemoveField( + model_name="settingsbrowser", + name="custom_covers", + ), + migrations.RemoveField( + model_name="settingsbrowser", + name="dynamic_covers", + ), + migrations.RemoveField( + model_name="settingsreader", + name="finish_on_last_page", + ), + migrations.RemoveField( + model_name="settingsreader", + name="read_rtl_in_reverse", + ), + ] diff --git a/codex/models/settings.py b/codex/models/settings.py index 64668c840..7b776414b 100644 --- a/codex/models/settings.py +++ b/codex/models/settings.py @@ -362,18 +362,6 @@ class SettingsBrowser(SettingsBase): search = CharField(max_length=4095, default="", blank=True) # Display preferences - # - # PENDING SCHEMA REMOVAL - # - # The next two are no longer user options: their checkboxes are gone - # from the browser settings drawer and the behavior is pinned on in - # ``views/browser/annotate/cover.py``. The columns stay so nobody's - # stored preference is destroyed while we find out whether anyone misses - # the toggles -- restoring them is a revert, not a data migration. Drop - # these two, their serializer fields and their ``BROWSER_DEFAULTS`` - # entries together. - custom_covers = BooleanField(default=True) - dynamic_covers = BooleanField(default=True) twenty_four_hour_time = BooleanField(default=False) always_show_filename = BooleanField(default=False) @@ -408,8 +396,6 @@ class SettingsBrowser(SettingsBase): "order_extra_keys", "collection_order_memory", "search", - "custom_covers", - "dynamic_covers", "twenty_four_hour_time", "always_show_filename", "view_mode", @@ -488,17 +474,6 @@ class SettingsReader(SettingsBase): default="", max_length=max_choices_len(ReadingDirectionChoices), ) - # PENDING SCHEMA REMOVAL - # - # The next two are no longer user options: their checkboxes are gone from - # the reader settings drawer and the behavior is pinned in - # ``stores/reader.js`` -- finish-on-last-page on, read-RTL-in-reverse off - # (its default, so a right-to-left book is read right to left). The - # columns stay so nobody's stored preference is destroyed; restoring the - # toggles is a revert, not a data migration. Drop these two, their - # serializer fields and their ``READER_DEFAULTS`` entries together. - read_rtl_in_reverse = BooleanField(default=None, null=True) - finish_on_last_page = BooleanField(default=None, null=True) page_transition = BooleanField(default=None, null=True) cache_book = BooleanField(default=None, null=True) @@ -508,8 +483,6 @@ class SettingsReader(SettingsBase): "fit_to", "two_pages", "reading_direction", - "read_rtl_in_reverse", - "finish_on_last_page", "page_transition", "cache_book", } diff --git a/codex/serializers/admin/stats.py b/codex/serializers/admin/stats.py index a3bf48a61..5d0cec547 100644 --- a/codex/serializers/admin/stats.py +++ b/codex/serializers/admin/stats.py @@ -80,13 +80,10 @@ class StatsSessionsSerializer(Serializer): top_collection = CountDictField(required=False, read_only=True) order_by = CountDictField(required=False, read_only=True) - dynamic_covers = CountDictField(required=False, read_only=True) - finish_on_last_page = CountDictField(required=False, read_only=True) fit_to = CountDictField(required=False, read_only=True) reading_direction = CountDictField(required=False, read_only=True) view_mode = CountDictField(required=False, read_only=True) table_cover_size = CountDictField(required=False, read_only=True) - custom_covers = CountDictField(required=False, read_only=True) multi_sort_count = IntegerField(required=False, read_only=True) @@ -268,26 +265,18 @@ class StatsPerUserSerializer(Serializer): browser_order_by_users = CountDictField(required=False) browser_view_mode_users = CountDictField(required=False) browser_table_cover_size_users = CountDictField(required=False) - browser_dynamic_covers_users = CountDictField(required=False) - browser_custom_covers_users = CountDictField(required=False) browser_chosen_top_collection_users = CountDictField(required=False) browser_chosen_order_by_users = CountDictField(required=False) browser_chosen_view_mode_users = CountDictField(required=False) browser_chosen_table_cover_size_users = CountDictField(required=False) - browser_chosen_dynamic_covers_users = CountDictField(required=False) - browser_chosen_custom_covers_users = CountDictField(required=False) reader_global_fit_to_users = CountDictField(required=False) reader_global_two_pages_users = CountDictField(required=False) reader_global_reading_direction_users = CountDictField(required=False) - reader_global_read_rtl_in_reverse_users = CountDictField(required=False) - reader_global_finish_on_last_page_users = CountDictField(required=False) reader_global_page_transition_users = CountDictField(required=False) reader_global_cache_book_users = CountDictField(required=False) reader_chosen_fit_to_users = CountDictField(required=False) reader_chosen_two_pages_users = CountDictField(required=False) reader_chosen_reading_direction_users = CountDictField(required=False) - reader_chosen_read_rtl_in_reverse_users = CountDictField(required=False) - reader_chosen_finish_on_last_page_users = CountDictField(required=False) reader_chosen_page_transition_users = CountDictField(required=False) reader_chosen_cache_book_users = CountDictField(required=False) diff --git a/codex/serializers/auth.py b/codex/serializers/auth.py index 730c338e5..a003f2d82 100644 --- a/codex/serializers/auth.py +++ b/codex/serializers/auth.py @@ -128,8 +128,6 @@ class SessionReaderDefaultsSerializer(Serializer): two_pages = BooleanField(read_only=True) page_transition = BooleanField(read_only=True) cache_book = BooleanField(read_only=True) - finish_on_last_page = BooleanField(read_only=True) - read_rtl_in_reverse = BooleanField(read_only=True) class SessionDefaultsSerializer(Serializer): diff --git a/codex/serializers/browser/settings.py b/codex/serializers/browser/settings.py index d72daf850..bd585f2d6 100644 --- a/codex/serializers/browser/settings.py +++ b/codex/serializers/browser/settings.py @@ -30,7 +30,6 @@ BrowserRouteCollectionField, ) from codex.serializers.mixins import JSONFieldSerializer -from codex.serializers.route import SimpleRouteSerializer from codex.serializers.settings import SettingsInputSerializer # Sorts worth remembering per top collection. ``search_score`` only means @@ -113,33 +112,16 @@ class BrowserFilterChoicesInputSerializer(JSONFieldSerializer): search = CharField(allow_blank=True, required=False) -class BrowserCoverInputSerializerBase(BrowserFilterChoicesInputSerializer): - """Base Serializer for Cover and Settings.""" +class BrowserSettingsSerializerBase(BrowserFilterChoicesInputSerializer): + """Base Serializer for Browser & OPDS Settings.""" JSON_FIELDS = frozenset(BrowserFilterChoicesInputSerializer.JSON_FIELDS | {"show"}) - custom_covers = BooleanField(required=False) - dynamic_covers = BooleanField(required=False) order_by = ChoiceField( choices=tuple(BROWSER_ORDER_BY_CHOICES.keys()), required=False ) order_reverse = BooleanField(required=False) show = BrowserSettingsShowCollectionFlagsSerializer(required=False) - - -class BrowserCoverInputSerializer(BrowserCoverInputSerializerBase): - """Browser Settings for the cover response.""" - - JSON_FIELDS = frozenset( - BrowserCoverInputSerializerBase.JSON_FIELDS | {"parent_route"} - ) - - parent_route = SimpleRouteSerializer(required=False) - - -class BrowserSettingsSerializerBase(BrowserCoverInputSerializerBase): - """Base Serializer for Browser & OPDS Settings.""" - top_collection = BrowseCollectionField(required=False) @override diff --git a/codex/serializers/reader.py b/codex/serializers/reader.py index 8e9df826d..b2487a4a7 100644 --- a/codex/serializers/reader.py +++ b/codex/serializers/reader.py @@ -30,8 +30,6 @@ class ReaderSettingsSerializer(Serializer): reading_direction = ReadingDirectionField( allow_blank=True, allow_null=True, required=False ) - read_rtl_in_reverse = BooleanField(allow_null=True, required=False) - finish_on_last_page = BooleanField(allow_null=True, required=False) mtime = TimestampField(read_only=True) page_transition = BooleanField(allow_null=True, required=False) cache_book = BooleanField(allow_null=True, required=False) diff --git a/codex/settings/db.py b/codex/settings/db.py index 61ddeeb53..b9adb06e9 100644 --- a/codex/settings/db.py +++ b/codex/settings/db.py @@ -241,13 +241,6 @@ def _in_choices_or_factory(row: SettingsDefaults, key: str) -> Any: "bookmark", "table_columns", ) -_READER_DEFAULTS_KEYS: tuple[str, ...] = ( - "fit_to", - "reading_direction", - "two_pages", - "page_transition", - "cache_book", -) def get_browser_defaults() -> dict[str, Any]: @@ -270,17 +263,9 @@ def get_browser_defaults() -> dict[str, Any]: def get_reader_defaults() -> dict[str, Any]: - """ - Resolve the site default global reader settings. - - Keeps the two pinned ``READER_DEFAULTS`` keys seeded alongside the - admin-configurable ones. - """ + """Resolve the site default global reader settings.""" row = _get_settings_defaults_or_factory() - return { - **READER_DEFAULTS, - **{key: _in_choices_or_factory(row, key) for key in _READER_DEFAULTS_KEYS}, - } + return {key: _in_choices_or_factory(row, key) for key in READER_DEFAULTS} def get_throttle_settings() -> ThrottleSettings | None: diff --git a/codex/user_data/restore.py b/codex/user_data/restore.py index afb3caec6..70f39870c 100644 --- a/codex/user_data/restore.py +++ b/codex/user_data/restore.py @@ -775,8 +775,6 @@ def _rename_table_columns(raw) -> dict: ) _BROWSER_BOOL_COLUMNS: Final[tuple[str, ...]] = ( "order_reverse", - "custom_covers", - "dynamic_covers", "twenty_four_hour_time", "always_show_filename", ) diff --git a/codex/user_data/schema.sql b/codex/user_data/schema.sql index 7048aee35..52e00a1b7 100644 --- a/codex/user_data/schema.sql +++ b/codex/user_data/schema.sql @@ -88,8 +88,6 @@ CREATE TABLE IF NOT EXISTS settings_browser ( order_extra_keys TEXT NOT NULL DEFAULT '[]', collection_order_memory TEXT NOT NULL DEFAULT '{}', search TEXT NOT NULL DEFAULT '', - custom_covers INTEGER NOT NULL DEFAULT 1, - dynamic_covers INTEGER NOT NULL DEFAULT 1, twenty_four_hour_time INTEGER NOT NULL DEFAULT 0, always_show_filename INTEGER NOT NULL DEFAULT 0, view_mode TEXT NOT NULL DEFAULT '', diff --git a/codex/user_data/serializers.py b/codex/user_data/serializers.py index 8256f8411..6815f78dd 100644 --- a/codex/user_data/serializers.py +++ b/codex/user_data/serializers.py @@ -207,8 +207,6 @@ def serialize_settings_browser( browser.collection_order_memory, separators=(",", ":") ), "search": browser.search, - "custom_covers": int(browser.custom_covers), - "dynamic_covers": int(browser.dynamic_covers), "twenty_four_hour_time": int(browser.twenty_four_hour_time), "always_show_filename": int(browser.always_show_filename), "view_mode": browser.view_mode, diff --git a/codex/views/browser/annotate/cover.py b/codex/views/browser/annotate/cover.py index 653a71d1d..e16e5e3c6 100644 --- a/codex/views/browser/annotate/cover.py +++ b/codex/views/browser/annotate/cover.py @@ -1,33 +1,12 @@ """Cover pk annotation for browser card querysets.""" -from typing import Final - from django.db.models import OuterRef, Q, Subquery -from codex.models import Comic, Folder, Volume +from codex.models import Comic, Folder from codex.models.paths import CustomCover -from codex.views.browser.annotate.card import _COLLECTION_BY, BrowserAnnotateCardView +from codex.views.browser.annotate.card import BrowserAnnotateCardView from codex.views.const import COLLECTION_RELATION, CUSTOM_COVER_COLLECTION_RELATION -# PENDING SCHEMA REMOVAL -# -# Dynamic covers and custom covers are no longer user options. -# They are pinned on here instead of read from ``self.params``, and their -# toggles are gone from the browser settings drawer. -# -# What is deliberately still here: ``SettingsBrowser.dynamic_covers`` and -# ``.custom_covers``, their serializer fields, and the query params. Every -# user's stored preference is therefore untouched, so putting the toggles back -# means reverting the code — no data migration, and nobody's setting is lost in -# the meantime. -# -# When we commit to this, drop the two columns, their serializer and -# ``BROWSER_DEFAULTS`` entries, the params the client sends, and the branches -# below that can no longer be reached: the sort-name fuzzy match in -# ``_cover_collection_q`` and the early return in ``_cover_custom_subqueries``. -_DYNAMIC_COVERS: Final[bool] = True -_CUSTOM_COVERS: Final[bool] = True - class BrowserAnnotateCoverView(BrowserAnnotateCardView): """ @@ -35,40 +14,23 @@ class BrowserAnnotateCoverView(BrowserAnnotateCardView): Pre-computes the representative comic pk per card via a correlated subquery, replacing the 72-request fan-out to ``CoverView`` with a - single aggregation pass. Reproduces ``CoverView.get_collection_filter`` - semantics exactly: direct collection-fk match when ``dynamic_covers`` is - on or the model is Volume/Folder; sort-name fuzzy match (correlated - on the GROUP BY columns) otherwise. Dynamic covers are now pinned on, so - the fuzzy branch is unreachable — see the note above. + single aggregation pass. The cover comic comes from a direct + collection-fk match; Folder cards match through the ``folders`` M2M so + descendant subfolders count. A custom cover, when one exists, overrides it. """ def _cover_collection_q(self, collection_model) -> Q: """Build the collection filter Q for a cover subquery (correlated via OuterRef).""" + # Folders are hierarchical: parent_folder is the direct FK, but the + # browse filter uses the ``folders`` M2M that includes every + # ancestor folder. A Folder card's cover must be pickable from any + # comic in a descendant subfolder, not just direct children — + # otherwise parent folders that contain only subfolders fall back + # to the card's own pk and serve the missing-cover placeholder. + if collection_model is Folder: + return Q(folders__pk=OuterRef("pk")) collection_rel = COLLECTION_RELATION[self.model_collection] - if _DYNAMIC_COVERS or collection_model in (Volume, Folder): - # Folders are hierarchical: parent_folder is the direct FK, but the - # browse filter uses the ``folders`` M2M that includes every - # ancestor folder. A Folder card's cover must be pickable from any - # comic in a descendant subfolder, not just direct children — - # otherwise parent folders that contain only subfolders fall back - # to the card's own pk and serve the missing-cover placeholder. - if collection_model is Folder: - return Q(folders__pk=OuterRef("pk")) - return Q(**{f"{collection_rel}__pk": OuterRef("pk")}) - - # Fuzzy match: correlate on the GROUP BY columns. These are the - # same columns used by add_group_by, so ``ids`` (the JsonGroupArray - # of merged collection pks) and the cover subquery pick the exact same - # comic set — without recomputing sort_names in Python. - collection_by_cols = _COLLECTION_BY.get(collection_model, ("sort_name",)) - correlation: dict = { - f"{collection_rel}__{col}": OuterRef(col) for col in collection_by_cols - } - parent_route = self.params.get("parent_route", {}) - if parent_pks := parent_route.get("pks"): - parent_rel = COLLECTION_RELATION[parent_route["collection"]] - correlation[f"{parent_rel}__pk__in"] = parent_pks - return Q(**correlation) + return Q(**{f"{collection_rel}__pk": OuterRef("pk")}) def _cover_filter_q(self, collection_model) -> Q: """Build the full filter Q for the cover comic subquery.""" @@ -121,8 +83,6 @@ def _cover_comic_ordered_qs(self, collection_model): def _cover_custom_subqueries(self): """Return (pk, updated_at) subqueries for a CustomCover, or (None, None).""" - if not _CUSTOM_COVERS: - return None, None # ``model_collection`` is the collection the cards being annotated belong to # (the *child* of the URL collection). ``kwargs["collection"]`` is the URL # collection itself, which is one level too high for the cover lookup — diff --git a/frontend/src/components/admin/tabs/stats-tab.vue b/frontend/src/components/admin/tabs/stats-tab.vue index 665b468f1..90473a2a1 100644 --- a/frontend/src/components/admin/tabs/stats-tab.vue +++ b/frontend/src/components/admin/tabs/stats-tab.vue @@ -35,9 +35,7 @@ const UNSET_LABELS = Object.freeze({ fitTo: "Unset (Fit to Width)", readingDirection: "Unset (Left to Right)", orderBy: "Unset (Name)", - finishOnLastPage: "Unset (On)", twoPages: "Unset (Off)", - readRtlInReverse: "Unset (Off)", pageTransition: "Unset (On)", cacheBook: "Unset (Off)", }); diff --git a/frontend/src/components/browser/drawer/browser-settings-covers.vue b/frontend/src/components/browser/drawer/browser-settings-covers.vue deleted file mode 100644 index 8e5fb7468..000000000 --- a/frontend/src/components/browser/drawer/browser-settings-covers.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - diff --git a/frontend/src/components/reader/drawer/reader-settings-scope.vue b/frontend/src/components/reader/drawer/reader-settings-scope.vue index c69b0fddf..d3f478306 100644 --- a/frontend/src/components/reader/drawer/reader-settings-scope.vue +++ b/frontend/src/components/reader/drawer/reader-settings-scope.vue @@ -67,14 +67,6 @@ import GLOBAL_DEFAULTS from "@/choices/reader-defaults.json"; import ReaderSettingsControls from "./reader-settings-controls.vue"; import SettingsIndicatorDot from "./settings-indicator-dot.vue"; -/* - * PENDING SCHEMA REMOVAL - * - * ``readRtlInReverse`` and ``finishOnLastPage`` are gone from here because - * they no longer have controls to override. Their columns and serializer - * fields are deliberately kept -- see the notes in ``stores/reader.js``. Put - * them back in this list if the checkboxes come back. - */ const ATTRS = Object.freeze([ "fitTo", "readingDirection", diff --git a/frontend/src/stores/browser.js b/frontend/src/stores/browser.js index 912110a06..3f5b1bda0 100644 --- a/frontend/src/stores/browser.js +++ b/frontend/src/stores/browser.js @@ -58,13 +58,6 @@ const SEARCH_ROOT_COLLECTIONS = Object.freeze( ); const NON_BROWSE_COLLECTIONS = Object.freeze(new Set(["arcs", "folders"])); const SEARCH_HIDE_TIMEOUT = 5000; -const COVER_KEYS = Object.freeze(["customCovers", "dynamicCovers", "show"]); -const DYNAMIC_COVER_KEYS = Object.freeze([ - "filters", - "orderBy", - "orderReverse", - "q", -]); const FILTER_ONLY_KEYS = Object.freeze(["filters", "q"]); const METADATA_LOAD_KEYS = Object.freeze(["filters", "q", "mtime"]); @@ -276,8 +269,6 @@ export const useBrowserStore = defineStore("browser", { }, settings: { breadcrumbs: [], - customCovers: BROWSER_DEFAULTS.customCovers, - dynamicCovers: BROWSER_DEFAULTS.dynamicCovers, filters: BROWSER_DEFAULTS.filters, orderBy: BROWSER_DEFAULTS.orderBy, orderReverse: BROWSER_DEFAULTS.orderReverse, @@ -438,25 +429,6 @@ export const useBrowserStore = defineStore("browser", { ? toBrowseRoute({ name: "browser", params }) : { name: "home" }; }, - /* - * PENDING SCHEMA REMOVAL - * - * Dynamic covers are pinned on, so the dynamic keys always ride along and - * the ``parentRoute`` fallback the sort-name cover match needed is - * unreachable. ``customCovers`` and ``dynamicCovers`` are still sent in - * COVER_KEYS and still stored per user; the server ignores them. Drop them - * from COVER_KEYS, from the state below and from the schema together. - */ - coverSettings(state) { - const { collection } = liveBrowseParams(); - if (collection == "comics") { - return {}; - } - return this._filterSettings(state, [ - ...COVER_KEYS, - ...DYNAMIC_COVER_KEYS, - ]); - }, filterOnlySettings(state) { return this._filterSettings(state, FILTER_ONLY_KEYS); }, diff --git a/frontend/src/stores/reader.js b/frontend/src/stores/reader.js index 75ba1d68c..d86be86e5 100644 --- a/frontend/src/stores/reader.js +++ b/frontend/src/stores/reader.js @@ -120,8 +120,6 @@ export const useReaderStore = defineStore("reader", { fitTo: READER_DEFAULTS.fitTo, twoPages: READER_DEFAULTS.twoPages, readingDirection: READER_DEFAULTS.readingDirection, - readRtlInReverse: READER_DEFAULTS.readRtlInReverse, - finishOnLastPage: READER_DEFAULTS.finishOnLastPage, pageTransition: READER_DEFAULTS.pageTransition, cacheBook: READER_DEFAULTS.cacheBook, }, @@ -243,21 +241,6 @@ export const useReaderStore = defineStore("reader", { /* * GETTER Algorithms */ - /* - * PENDING SCHEMA REMOVAL - * - * "Read RTL Comics LTR" is no longer a user option, and it is pinned OFF - * — its default — so a right-to-left book is read right to left. The - * checkbox is gone from the reader settings drawer. - * ``SettingsReader.read_rtl_in_reverse`` and its serializer field are - * deliberately still there, so every user's stored preference survives - * and putting the toggle back is a revert rather than a data migration. - * Drop the column, its serializer and ``READER_DEFAULTS`` entries, and - * this pass-through, when we commit to this. - */ - setReadRTLInReverse(bookSettings) { - return bookSettings; - }, getBookSettings(book) { if (!book) { return {}; @@ -277,8 +260,7 @@ export const useReaderStore = defineStore("reader", { * shape confusion. */ const resultSettings = {}; - let bookSettings = book?.settings || {}; - bookSettings = this.setReadRTLInReverse(bookSettings); + const bookSettings = book?.settings || {}; const allSettings = [ this.globalSettings, this.intermediateSettings, @@ -696,17 +678,6 @@ export const useReaderStore = defineStore("reader", { }; page = Math.max(Math.min(book.maxPage, page), 0); const updates = { page }; - /* - * PENDING SCHEMA REMOVAL - * - * Finishing a book on its last page is no longer a user option; the - * checkbox is gone from the reader settings drawer. - * ``SettingsReader.finish_on_last_page`` and its serializer field are - * deliberately still there, so every user's stored preference survives - * and putting the toggle back is a revert rather than a data migration. - * Drop the column, its serializer and ``READER_DEFAULTS`` entries when - * we commit to this. - */ if (page >= book.maxPage) { updates["finished"] = true; } diff --git a/frontend/tests/unit/stats-tab.test.js b/frontend/tests/unit/stats-tab.test.js index e95de1a41..cb07e0bb8 100644 --- a/frontend/tests/unit/stats-tab.test.js +++ b/frontend/tests/unit/stats-tab.test.js @@ -48,13 +48,10 @@ const STATS = { topCollection: { publishers: 2 }, // camelCase, like the API renders it and like the choices maps are keyed. orderBy: { sortName: 2 }, - dynamicCovers: { true: 2 }, - finishOnLastPage: { true: 1 }, fitTo: { W: 1 }, readingDirection: { ltr: 1 }, viewMode: { cover: 2, table: 1 }, tableCoverSize: { sm: 1 }, - customCovers: { true: 2 }, multiSortCount: 0, }, collections: { @@ -87,6 +84,7 @@ const STATS = { browserOrderByUsers: { sortName: 1, "": 1 }, browserChosenOrderByUsers: { sortName: 1 }, readerGlobalFitToUsers: { W: 1, "": 1 }, + readerGlobalTwoPagesUsers: { true: 1, "": 1 }, }, usage: { bookmarkCount: 1, diff --git a/tests/test_settings_defaults.py b/tests/test_settings_defaults.py index a9908dc67..af6e6cb9b 100644 --- a/tests/test_settings_defaults.py +++ b/tests/test_settings_defaults.py @@ -93,13 +93,7 @@ def test_browser_field_defaults_match_the_settings_models(self) -> None: assert _field_default(SettingsDefaults, "table_columns") == {} def test_reader_field_defaults_match_reader_defaults(self) -> None: - for key in ( - "fit_to", - "reading_direction", - "two_pages", - "page_transition", - "cache_book", - ): + for key in READER_DEFAULTS: assert _field_default(SettingsDefaults, key) == READER_DEFAULTS[key], key def test_fresh_get_equals_factory(self) -> None: diff --git a/tests/test_telemeter_privacy.py b/tests/test_telemeter_privacy.py index c0b64f4ce..b7e1e8bb5 100644 --- a/tests/test_telemeter_privacy.py +++ b/tests/test_telemeter_privacy.py @@ -55,11 +55,8 @@ "stats.tagging.default_sources", "stats.sessions.top_collection", "stats.sessions.order_by", - "stats.sessions.dynamic_covers", "stats.sessions.view_mode", "stats.sessions.table_cover_size", - "stats.sessions.custom_covers", - "stats.sessions.finish_on_last_page", "stats.sessions.fit_to", "stats.sessions.reading_direction", # per_user draws on the same closed vocabularies as sessions, counted @@ -74,20 +71,12 @@ "stats.per_user.browser_chosen_view_mode_users", "stats.per_user.browser_table_cover_size_users", "stats.per_user.browser_chosen_table_cover_size_users", - "stats.per_user.browser_dynamic_covers_users", - "stats.per_user.browser_chosen_dynamic_covers_users", - "stats.per_user.browser_custom_covers_users", - "stats.per_user.browser_chosen_custom_covers_users", "stats.per_user.reader_global_fit_to_users", "stats.per_user.reader_chosen_fit_to_users", "stats.per_user.reader_global_two_pages_users", "stats.per_user.reader_chosen_two_pages_users", "stats.per_user.reader_global_reading_direction_users", "stats.per_user.reader_chosen_reading_direction_users", - "stats.per_user.reader_global_read_rtl_in_reverse_users", - "stats.per_user.reader_chosen_read_rtl_in_reverse_users", - "stats.per_user.reader_global_finish_on_last_page_users", - "stats.per_user.reader_chosen_finish_on_last_page_users", "stats.per_user.reader_global_page_transition_users", "stats.per_user.reader_chosen_page_transition_users", "stats.per_user.reader_global_cache_book_users", diff --git a/tests/test_user_data_restore.py b/tests/test_user_data_restore.py index 63286ba03..4953a24c7 100644 --- a/tests/test_user_data_restore.py +++ b/tests/test_user_data_restore.py @@ -465,6 +465,7 @@ def _browser_row(**overrides) -> dict: "order_reverse": 1, "order_extra_keys": "[]", "search": "", + # Retired sidecar columns; restore must ignore them. "custom_covers": 1, "dynamic_covers": 1, "twenty_four_hour_time": 0,