Skip to content

Commit

Permalink
Fix reader location not persisted if tab closed quickly #599
Browse files Browse the repository at this point in the history
This change needs to be persisted remotely so that the value can be synced to
other devices running Zotero. To achieve this, we use a fetch request with the
`keepalive` option, ensuring the request is executed even after the tab is
closed.
  • Loading branch information
tnajdek committed Feb 11, 2025
1 parent 80cfc0f commit a497e55
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 23 deletions.
34 changes: 17 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"redux-async-queue": "^1.0.0",
"redux-thunk": "^3.1.0",
"use-debounce": "^10.0.4",
"zotero-api-client": "^0.46.0"
"zotero-api-client": "^0.47.0"
},
"devDependencies": {
"@babel/core": "^7.26.0",
Expand Down
12 changes: 7 additions & 5 deletions src/js/component/reader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '../actions';
import { PDFWorker } from '../common/pdf-worker';
import { getItemViewState, updateItemViewState } from '../common/viewstate'
import { useFetchingState } from '../hooks';
import { useFetchingState, useTrackedSettingsKey } from '../hooks';
import TagPicker from './item-details/tag-picker';
import { READER_CONTENT_TYPES } from '../constants/reader';
import Portal from './portal';
Expand Down Expand Up @@ -166,7 +166,7 @@ const Reader = () => {
});
const location = useSelector(state => state.current.location);
const pageIndexSettingKey = getLastPageIndexSettingKey(attachmentKey, libraryKey);
const locationValue = useSelector(state => state.libraries[userLibraryKey]?.settings?.entries?.[pageIndexSettingKey]?.value ?? null);
const { value: locationValue, update: updateLocationValueSync } = useTrackedSettingsKey(pageIndexSettingKey, userLibraryKey);
const attachmentItem = useSelector(state => state.libraries[libraryKey]?.items[attachmentKey]);
const isFetchingUrl = useSelector(state => state.libraries[libraryKey]?.attachmentsUrl[attachmentKey]?.isFetching ?? false);
const url = useSelector(state => state.libraries[libraryKey]?.attachmentsUrl[attachmentKey]?.url);
Expand Down Expand Up @@ -380,11 +380,13 @@ const Reader = () => {
state.importedAnnotations, state.viewState
]);

const handleOnBeforeUnload = useCallback(async () => {
const handleOnBeforeUnload = useCallback(() => {
if (pendingViewState.current) {
await updateItemViewState(attachmentItem.key, libraryKey, pendingViewState.current);
const pageIndexKey = PAGE_INDEX_KEY_LOOKUP[attachmentItem.contentType];
updateItemViewState(attachmentItem.key, libraryKey, pendingViewState.current);
updateLocationValueSync(pendingViewState.current[pageIndexKey]);
}
}, [attachmentItem, libraryKey]);
}, [attachmentItem, libraryKey, updateLocationValueSync]);

useEffect(() => {
// pdf js stores last page in localStorage but we want to use one from user library settings instead
Expand Down
1 change: 1 addition & 0 deletions src/js/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './use-navigation-state';
export * from './state';
export * from './use-item-action-handlers';
export * from './use-items-count';
export * from './use-tracked-settings-key';
26 changes: 26 additions & 0 deletions src/js/hooks/use-tracked-settings-key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import api from 'zotero-api-client';

import { useSelector } from 'react-redux';

// Normally components don't need to track version number of the setting (to avoid re-renders when
// the version changes) and updates are dispatched asynchonously (and might be queued, e.g., to
// avoid out-of-order updates). However, to support updating the value on tab/window close, we need
// a synchroneus version with keepalive fetch call. This hook tracks version of the setting and
// provides a method to update the setting synchronously, but using this hook might cause re-renders.
export const useTrackedSettingsKey = (settingsKey, libraryKey) => {
const config = useSelector(state => state.config);
const value = useSelector(state => state.libraries[libraryKey]?.settings?.entries?.[settingsKey]?.value ?? null);
const version = useSelector(state => state.libraries[libraryKey]?.settings?.entries?.[settingsKey]?.version ?? null);

const update = (newValue) => {
if(newValue !== value) {
api(config.apiKey, config.apiConfig)
.library(libraryKey)
.version(version)
.settings(settingsKey)
.put({ value: newValue }, { keepalive: true });
}
}

return { value, update };
};

0 comments on commit a497e55

Please sign in to comment.