Skip to content

Commit 2e0399f

Browse files
committed
refactor(bookmarks): improve code quality and type safety
- Create useBookmarkBase hook for shared bookmark functionality - Extract useCollections and useBookmarkCollections hooks from SaveToCollectionAction - Add isBookmarkSyncError utility to centralize error checking - Strengthen Bookmark type to use BookmarkType enum instead of string - Standardize API parameter naming (mushafId) with proper TypeScript types - Refactor usePageBookmark and useVerseBookmark to use base hook - Fix BookmarkIcon to properly use useVerseBookmark hook - Use reactive useIsLoggedIn hook consistently across components - Add Sentry error logging to useSyncUserData - Rename Collection to CollectionOption in SaveToCollectionModal for clarity
1 parent 1a2bc75 commit 2e0399f

File tree

14 files changed

+704
-435
lines changed

14 files changed

+704
-435
lines changed

src/components/Collection/SaveToCollectionModal/SaveToCollectionModal.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@ import { logButtonClick, logEvent } from '@/utils/eventLogger';
1515
import { RuleType } from 'types/FieldRule';
1616
import { FormFieldType } from 'types/FormField';
1717

18-
export type Collection = {
18+
/**
19+
* UI-specific collection type for the save modal with checked state
20+
* This is different from types/Collection which is the API response type
21+
*/
22+
export type CollectionOption = {
1923
id: string;
2024
name: string;
2125
checked?: boolean;
2226
};
2327

2428
type SaveToCollectionModalProps = {
2529
isOpen: boolean;
26-
collections: Collection[];
27-
onCollectionToggled: (collection: Collection, newValue: boolean) => void;
30+
collections: CollectionOption[];
31+
onCollectionToggled: (collection: CollectionOption, newValue: boolean) => void;
2832
onNewCollectionCreated: (name: string) => Promise<void>;
2933
onClose?: () => void;
3034
verseKey: string;
@@ -68,7 +72,7 @@ const SaveToCollectionModal = ({
6872
logButtonClick('save_to_collection_add_new_collection');
6973
};
7074

71-
const handleCheckboxChange = (collection: Collection) => (checked: boolean) => {
75+
const handleCheckboxChange = (collection: CollectionOption) => (checked: boolean) => {
7276
const eventData = {
7377
verseKey,
7478
collectionId: collection.id,

src/components/QuranReader/TranslationView/BookmarkIcon.tsx

Lines changed: 22 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,49 @@
1-
/* eslint-disable react-func/max-lines-per-function */
2-
import { useMemo } from 'react';
3-
41
import classNames from 'classnames';
52
import useTranslation from 'next-translate/useTranslation';
6-
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
7-
import { useSWRConfig } from 'swr';
3+
import { shallowEqual, useSelector } from 'react-redux';
84

95
import styles from './TranslationViewCell.module.scss';
106

117
import Button, { ButtonSize, ButtonVariant } from '@/dls/Button/Button';
12-
import { ToastStatus, useToast } from '@/dls/Toast/Toast';
8+
import useVerseBookmark from '@/hooks/useVerseBookmark';
139
import BookmarkedIcon from '@/icons/bookmark.svg';
14-
import { selectBookmarks, toggleVerseBookmark } from '@/redux/slices/QuranReader/bookmarks';
1510
import { selectQuranReaderStyles } from '@/redux/slices/QuranReader/styles';
1611
import { getMushafId } from '@/utils/api';
17-
import { deleteBookmarkById } from '@/utils/auth/api';
18-
import { makeBookmarkUrl } from '@/utils/auth/apiPaths';
19-
import { isLoggedIn } from '@/utils/auth/login';
2012
import { logButtonClick } from '@/utils/eventLogger';
21-
import BookmarksMap from 'types/BookmarksMap';
22-
import BookmarkType from 'types/BookmarkType';
2313
import Verse from 'types/Verse';
2414

2515
type Props = {
2616
verse: Verse;
27-
pageBookmarks: BookmarksMap | undefined;
2817
bookmarksRangeUrl: string;
2918
};
3019

31-
const BookmarkIcon: React.FC<Props> = ({ verse, pageBookmarks, bookmarksRangeUrl }) => {
20+
/**
21+
* BookmarkIcon component that shows a bookmark icon for bookmarked verses.
22+
* Only renders when the verse is bookmarked.
23+
*
24+
* @returns {JSX.Element | null} The bookmark icon button or null if not bookmarked
25+
*/
26+
const BookmarkIcon: React.FC<Props> = ({ verse, bookmarksRangeUrl }) => {
3227
const { t } = useTranslation('quran-reader');
3328
const quranReaderStyles = useSelector(selectQuranReaderStyles, shallowEqual);
34-
const bookmarkedVerses = useSelector(selectBookmarks, shallowEqual);
35-
const { cache, mutate } = useSWRConfig();
36-
const toast = useToast();
37-
const dispatch = useDispatch();
38-
39-
const isVerseBookmarked = useMemo(() => {
40-
const isUserLoggedIn = isLoggedIn();
41-
if (isUserLoggedIn && pageBookmarks) {
42-
return !!pageBookmarks[verse.verseKey];
43-
}
44-
return !!bookmarkedVerses[verse.verseKey];
45-
}, [bookmarkedVerses, pageBookmarks, verse.verseKey]);
29+
const mushafId = getMushafId(quranReaderStyles.quranFont, quranReaderStyles.mushafLines).mushaf;
4630

31+
const { isVerseBookmarked, handleToggleBookmark } = useVerseBookmark({
32+
verse: {
33+
verseKey: verse.verseKey,
34+
verseNumber: verse.verseNumber,
35+
chapterId: verse.chapterId,
36+
},
37+
mushafId,
38+
bookmarksRangeUrl,
39+
});
40+
41+
// Only show the icon when the verse is bookmarked
4742
if (!isVerseBookmarked) return null;
4843

49-
const mushafId = getMushafId(quranReaderStyles.quranFont, quranReaderStyles.mushafLines).mushaf;
50-
5144
const onClick = () => {
5245
logButtonClick('translation_view_un_bookmark_verse');
53-
54-
if (isLoggedIn()) {
55-
const bookmarkedVersesRange = cache.get(bookmarksRangeUrl);
56-
const nextBookmarkedVersesRange = {
57-
...bookmarkedVersesRange,
58-
[verse.verseKey]: !isVerseBookmarked,
59-
};
60-
mutate(bookmarksRangeUrl, nextBookmarkedVersesRange, {
61-
revalidate: false,
62-
});
63-
64-
cache.delete(
65-
makeBookmarkUrl(
66-
mushafId,
67-
Number(verse.chapterId),
68-
BookmarkType.Ayah,
69-
Number(verse.verseNumber),
70-
),
71-
);
72-
73-
const bookmarkId = pageBookmarks[verse.verseKey].id;
74-
if (bookmarkId) {
75-
deleteBookmarkById(bookmarkId).catch((err) => {
76-
if (err.status === 400) {
77-
toast(t('common:error.bookmark-sync'), {
78-
status: ToastStatus.Error,
79-
});
80-
return;
81-
}
82-
toast(t('common:error.general'), {
83-
status: ToastStatus.Error,
84-
});
85-
});
86-
}
87-
} else {
88-
dispatch(toggleVerseBookmark(verse.verseKey));
89-
}
46+
handleToggleBookmark();
9047
};
9148

9249
return (

0 commit comments

Comments
 (0)