Skip to content

Commit

Permalink
Merge pull request #2012 from talal-najam/feature/floating-toggle-button
Browse files Browse the repository at this point in the history
Add reading preference switch to navbar
  • Loading branch information
osamasayed authored Sep 5, 2023
2 parents b622a0c + 8293cd9 commit 7b97159
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 20 deletions.
10 changes: 9 additions & 1 deletion src/components/GlobalScrollListener.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { useDispatch } from 'react-redux';

import useScrollDirection, { ScrollDirection } from '@/hooks/useScrollDirection';
import { setIsVisible } from '@/redux/slices/navbar';
import { setIsExpanded } from '@/redux/slices/QuranReader/contextMenu';
import {
setIsExpanded,
setShowReadingPreferenceSwitcher,
} from '@/redux/slices/QuranReader/contextMenu';

const GlobalScrollListener = () => {
const dispatch = useDispatch();
Expand All @@ -23,6 +26,11 @@ const GlobalScrollListener = () => {
dispatch({ type: setIsExpanded.type, payload: true });
dispatch({ type: setIsVisible.type, payload: true });
}
if (newYPosition > 150 && direction === ScrollDirection.Down) {
dispatch({ type: setShowReadingPreferenceSwitcher.type, payload: true });
} else if (newYPosition <= 150 && direction === ScrollDirection.Up) {
dispatch({ type: setShowReadingPreferenceSwitcher.type, payload: false });
}
},
[dispatch],
);
Expand Down
11 changes: 8 additions & 3 deletions src/components/QuranReader/ContextMenu.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,15 @@
}
}

.halfSection {
width: 50%; // when reading preference toggle is hidden
}

.section {
width: 50%;
display: flex;
align-items: center;
width: calc(100% / 3);
@include breakpoints.smallerThanTablet {
width: 50%;
}
}

.alignStart {
Expand Down
22 changes: 19 additions & 3 deletions src/components/QuranReader/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import useTranslation from 'next-translate/useTranslation';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';

import styles from './ContextMenu.module.scss';
import ReadingPreferenceSwitcher, {
ReadingPreferenceSwitcherType,
} from './ReadingPreferenceSwitcher';

import { SwitchSize } from '@/dls/Switch/Switch';
import ChevronDownIcon from '@/icons/chevron-down.svg';
import { selectNavbar } from '@/redux/slices/navbar';
import { selectContextMenu } from '@/redux/slices/QuranReader/contextMenu';
Expand All @@ -31,7 +35,10 @@ const ContextMenu = () => {
const isSidebarNavigationVisible = useSelector(selectIsSidebarNavigationVisible);
const { t, lang } = useTranslation('common');
const isSideBarVisible = useSelector(selectNotes, shallowEqual).isVisible;
const { isExpanded } = useSelector(selectContextMenu, shallowEqual);
const { isExpanded, showReadingPreferenceSwitcher } = useSelector(
selectContextMenu,
shallowEqual,
);
const isNavbarVisible = useSelector(selectNavbar, shallowEqual).isVisible;
const { verseKey, chapterId, page, hizb } = useSelector(selectLastReadVerseKey, shallowEqual);
const chapterData = useMemo(() => {
Expand Down Expand Up @@ -66,7 +73,7 @@ const ContextMenu = () => {
style={{ '--progress': `${progress}%` }} // this is to pass the value to css so it can be used to show the progress bar.
>
<div className={styles.sectionsContainer}>
<div className={styles.section}>
<div className={showReadingPreferenceSwitcher ? styles.section : styles.halfSection}>
<div className={classNames(styles.row)}>
<p
className={classNames(styles.bold, styles.alignStart, styles.surahName, {
Expand Down Expand Up @@ -100,7 +107,16 @@ const ContextMenu = () => {
</p>
</div>
</div>
<div className={classNames(styles.section, styles.leftSection)}>
{showReadingPreferenceSwitcher && (
<div className={styles.halfSection}>
<ReadingPreferenceSwitcher
size={SwitchSize.XSmall}
isIconsOnly
type={ReadingPreferenceSwitcherType.ContextMenu}
/>
</div>
)}
<div className={showReadingPreferenceSwitcher ? styles.section : styles.halfSection}>
<div className={classNames(styles.row)}>
<p
className={classNames(styles.alignEnd, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
@use "src/styles/breakpoints";

.container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}

.spinner {
margin-inline-end: var(--spacing-xsmall);
}

.preferenceTextContainer {
padding-inline-start: var(--spacing-xsmall);
}

.iconContainer {
& > svg {
width: 15px;
display: block;
margin: auto;
}

@include breakpoints.smallerThanTablet {
& > svg {
-webkit-margin-end: 0;
margin-inline-end: 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import React from 'react';

import classNames from 'classnames';
import useTranslation from 'next-translate/useTranslation';

import styles from '@/components/QuranReader/ReadingPreferenceSwitcher/ReadingPreference.module.scss';
import Spinner from '@/dls/Spinner/Spinner';
import BookIcon from '@/icons/book.svg';
import ReaderIcon from '@/icons/reader.svg';
import { ReadingPreference } from 'types/QuranReader';

type Props = {
readingPreference: ReadingPreference;
selectedReadingPreference: ReadingPreference;
isIconsOnly?: boolean;
isLoading: boolean;
};

export const readingPreferenceIcons = {
[ReadingPreference.Reading]: <ReaderIcon />,
[ReadingPreference.Translation]: <BookIcon />,
};

const LoadingSwitcher: React.FC<Props> = ({
readingPreference,
selectedReadingPreference,
isIconsOnly = false,
isLoading,
}) => {
const { t } = useTranslation('common');
Expand All @@ -23,10 +33,23 @@ const LoadingSwitcher: React.FC<Props> = ({
<span>
<Spinner className={styles.spinner} />
</span>
<span>{t(`reading-preference.${readingPreference}`)}</span>
{!isIconsOnly && (
<span className={styles.preferenceTextContainer}>
{t(`reading-preference.${selectedReadingPreference}`)}
</span>
)}
</div>
) : (
t(`reading-preference.${selectedReadingPreference}`)
<div className={styles.container}>
<span className={classNames(styles.iconContainer, isIconsOnly && styles.iconsCenter)}>
{readingPreferenceIcons[selectedReadingPreference]}
</span>
{!isIconsOnly && (
<span className={styles.preferenceTextContainer}>
{t(`reading-preference.${selectedReadingPreference}`)}
</span>
)}
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
$readingPreferenceMaxWidth: calc(20 * var(--spacing-large));
$contextMenuReadingPreferenceMaxWidth: calc(10 * var(--spacing-large));
.container {
max-width: $readingPreferenceMaxWidth;
margin-block-start: var(--spacing-small);
margin-block-end: var(--spacing-xxsmall);
margin-inline-start: auto;
margin-inline-end: auto;
padding-block-start: 0;
padding-block-end: 0;
padding-inline-start: var(--spacing-small);
padding-inline-end: var(--spacing-small);
}

.surahHeaderContainer {
margin-block-start: var(--spacing-small);
margin-block-end: var(--spacing-xxsmall);
}

.contextMenuContainer {
max-width: $contextMenuReadingPreferenceMaxWidth;
}
48 changes: 42 additions & 6 deletions src/components/QuranReader/ReadingPreferenceSwitcher/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,40 @@
import classNames from 'classnames';
import { useRouter } from 'next/router';
import { useSelector } from 'react-redux';

import LoadingSwitcher from './ReadingPreferenceOption';
import styles from './ReadingPreferenceSwitcher.module.scss';

import Switch from '@/dls/Switch/Switch';
import Switch, { SwitchSize } from '@/dls/Switch/Switch';
import usePersistPreferenceGroup from '@/hooks/auth/usePersistPreferenceGroup';
import {
selectReadingPreferences,
setReadingPreference,
} from '@/redux/slices/QuranReader/readingPreferences';
import { selectLastReadVerseKey } from '@/redux/slices/QuranReader/readingTracker';
import { logValueChange } from '@/utils/eventLogger';
import PreferenceGroup from 'types/auth/PreferenceGroup';
import { ReadingPreference } from 'types/QuranReader';

const ReadingPreferenceSwitcher = () => {
export enum ReadingPreferenceSwitcherType {
SurahHeader = 'surah_header',
ContextMenu = 'context_menu',
}

interface Props {
size?: SwitchSize;
isIconsOnly?: boolean;
type?: ReadingPreferenceSwitcherType;
}

const ReadingPreferenceSwitcher: React.FC<Props> = ({
size,
isIconsOnly = false,
type = ReadingPreferenceSwitcherType.SurahHeader,
}) => {
const readingPreferences = useSelector(selectReadingPreferences);
const lastReadVerseKey = useSelector(selectLastReadVerseKey);
const lastReadVerse = lastReadVerseKey.verseKey?.split(':')[1];
const { readingPreference } = readingPreferences;
const {
actions: { onSettingsChange },
Expand All @@ -30,6 +49,7 @@ const ReadingPreferenceSwitcher = () => {
readingPreference={readingPreference}
selectedReadingPreference={ReadingPreference.Translation}
isLoading={isLoading}
isIconsOnly={isIconsOnly}
/>
),
value: ReadingPreference.Translation,
Expand All @@ -40,18 +60,28 @@ const ReadingPreferenceSwitcher = () => {
readingPreference={readingPreference}
selectedReadingPreference={ReadingPreference.Reading}
isLoading={isLoading}
isIconsOnly={isIconsOnly}
/>
),
value: ReadingPreference.Reading,
},
];

const onViewSwitched = (view: ReadingPreference) => {
logValueChange('reading_preference', readingPreference, view);
logValueChange(`${type}_reading_preference`, readingPreference, view);

// drop `startingVerse` from query params
const newQueryParams = { ...router.query };
delete newQueryParams.startingVerse;

// Track `startingVerse` once we're past the start of the page so we can
// continue from the same ayah when switching views. Without the > 1 check,
// switching views at the start of the page causes unnecessary scrolls

if (type === ReadingPreferenceSwitcherType.SurahHeader) {
delete newQueryParams.startingVerse;
} else if (parseInt(lastReadVerse, 10) > 1) {
newQueryParams.startingVerse = lastReadVerse;
}

const newUrlObject = {
pathname: router.pathname,
query: newQueryParams,
Expand All @@ -69,11 +99,17 @@ const ReadingPreferenceSwitcher = () => {
};

return (
<div className={styles.container}>
<div
className={classNames(styles.container, {
[styles.surahHeaderContainer]: type === ReadingPreferenceSwitcherType.SurahHeader,
[styles.contextMenuContainer]: type === ReadingPreferenceSwitcherType.ContextMenu,
})}
>
<Switch
items={readingPreferencesOptions}
selected={readingPreference}
onSelect={onViewSwitched}
size={size}
/>
</div>
);
Expand Down
14 changes: 14 additions & 0 deletions src/components/dls/Switch/Switch.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ $separatorOpacity: 0.2;
); // use grid to prevent layout shift when `font-family: bold` on item selected https://css-tricks.com/bold-on-hover-without-the-layout-shift/
}

.xSmallContainer {
padding-block-start: 0;
padding-block-end: 0;
padding-inline-start: 0;
padding-inline-end: 0;
}

.item {
cursor: pointer;
color: var(--color-text-default);
Expand Down Expand Up @@ -94,3 +101,10 @@ $background-container: calc(0.8 * var(--spacing-xxsmall));
padding-inline-start: var(--spacing-small);
padding-inline-end: var(--spacing-small);
}

.itemXSmall {
padding-block-start: var(--spacing-micro);
padding-block-end: var(--spacing-micro);
padding-inline-start: var(--spacing-micro);
padding-inline-end: var(--spacing-micro);
}
8 changes: 7 additions & 1 deletion src/components/dls/Switch/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Item = {
disabled?: boolean;
};
export enum SwitchSize {
XSmall = 'xsmall',
Small = 'small',
Normal = 'normal',
Large = 'large',
Expand All @@ -29,7 +30,11 @@ const Switch = ({ items, onSelect, selected, size = SwitchSize.Normal }: SwitchP
const selectedIndex = items.findIndex((item) => item.value === selected);
const { locale } = useRouter();
return (
<div className={styles.container}>
<div
className={classNames(styles.container, {
[styles.xSmallContainer]: size === SwitchSize.XSmall,
})}
>
{items.map((item) => (
<button
disabled={item.disabled}
Expand All @@ -38,6 +43,7 @@ const Switch = ({ items, onSelect, selected, size = SwitchSize.Normal }: SwitchP
[styles.itemLarge]: size === SwitchSize.Large,
[styles.itemNormal]: size === SwitchSize.Normal,
[styles.itemSmall]: size === SwitchSize.Small,
[styles.itemXSmall]: size === SwitchSize.XSmall,
})}
key={item.value}
onClick={() => onSelect(item.value)}
Expand Down
9 changes: 7 additions & 2 deletions src/redux/slices/QuranReader/contextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import SliceName from '@/redux/types/SliceName';

export type ContextMenu = {
isExpanded: boolean;
showReadingPreferenceSwitcher: boolean;
};

const initialState: ContextMenu = { isExpanded: true };
const initialState: ContextMenu = { isExpanded: true, showReadingPreferenceSwitcher: false };

export const contextMenuSlice = createSlice({
name: SliceName.CONTEXT_MENU,
Expand All @@ -17,10 +18,14 @@ export const contextMenuSlice = createSlice({
...state,
isExpanded: action.payload,
}),
setShowReadingPreferenceSwitcher: (state: ContextMenu, action: PayloadAction<boolean>) => ({
...state,
showReadingPreferenceSwitcher: action.payload,
}),
},
});

export const { setIsExpanded } = contextMenuSlice.actions;
export const { setIsExpanded, setShowReadingPreferenceSwitcher } = contextMenuSlice.actions;

export const selectContextMenu = (state: RootState) => state.contextMenu;

Expand Down

0 comments on commit 7b97159

Please sign in to comment.