Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio keeps playing while navigating the site #1336

Merged
merged 14 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const jestConfig: JestConfigWithTsJest = {
lines: 0.1,
},
},
testTimeout: 15_000,
};

export default jestConfig;
Expand Down
71 changes: 58 additions & 13 deletions src/app/common/components/AudioPlayer/AudioPlayer.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import PauseBtn from '@images/audio-player/PauseBtn.webp';
import PlayBtn from '@images/audio-player/PlayBtn.webp';

import React, { useEffect, useRef, useState } from 'react';
import { useAudioContext } from '@stores/root-store';
import { useAudioContext, useModalContext } from '@stores/root-store';

import base64ToUrl from '../../utils/base64ToUrl.utility';

const AudioPlayer:React.FC<{ immediatelyPlay?:boolean }> = ({ immediatelyPlay }) => {
const AudioPlayer: React.FC<{ immediatelyPlay?: boolean }> = ({ immediatelyPlay }) => {
const { audio } = useAudioContext();
const [audioState, setAudioState] = useState(audio);
const [isPlaying, setIsPlaying] = useState<boolean>(false);

const { modalStore: { modalsState } } = useModalContext();
const audioModalState = modalsState.audio;

const audioPlayer = useRef<HTMLMediaElement>(null);
const progressBar = useRef<HTMLInputElement | null>(null);
const animationRef = useRef<number>();
Expand Down Expand Up @@ -45,38 +49,79 @@ const AudioPlayer:React.FC<{ immediatelyPlay?:boolean }> = ({ immediatelyPlay })
setIsPlaying(!isPlaying);
if (!isPlaying) {
audioPlayer.current?.play();
animationRef.current = requestAnimationFrame(whilePlaying);
} else {
audioPlayer.current?.pause();
cancelAnimationFrame(Number(animationRef.current));
}
};

const play = () => {
setIsPlaying(true);
audioPlayer.current?.play();
};

const pause = () => {
setIsPlaying(false);
audioPlayer.current?.pause();
};

const changeRange = () => {
setMaxDuration();
if (audioPlayer.current) {
audioPlayer.current.currentTime = Number(progressBar.current?.value);
}
changePlayerCurrentTime();
};

useEffect(
() => {
setMaxDuration();
if (immediatelyPlay && !isPlaying) {
setTimeout(() => {
togglePlayPause();
}, (1000));
}
() => () => {
cancelAnimationFrame(Number(animationRef.current));
},
[audioPlayer.current?.readyState],
[],
);

useEffect(() => {
if (!isPlaying) {
cancelAnimationFrame(Number(animationRef.current));
} else {
animationRef.current = requestAnimationFrame(whilePlaying);
}
}, [isPlaying]);

const resetAudio = () => {
if (audioPlayer.current && progressBar.current) {
setMaxDuration();
audioPlayer.current.currentTime = 0;
progressBar.current.value = '0';
changePlayerCurrentTime();
}
};

useEffect(() => {
resetAudio();
if (!isPlaying) {
play();
}
}, [audioState]);

useEffect(() => {
if (!audioModalState) return;
setAudioState(audio ? { ...audio } : audio);
}, [audioModalState]);

const handleOnLoadedData = () => {
resetAudio();
if (immediatelyPlay) {
play();
}
};

return (
<div className="audioPlayer">
<audio
ref={audioPlayer}
src={base64ToUrl(audio?.base64, audio?.mimeType)}
src={base64ToUrl(audioState?.base64, audioState?.mimeType)}
preload="metadata"
onLoadedData={handleOnLoadedData}
/>
{isPlaying
? (
Expand Down
2 changes: 1 addition & 1 deletion src/app/common/components/ProtectedComponent.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import FRONTEND_ROUTES from '../constants/frontend-routes.constants';
import AuthService from '../services/auth-service/AuthService';

type PropsWithChildren = { children: ReactNode };
const ProtectedComponent:FC<PropsWithChildren> = ({ children }): ReactNode => {
const ProtectedComponent:FC<PropsWithChildren> = ({ children }) => {
const navigate = useNavigate();
const isLoggedIn = AuthService.isLoggedIn();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CancelBtn from '@images/utils/Cancel_btn.svg';

import { observer } from 'mobx-react-lite';
import { ChangeEvent, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import donateButtonRequest from '@app/common/requests/donateButtonRequest';
import { PositiveNumber } from '@constants/custom-types.constants';
import { useModalContext } from '@stores/root-store';
Expand Down Expand Up @@ -136,7 +137,7 @@ const DonatesModal = () => {
>
Я даю згоду на обробку моїх
{' '}
<a className="privacyPolicy" href="/privacy-policy">персональних даних</a>
<Link className="privacyPolicy" to="/privacy-policy">персональних даних</Link>
</Checkbox>
</div>
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const ListenTextModal = () => {
let timeoutId: NodeJS.Timeout;

const handleClose = () => {
if (closing) return;
setClosing(true);
timeoutId = setTimeout(() => {
setModal('audio');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@

z-index: l.$stickyEl;
box-shadow: 0 2px 15px rgba(163, 163, 163, 0.25);

align-self: center;
}

.fadeInAnimation {
animation: fadeIn .5s;
}
.fadeOutAnimation {
animation: fadeOut .5s;
animation: fadeOut .5s forwards;
}

@include mut.fadeKeyframes("fadeIn", "fadeIn") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import './RelatedFigureItemModal.styles.scss';

import { observer } from 'mobx-react-lite';
import { Link } from 'react-router-dom';
import CancelBtn from '@assets/images/utils/Cancel_btn_mobile.svg';
import useMobx, { useModalContext } from '@stores/root-store';

Expand Down Expand Up @@ -55,13 +56,13 @@ const RelatedFiguresItemModal = () => {
</div>
</div>
</div>
<a
<Link
className="redirectionButton"
href={`../${relation?.url}`}
to={`../${relation?.url}`}
onClick={handleClick}
>
<p>Перейти на сторінку постаті</p>
</a>
</Link>
</Modal>
);
};
Expand Down
2 changes: 2 additions & 0 deletions src/app/layout/app/App.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ModalWrapper from '@layout/ModalWrapper.component';
import { useModalContext } from '@stores/root-store';

import CopyWithCopyright from '@/app/common/components/CopyWithCopyright.component';
import ListenTextModal from '@/app/common/components/modals/ListenText/ListenText.component';

import Footer from '../footer/Footer.component';

Expand All @@ -25,6 +26,7 @@ const App = () => {
const { modalStore: { isPageDimmed } } = useModalContext();
return (
<div className="mainBlockWrapper" style={{ position: 'relative' }}>
<ListenTextModal />
<ToastContainer position="bottom-right" limit={3} />
<CopyWithCopyright copyrightText={CopyrightText}>
<ModalWrapper />
Expand Down
14 changes: 7 additions & 7 deletions src/app/layout/footer/Footer.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ const Footer = () => {
</div>
<div className="usefulLinksColumnContainer">
<ul className="usefulLinksColumn">
<li><a href={FRONTEND_ROUTES.BASE}>Головна</a></li>
<li><a href={FRONTEND_ROUTES.OTHER_PAGES.CATALOG}>Стріткоди</a></li>
<li><a href={FRONTEND_ROUTES.OTHER_PAGES.ABOUT_US}>Про нас</a></li>
<li><a href={FRONTEND_ROUTES.OTHER_PAGES.CONTACT_US}>Контакти</a></li>
<li><Link to={FRONTEND_ROUTES.BASE}>Головна</Link></li>
<li><Link to={FRONTEND_ROUTES.OTHER_PAGES.CATALOG}>Стріткоди</Link></li>
<li><Link to={FRONTEND_ROUTES.OTHER_PAGES.ABOUT_US}>Про нас</Link></li>
<li><Link to={FRONTEND_ROUTES.OTHER_PAGES.CONTACT_US}>Контакти</Link></li>
</ul>
<ul className="usefulLinksColumn">
<li><a href={FRONTEND_ROUTES.OTHER_PAGES.PARTNERS}>Партнери</a></li>
<li><Link to={FRONTEND_ROUTES.OTHER_PAGES.PARTNERS}>Партнери</Link></li>
{hasVacancies ? (
<li>
<Link
Expand All @@ -58,10 +58,10 @@ const Footer = () => {
</Link>
</li>
) : null}
<li><a href={FRONTEND_ROUTES.OTHER_PAGES.SUPPORT_US}>Донати</a></li>
<li><Link to={FRONTEND_ROUTES.OTHER_PAGES.SUPPORT_US}>Донати</Link></li>
</ul>
<ul className="usefulLinksColumn supportLinks">
<li><a href={FRONTEND_ROUTES.OTHER_PAGES.PRIVACY_POLICY}>Політика конфіденційності</a></li>
<li><Link to={FRONTEND_ROUTES.OTHER_PAGES.PRIVACY_POLICY}>Політика конфіденційності</Link></li>
<li><ContactUsModal toggleState={() => { }} text="Зворотний зв&apos;язок" /></li>
<li>
<a href={REPORTS} target="_blank" rel="noreferrer">
Expand Down
29 changes: 23 additions & 6 deletions src/app/layout/header/HeaderBlock.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RefObject, useCallback, useEffect, useRef, useState,
} from 'react';
import { useMediaQuery } from 'react-responsive';
import { Link } from 'react-router-dom';
import useEventListener from '@hooks/external/useEventListener.hook';
import useOnClickOutside from '@hooks/stateful/useClickOutside.hook';
import useToggle from '@hooks/stateful/useToggle.hook';
Expand All @@ -21,6 +22,7 @@ import useMobx, { useModalContext } from '@stores/root-store';
import { Button, Input, Popover, PopoverProps } from 'antd';

import StreetcodesApi from '@/app/api/streetcode/streetcodes.api';
import FRONTEND_ROUTES from '@/app/common/constants/frontend-routes.constants';
import { joinToStreetcodeClickEvent } from '@/app/common/utils/googleAnalytics.unility';
import StreetcodeFilterRequestDTO, { StreetcodeFilterResultDTO } from '@/models/filters/streetcode-filter.model';

Expand All @@ -32,6 +34,7 @@ const HeaderBlock = () => {
const [searchQuery, setSearchQuery] = useState<string>('');
const [isPopoverVisible, setIsPopoverVisible] = useState<boolean>(false);
const inputRef = useRef(null);
const [inputValue, setInputValue] = useState('');
const searchBlockRef = useRef(null);
const { modalStore: { setModal, setIsPageDimmed, isPageDimmed } } = useModalContext();
const dimWrapperRef = useRef(null);
Expand All @@ -50,6 +53,8 @@ const HeaderBlock = () => {
};

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);

const { value } = e.target;
setSearchQuery(value);
if (value.length > 0 && isDesktop) {
Expand Down Expand Up @@ -108,13 +113,18 @@ const HeaderBlock = () => {
}
}, [searchQuery]);

const handleOnSearchResultItemClick = () => {
closeSearchBlock();
setInputValue('');
};

const popoverProps: PopoverProps = {
trigger: 'click',
open: isPopoverVisible && !isLoading,
getPopupContainer: (trigger: HTMLElement) => trigger.parentNode as HTMLElement,
content: (
<div ref={searchBlockRef}>
<SearchBlock searchResult={searchResult} />
<SearchBlock searchResult={searchResult} onSearchResultItemClick={handleOnSearchResultItemClick} />
</div>
),
afterOpenChange: handlePopoverVisibleChange,
Expand All @@ -131,10 +141,14 @@ const HeaderBlock = () => {
<div className="HeaderBlock" ref={dimWrapperRef}>
<div className={`navBarContainer ${isHeaderHidden ? 'hiddenNavBar' : ''} ${isPageDimmed ? 'dim' : ''}`}>
<div className="leftPartContainer">
<div className="logoContainer" onClick={() => window.location.href = '/'}>
{isDesktop
? <StreetcodeSvg />
: <StreetcodeSvgMobile />}
<div className="logoContainer">
<Link to={FRONTEND_ROUTES.BASE}>
{isDesktop ? (
<StreetcodeSvg />
) : (
<StreetcodeSvgMobile />
)}
</Link>
</div>
{isDesktop && isHeaderHidden && (
<Popover
Expand All @@ -144,6 +158,7 @@ const HeaderBlock = () => {
>
<input
onChange={handleInputChange}
value={inputValue}
placeholder="Пошук..."
ref={inputRef}
className={`ant-input
Expand All @@ -164,6 +179,7 @@ const HeaderBlock = () => {
onChange={handleInputChange}
placeholder="Пошук..."
onClick={onInputClick}
value={inputValue}
prefix={(
<MagnifyingGlass
viewBox="0 -2 24 24"
Expand Down Expand Up @@ -221,6 +237,7 @@ const HeaderBlock = () => {
<div ref={inputRef} className={`searchContainerMobile ${(isInputActive ? 'active' : '')}`}>
<input
onChange={handleInputChange}
value={inputValue}
className="ant-input css-dev-only-do-not-override-26rdvq"
placeholder="Що ти шукаєш?"
/>
Expand All @@ -232,7 +249,7 @@ const HeaderBlock = () => {
search();
}}
>
Пошук
Пошук
</Button>
</div>
</Popover>
Expand Down
5 changes: 3 additions & 2 deletions src/app/layout/header/SearchBlock/SearchBlock.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import SearchResultItem from './SearchResultItem/SearchItem.component';

interface Props {
searchResult: StreetcodeFilterResultDTO[];
onSearchResultItemClick?: React.MouseEventHandler<HTMLAnchorElement>;
}
const SearchBlock = ({ searchResult } : Props) => {
const SearchBlock = ({ searchResult, onSearchResultItemClick } : Props) => {
const blockHeight = searchResult.length > 9 ? '418px' : '100%';

return (
<div className="searchResultsBlock" style={{ height: blockHeight }}>
{searchResult.length === 0 ? <p>Результатів немає</p>
: (searchResult.map((searchResultItem: StreetcodeFilterResultDTO, index) => (
<SearchResultItem key={index} searchResultItem={searchResultItem} />
<SearchResultItem key={index} searchResultItem={searchResultItem} onClick={onSearchResultItemClick} />
)))}
</div>
);
Expand Down
Loading
Loading