fix: harden favorites controller lifecycle

This commit is contained in:
wxs
2026-07-17 15:12:00 +08:00
parent ff1decd4a7
commit 8ebb50524c
4 changed files with 180 additions and 5 deletions
+20
View File
@@ -87,6 +87,26 @@ export function syncFavoriteRowPickers(options: {
}
}
export function disposeFavoriteRowPickers(document: Document): void {
const state = pickerDocumentStates.get(document);
if (state) {
closeFavoritePicker(document, state);
state.contextByButton.forEach((_context, button) => {
button.onclick = null;
button.remove();
});
state.contextByButton.clear();
state.pendingOperations.clear();
pickerDocumentStates.delete(document);
}
document.querySelectorAll<HTMLButtonElement>(ACTION_SELECTOR).forEach((button) => {
button.onclick = null;
button.remove();
});
removeFavoritePickerRoots(document);
}
type FavoritePickerContext = {
button: HTMLButtonElement;
creator: FavoriteCreatorInput;
+19 -4
View File
@@ -17,7 +17,10 @@ import { promptForAuthorIds } from "./author-id-dialog";
import { promptForBatchName } from "./batch-name-dialog";
import { createBatchPayload, type BatchPayload } from "./batch-payload";
import { promptForFavoriteFolderName } from "./favorite-folder-dialog";
import { syncFavoriteRowPickers } from "./favorite-row-picker";
import {
disposeFavoriteRowPickers,
syncFavoriteRowPickers
} from "./favorite-row-picker";
import {
createFavoritesDrawer,
type FavoritesDrawerController
@@ -232,6 +235,7 @@ export function createMarketController(options: CreateMarketControllerOptions) {
let cachedFavoritesState: FavoritesStateV1 | undefined;
let favoritesRefreshVersion = 0;
let favoriteActionButtons: HTMLButtonElement[] = [];
let needsFavoritesRefresh = false;
const favoritesDrawer: FavoritesDrawerController = createFavoritesDrawer(
options.document,
{
@@ -277,6 +281,9 @@ export function createMarketController(options: CreateMarketControllerOptions) {
return;
}
if (favoriteControlsMissing) {
needsFavoritesRefresh = true;
}
scheduleSync();
});
const observationRoot = options.document.body ?? options.document.documentElement;
@@ -522,7 +529,6 @@ export function createMarketController(options: CreateMarketControllerOptions) {
const ready = (async () => {
await runSyncCycle();
await refreshFavorites();
await waitForDomSettled();
})();
return {
@@ -533,6 +539,7 @@ export function createMarketController(options: CreateMarketControllerOptions) {
options.window.clearTimeout(scheduledSyncTimeoutId);
scheduledSyncTimeoutId = null;
}
disposeFavoriteRowPickers(options.document);
favoritesDrawer.dispose();
},
ready
@@ -725,6 +732,9 @@ export function createMarketController(options: CreateMarketControllerOptions) {
return;
}
cachedFavoritesState = undefined;
favoriteActionButtons = [];
disposeFavoriteRowPickers(options.document);
favoritesDrawer.setError("收藏夹暂不可用");
}
}
@@ -842,8 +852,9 @@ export function createMarketController(options: CreateMarketControllerOptions) {
try {
return await mutation();
} catch (error) {
favoritesDrawer.setError(readFavoriteOperationError(error));
throw error;
const message = readFavoriteOperationError(error);
favoritesDrawer.setError(message);
throw new Error(message);
} finally {
favoritesDrawer.setBusy(false);
}
@@ -1828,6 +1839,10 @@ export function createMarketController(options: CreateMarketControllerOptions) {
try {
toolbar = ensurePluginToolbar(options.document, toolbarHandlers);
await hydrateCurrentPage();
if (needsFavoritesRefresh) {
needsFavoritesRefresh = false;
await refreshFavorites();
}
applyCurrentView();
lastKnownPageSignature = readMarketPageSignature(options.document);
} finally {