Browse Source

fix : request for next dua parts sends after scroling to buttom

fix : audio plays even if it is not synced
master
sina_sajjadi 2 weeks ago
parent
commit
68d3f13eff
  1. 9
      src/components/context/audio-conext.tsx
  2. 1
      src/components/language-switcher.tsx
  3. 1
      src/components/layout/sidebar.tsx
  4. 1
      src/components/modals/modal-manager.tsx
  5. 1
      src/components/modals/modal.tsx
  6. 1
      src/components/sidebar/tabs.tsx
  7. 1
      src/components/ui/search-duas.tsx
  8. 5
      src/components/utils/colorize-vowels.tsx
  9. 4
      src/pages/_app.tsx
  10. 123
      src/pages/duas/[slug].tsx
  11. 7
      tailwind.config.ts

9
src/components/context/audio-conext.tsx

@ -48,7 +48,7 @@ export const AudioProvider: React.FC<{ children: ReactNode }> = ({
children, children,
}) => { }) => {
const audioRef = useRef<HTMLAudioElement | null>(null); const audioRef = useRef<HTMLAudioElement | null>(null);
const partRefs = useRef<(HTMLDivElement | null)[]>([]);
let partRefs = useRef<(HTMLDivElement | null)[]>([]);
const [audios, setAudios] = useState<Audio | null>({}); const [audios, setAudios] = useState<Audio | null>({});
const [audio, setAudio] = useState<Audio | null>({}); const [audio, setAudio] = useState<Audio | null>({});
@ -62,12 +62,6 @@ export const AudioProvider: React.FC<{ children: ReactNode }> = ({
`web/mafatih/${id}/audios/` `web/mafatih/${id}/audios/`
); );
console.log(
"Audio Response:",
audioResponse.data.results,
"selectedReciter:",
selectedReciter
);
setAudios(audioResponse.data.results); setAudios(audioResponse.data.results);
let selectedAudio: Audio | undefined; let selectedAudio: Audio | undefined;
@ -123,7 +117,6 @@ export const AudioProvider: React.FC<{ children: ReactNode }> = ({
setSelectedReciter, setSelectedReciter,
lastPart , setLastPart lastPart , setLastPart
}; };
console.log(audios);
return ( return (
<AudioContext.Provider value={value}>{children}</AudioContext.Provider> <AudioContext.Provider value={value}>{children}</AudioContext.Provider>

1
src/components/language-switcher.tsx

@ -161,7 +161,6 @@ const LanguageSwitcher: React.FC = () => {
document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("mousedown", handleClickOutside);
}; };
}, []); }, []);
console.log(languages);
return ( return (
<div className="relative inline-block font-sans" ref={wrapperRef}> <div className="relative inline-block font-sans" ref={wrapperRef}>

1
src/components/layout/sidebar.tsx

@ -6,7 +6,6 @@ function SideBar() {
const router = useRouter(); const router = useRouter();
const params = useParams(); const params = useParams();
const slug = params?.slug as string; const slug = params?.slug as string;
console.log(router);
if (router.pathname.includes("/about") || router.pathname.includes("/last-read")) { if (router.pathname.includes("/about") || router.pathname.includes("/last-read")) {
return null; return null;

1
src/components/modals/modal-manager.tsx

@ -16,7 +16,6 @@ const SearchModal = dynamic(() => import("@/components/modals/search-modal"));
const ManagedModal: React.FC = () => { const ManagedModal: React.FC = () => {
const { displayModal, closeModal, modalView } = useUI(); const { displayModal, closeModal, modalView } = useUI();
console.log(modalView);
return ( return (
<Modal rootClassName="bottom" variant="bottom" open={displayModal} onClose={closeModal}> <Modal rootClassName="bottom" variant="bottom" open={displayModal} onClose={closeModal}>

1
src/components/modals/modal.tsx

@ -64,7 +64,6 @@ export default function Modal({
clearAllBodyScrollLocks(); clearAllBodyScrollLocks();
}; };
}, [open]); }, [open]);
console.log(children);

1
src/components/sidebar/tabs.tsx

@ -31,7 +31,6 @@ const Tabs = () => {
setPath((prev) => { setPath((prev) => {
const newPath = [...prev]; // Create a shallow copy of the array const newPath = [...prev]; // Create a shallow copy of the array
newPath.splice(index + 1); // Remove elements starting from index + 1 newPath.splice(index + 1); // Remove elements starting from index + 1
console.log(newPath);
return newPath; // Return the updated array return newPath; // Return the updated array
}); });

1
src/components/ui/search-duas.tsx

@ -53,7 +53,6 @@ const SearchDuas: React.FC = () => {
setResults(res.data.results); setResults(res.data.results);
setIsLoading(false); setIsLoading(false);
// Optionally, you can handle the response here // Optionally, you can handle the response here
// console.log(res);
}) })
.catch((err) => { .catch((err) => {
console.error("Error fetching search results:", err); console.error("Error fetching search results:", err);

5
src/components/utils/colorize-vowels.tsx

@ -1,6 +1,5 @@
const colorizeVowels = (text: string) => { const colorizeVowels = (text: string) => {
// Normalize text to decomposed form (NFD) // Normalize text to decomposed form (NFD)
const normalizedText = text.normalize("NFD");
// Function to remove vowels/diacritics // Function to remove vowels/diacritics
const removeDiacritics = (str: string) => const removeDiacritics = (str: string) =>
@ -10,11 +9,11 @@ const colorizeVowels = (text: string) => {
<div className="relative text-rendering-optimize-speed"> <div className="relative text-rendering-optimize-speed">
{/* Bottom layer: Full text with vowels in orange */} {/* Bottom layer: Full text with vowels in orange */}
<div className="absolute top-0 right-0 text-[#EB6E57] font-[UthmanTaha]"> <div className="absolute top-0 right-0 text-[#EB6E57] font-[UthmanTaha]">
{normalizedText}
{text}
</div> </div>
{/* Top layer: Text without vowels in black */} {/* Top layer: Text without vowels in black */}
<div className="relative text-black font-[UthmanTaha]"> <div className="relative text-black font-[UthmanTaha]">
{removeDiacritics(normalizedText)}
{removeDiacritics(text)}
</div> </div>
</div> </div>
); );

4
src/pages/_app.tsx

@ -17,7 +17,6 @@ const rtlLanguages = ["ar", "ur", "fa", "ks", "tg", "bn"];
function App({ Component, pageProps }: AppProps) { function App({ Component, pageProps }: AppProps) {
const isRtl = rtlLanguages.includes(pageProps?._nextI18Next?.initialLocale) || false; const isRtl = rtlLanguages.includes(pageProps?._nextI18Next?.initialLocale) || false;
console.log(isRtl);
return ( return (
<FontSettingsProvider> <FontSettingsProvider>
@ -29,7 +28,8 @@ console.log(isRtl);
<div className="m-auto lg:p-6"> <div className="m-auto lg:p-6">
<div className="flex m-auto max-w-[1440px] flex-col lg:flex-row lg:gap-6 relative"> <div className="flex m-auto max-w-[1440px] flex-col lg:flex-row lg:gap-6 relative">
<SideBar /> <SideBar />
<main className={`w-full`}>
<main
className={`w-full`}>
<Component {...pageProps} /> <Component {...pageProps} />
</main> </main>
</div> </div>

123
src/pages/duas/[slug].tsx

@ -1,4 +1,3 @@
// components/DuaComponent.tsx
import React, { useEffect, useRef, useState, useCallback } from "react"; import React, { useEffect, useRef, useState, useCallback } from "react";
import Image from "next/image"; import Image from "next/image";
import SettingIcon from "../../../public/assets/images/WhiteSetting.svg"; import SettingIcon from "../../../public/assets/images/WhiteSetting.svg";
@ -16,8 +15,6 @@ import { useFontSettingsContext } from "@/components/context/font-setting-contex
import { useAudio } from "@/components/context/audio-conext"; import { useAudio } from "@/components/context/audio-conext";
import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { serverSideTranslations } from "next-i18next/serverSideTranslations";
// Define the Dua interface // Define the Dua interface
interface Dua { interface Dua {
id: number; id: number;
@ -30,14 +27,14 @@ interface Dua {
// Define the structure for audio synchronization data // Define the structure for audio synchronization data
interface AudioSyncData { interface AudioSyncData {
id: number; id: number;
duration: [number, number][];
duration: [number, number][]; // Start and end time in seconds
} }
// Define the Audio interface
// Define the API response structure
// Define the API response structure for Dua parts
interface DuaPartsResponse { interface DuaPartsResponse {
results: Dua[]; results: Dua[];
next: string;
count: number;
} }
const DuaComponent: React.FC<DuaComponentProps> = ({ const DuaComponent: React.FC<DuaComponentProps> = ({
@ -46,53 +43,66 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
}) => { }) => {
const params = useParams(); const params = useParams();
const slug = (params?.slug as string) ?? SelectedDua; const slug = (params?.slug as string) ?? SelectedDua;
console.log(SelectedDua);
const router = useRouter(); const router = useRouter();
const { openSetting, openAudio } = useUI(); const { openSetting, openAudio } = useUI();
const { audioRef, partRefs, getAudio, audio, setLastPart } = useAudio(); const { audioRef, partRefs, getAudio, audio, setLastPart } = useAudio();
console.log("Audio Ref:", audioRef.current);
const [scrollPosition, setScrollPosition] = useState(0);
const [fetching, setFetching] = useState(false); // Track if data is being fetched
// Use the shared context for font settings // Use the shared context for font settings
const { fontSettings } = useFontSettingsContext(); const { fontSettings } = useFontSettingsContext();
// State hooks // State hooks
const [duaParts, setDuaParts] = useState<Dua[]>([]); const [duaParts, setDuaParts] = useState<Dua[]>([]);
const [recitingPart, setRecitingPart] = useState<Dua | null>(null); const [recitingPart, setRecitingPart] = useState<Dua | null>(null);
const [loading, setLoading] = useState(false);
// Audio reference
// Fetch Dua parts and audio
// Use useCallback to memoize fetchData so it doesn’t change between renders
const fetchData = useCallback(async (nextPage) => {
if (!slug || fetching) return; // Prevent fetching if data is already being fetched
// Refs to track each part
const id = slug.split("-").pop();
if (!id) return;
// Fetch Dua parts and audio
useEffect(() => {
if (!slug) return;
const fetchData = async () => {
const id = slug.split("-").pop();
if (!id) return;
try {
// Fetching dua parts
const duaResponse = await http.get<DuaPartsResponse>(
`web/mafatih-duas/${id}/parts/`
);
setDuaParts(duaResponse.data.results);
} catch (error) {
console.error("Error fetching Dua parts:", error);
// Optionally, set an error state here
}
setFetching(true); // Set fetching to true when data starts fetching
getAudio(id);
};
// Reset the duaParts state when the slug changes
const offset = nextPage ? duaParts.length : 0;
try {
setLoading(true);
// Fetch the data
const duaResponse = await http.get<DuaPartsResponse>(
`web/mafatih-duas/${id}/parts/?offset=${offset}`
);
// Append the new results to the existing duaParts
setDuaParts((prev) => [...prev, ...duaResponse.data.results]);
} catch (error) {
console.error("Error fetching Dua parts:", error);
} finally {
setLoading(false);
setFetching(false); // Reset fetching state after fetching is done
}
getAudio(id);
}, [slug, fetching, duaParts, getAudio]); // Dependencies for fetchData
// Use the memoized fetchData in the effect hook
useEffect(() => {
setDuaParts([]); // Reset Dua parts when slug changes
fetchData(false);
}, [slug]); // Only slug changes should trigger this effect
fetchData();
}, [slug]);
// Play audio from a specific part // Play audio from a specific part
const playAudio = useCallback( const playAudio = useCallback(
(part: Dua) => { (part: Dua) => {
if (!Object.keys(audio).length) return; if (!Object.keys(audio).length) return;
console.log(part);
const selectedAudio = audio.audio_sync_data.find( const selectedAudio = audio.audio_sync_data.find(
(item) => item.id === part.id (item) => item.id === part.id
@ -105,13 +115,15 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
audioRef.current.play().catch((error) => { audioRef.current.play().catch((error) => {
console.error("Error playing audio:", error); console.error("Error playing audio:", error);
}); });
return;
} }
openAudio();
audioRef.current.play().catch((error) => {
console.error("Error playing audio:", error);
});
}, },
[audio] [audio]
); );
console.log(audio);
console.log(fontSettings.arabicRange);
// Handle audio end to scroll to the next part // Handle audio end to scroll to the next part
const handleAudioEnd = useCallback(() => { const handleAudioEnd = useCallback(() => {
@ -145,6 +157,7 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
setRecitingPart(currentRecitingPart); setRecitingPart(currentRecitingPart);
}, [audio, duaParts]); }, [audio, duaParts]);
// Process the slug into a more readable format
function processSlug(slug: string): string { function processSlug(slug: string): string {
if (!slug) return ""; if (!slug) return "";
@ -205,14 +218,24 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
} }
}, [recitingPart, duaParts]); }, [recitingPart, duaParts]);
const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
const target = e.currentTarget;
const isBottom = target.scrollHeight - target.scrollTop === target.clientHeight;
if (isBottom && !fetching) { // Only fetch when not already fetching
fetchData(true);
}
setScrollPosition(target.scrollTop); // Update scroll position
}, [fetching]);
if (!slug) { if (!slug) {
return null; // Handling the case where slug is not available return null; // Handling the case where slug is not available
} }
console.log(audioRef.current);
return ( return (
<div <div
onScroll={handleScroll} // Add the onScroll event listener here
className={`rounded-3xl overflow-y-auto flex-grow h-[calc(100vh-130px)] lg:bg-[#F5F5F5] lg:p-6 lg:rounded-3xl ${ className={`rounded-3xl overflow-y-auto flex-grow h-[calc(100vh-130px)] lg:bg-[#F5F5F5] lg:p-6 lg:rounded-3xl ${
!slug && "hidden lg:flex" !slug && "hidden lg:flex"
}`} }`}
@ -255,9 +278,7 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
<div className="p-3 bg-white rounded-3xl"> <div className="p-3 bg-white rounded-3xl">
{item.text && ( {item.text && (
<div <div
className={`mb-4 text-right ${
!fontSettings.arabic && "hidden"
}`}
className={`mb-4 text-right ${!fontSettings.arabic && "hidden"}`}
style={{ style={{
fontSize: `${25 * (fontSettings.arabicRange / 100)}px`, fontSize: `${25 * (fontSettings.arabicRange / 100)}px`,
}} }}
@ -267,13 +288,9 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
)} )}
{item.local_alpha && ( {item.local_alpha && (
<p <p
className={`text-sm font-normal mb-4 ${
!fontSettings.transliteration && "hidden"
}`}
className={`text-sm font-normal mb-4 ${!fontSettings.transliteration && "hidden"}`}
style={{ style={{
fontSize: `${
14 * (fontSettings.transliterationRange / 100)
}px`,
fontSize: `${14 * (fontSettings.transliterationRange / 100)}px`,
}} }}
> >
{item.local_alpha} {item.local_alpha}
@ -281,9 +298,7 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
)} )}
{item.translation && ( {item.translation && (
<p <p
className={`text-sm font-normal border-t pt-4 ${
!fontSettings.translation && "hidden"
}`}
className={`text-sm font-normal border-t pt-4 ${!fontSettings.translation && "hidden"}`}
style={{ style={{
fontSize: `${14 * (fontSettings.translationRange / 100)}px`, fontSize: `${14 * (fontSettings.translationRange / 100)}px`,
}} }}
@ -297,7 +312,6 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
</p> </p>
)} )}
{/* Play button to start audio from specific time */}
{audio && !!Object.keys(audio)?.length && ( {audio && !!Object.keys(audio)?.length && (
<button <button
onClick={() => playAudio(item)} onClick={() => playAudio(item)}
@ -324,10 +338,11 @@ const DuaComponent: React.FC<DuaComponentProps> = ({
); );
}; };
// Fetching server-side translations for multi-language support
export async function getServerSideProps({ locale }: { locale: string }) { export async function getServerSideProps({ locale }: { locale: string }) {
return { return {
props: { props: {
...(await serverSideTranslations(locale, ['common'])), // Load translations for 'common' namespace
...(await serverSideTranslations(locale, ["common"])), // Load translations for 'common' namespace
}, },
}; };
} }

7
tailwind.config.ts

@ -26,10 +26,13 @@ export default {
addUtilities( addUtilities(
{ {
'.text-rendering-optimize-speed': { '.text-rendering-optimize-speed': {
'text-rendering': 'optimizeSpeed',
'text-rendering': 'geometricPrecision',
},
'.no-ligatures': {
'font-variant-ligatures': 'none', // Add this utility
}, },
}, },
['responsive', 'hover'] // Make this available for responsive and hover variants
['responsive', 'hover'] // Ensure this utility is available for responsive and hover variants
); );
}, },
], ],

Loading…
Cancel
Save