Skip to content
This repository was archived by the owner on Jan 30, 2026. It is now read-only.

Commit eeaf3fd

Browse files
fix: stop media when open post (#1796)
* fix: stop media when open post * fix eslint * fix: use js api to pause media instead of seting src which broke back navigation due to adding duplicates ot history. SPOTIFY STILL NOT WORKING * fix spotify embed * fix * fix eslint --------- Co-authored-by: James Browning <jamesbrowning91@gmail.com>
1 parent f180396 commit eeaf3fd

File tree

7 files changed

+312
-20
lines changed

7 files changed

+312
-20
lines changed

package-lock.json

Lines changed: 0 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
"react-dom": "18.3.1",
4545
"react-easy-crop": "^5.1.0",
4646
"react-quilljs": "^2.0.5",
47-
"react-spotify-embed": "^2.0.4",
4847
"react-syntax-highlighter": "^15.6.1",
4948
"react-tweet": "^3.2.1",
5049
"sharp": "^0.33.5",

src/components/LinkPreview/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useEffect, useState } from 'react';
44
import getYouTubeID from 'get-youtube-id';
55
import { Tweet } from 'react-tweet';
66
import { Preview, Post as PostUI } from '@social/ui-shared';
7-
import { Spotify } from 'react-spotify-embed';
87
import { usePubkyClientContext } from '@/contexts';
98
import { PostView } from '@/types/Post';
109
import { getPost } from '@/services/postService';
@@ -149,7 +148,7 @@ export default function LinkPreviewer({ content, setQuote }: LinkPreviewerProps)
149148
<iframe
150149
width="100%"
151150
height="315"
152-
src={`https://www.youtube.com/embed/${videoId}`}
151+
src={`https://www.youtube.com/embed/${videoId}?enablejsapi=1`}
153152
title="YouTube video player"
154153
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
155154
allowFullScreen
@@ -167,7 +166,6 @@ export default function LinkPreviewer({ content, setQuote }: LinkPreviewerProps)
167166
</div>
168167
)}
169168
{githubUrl && <Preview.GitHub url={githubUrl} />}
170-
{spotifyUrl && <Spotify link={spotifyUrl} />}
171169
</>
172170
);
173171
}

src/components/Modal/_PostView/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Header } from './components/_Header';
1919
import RootParent from './components/_RootParent';
2020
import PostRoot from './components/_PostRoot';
2121
import { ImageArticle } from './components/_ImageArticle';
22+
import { useMediaPause } from '@/hooks/useMediaPause';
2223

2324
interface PostViewModalProps {
2425
showModal: boolean;
@@ -36,6 +37,9 @@ export default function PostViewModal({ showModal, setShowModal, post }: PostVie
3637
const originalTitle = useRef<string>('');
3738
const [postKey, setPostKey] = useState<string>('');
3839

40+
// Use the media pause hook to stop background media when modal opens
41+
useMediaPause(showModal);
42+
3943
// Reset replies when modal opens and update post key for proper re-rendering
4044
useEffect(() => {
4145
if (showModal && post) {

src/components/Post/_Content.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Tweet } from 'react-tweet';
88
import Parsing from '../Content/_Parsing';
99
import { Button, Icon, Skeleton, Typography } from '@social/ui-shared';
1010
import { FileView, PostView } from '@/types/Post';
11-
import { Spotify } from 'react-spotify-embed';
11+
import SpotifyEmbed from '@/components/SpotifyEmbed';
1212
import { useIsMobile } from '@/hooks/useIsMobile';
1313
import { useInlineUrls } from '@/hooks/useInlineUrls';
1414
import { useFileLoading } from '@/hooks/useFileLoading';
@@ -193,7 +193,7 @@ export default function Content({
193193
<iframe
194194
width="100%"
195195
height="315"
196-
src={`https://www.youtube.com/embed/${videoId}`}
196+
src={`https://www.youtube.com/embed/${videoId}?enablejsapi=1`}
197197
title="YouTube video player"
198198
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
199199
allowFullScreen
@@ -220,7 +220,7 @@ export default function Content({
220220
)}
221221
{spotifyUrl && (
222222
<div onClick={(event) => event.stopPropagation()} className="mt-4">
223-
<Spotify link={spotifyUrl} />
223+
<SpotifyEmbed link={spotifyUrl} />
224224
</div>
225225
)}
226226
</div>
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
'use client';
2+
3+
import { useEffect, useRef, useState } from 'react';
4+
5+
interface SpotifyEmbedProps {
6+
link: string;
7+
width?: string;
8+
height?: string;
9+
}
10+
11+
// Global registry for Spotify controllers
12+
declare global {
13+
interface Window {
14+
spotifyControllers: Map<string, any>;
15+
spotifyIframeAPI: any;
16+
}
17+
}
18+
19+
// Initialize global registry if it doesn't exist
20+
if (typeof window !== 'undefined' && !window.spotifyControllers) {
21+
window.spotifyControllers = new Map();
22+
}
23+
24+
export default function SpotifyEmbed({ link, width = '100%', height = '160' }: SpotifyEmbedProps) {
25+
const containerRef = useRef<HTMLDivElement>(null);
26+
const [isLoaded, setIsLoaded] = useState(false);
27+
const embedId = useRef(`spotify-embed-${Math.random().toString(36).substr(2, 9)}`);
28+
const isComponentMounted = useRef(true);
29+
30+
// Convert Spotify URL to URI format for API
31+
const convertSpotifyUrlToUri = (url: string): string => {
32+
// Handle different Spotify URL formats
33+
const trackMatch = url.match(/open\.spotify\.com\/track\/([a-zA-Z0-9]+)/);
34+
const albumMatch = url.match(/open\.spotify\.com\/album\/([a-zA-Z0-9]+)/);
35+
const playlistMatch = url.match(/open\.spotify\.com\/playlist\/([a-zA-Z0-9]+)/);
36+
const episodeMatch = url.match(/open\.spotify\.com\/episode\/([a-zA-Z0-9]+)/);
37+
const showMatch = url.match(/open\.spotify\.com\/show\/([a-zA-Z0-9]+)/);
38+
39+
if (trackMatch) {
40+
return `spotify:track:${trackMatch[1]}`;
41+
} else if (albumMatch) {
42+
return `spotify:album:${albumMatch[1]}`;
43+
} else if (playlistMatch) {
44+
return `spotify:playlist:${playlistMatch[1]}`;
45+
} else if (episodeMatch) {
46+
return `spotify:episode:${episodeMatch[1]}`;
47+
} else if (showMatch) {
48+
return `spotify:show:${showMatch[1]}`;
49+
}
50+
51+
// If no match found, return the original URL
52+
return url;
53+
};
54+
55+
// Convert Spotify URL to embed URL format for iframe
56+
const convertSpotifyUrlToEmbedUrl = (url: string): string => {
57+
// Handle different Spotify URL formats
58+
const trackMatch = url.match(/open\.spotify\.com\/track\/([a-zA-Z0-9]+)/);
59+
const albumMatch = url.match(/open\.spotify\.com\/album\/([a-zA-Z0-9]+)/);
60+
const playlistMatch = url.match(/open\.spotify\.com\/playlist\/([a-zA-Z0-9]+)/);
61+
const episodeMatch = url.match(/open\.spotify\.com\/episode\/([a-zA-Z0-9]+)/);
62+
const showMatch = url.match(/open\.spotify\.com\/show\/([a-zA-Z0-9]+)/);
63+
64+
if (trackMatch) {
65+
return `https://open.spotify.com/embed/track/${trackMatch[1]}`;
66+
} else if (albumMatch) {
67+
return `https://open.spotify.com/embed/album/${albumMatch[1]}`;
68+
} else if (playlistMatch) {
69+
return `https://open.spotify.com/embed/playlist/${playlistMatch[1]}`;
70+
} else if (episodeMatch) {
71+
return `https://open.spotify.com/embed/episode/${episodeMatch[1]}`;
72+
} else if (showMatch) {
73+
return `https://open.spotify.com/embed/show/${showMatch[1]}`;
74+
}
75+
76+
// If no match found, return the original URL
77+
return url;
78+
};
79+
80+
const embedUrl = convertSpotifyUrlToEmbedUrl(link);
81+
const spotifyUri = convertSpotifyUrlToUri(link);
82+
83+
useEffect(() => {
84+
let script: HTMLScriptElement | null = null;
85+
86+
const loadSpotifyScript = () => {
87+
// Check if script is already loaded
88+
if (document.querySelector('script[src="https://open.spotify.com/embed/iframe-api/v1"]')) {
89+
// If script exists, check if API is available
90+
if (window.spotifyIframeAPI && isComponentMounted.current) {
91+
createController();
92+
} else if (isComponentMounted.current) {
93+
// Wait for API to be ready
94+
const checkAPI = setInterval(() => {
95+
if (window.spotifyIframeAPI && isComponentMounted.current) {
96+
clearInterval(checkAPI);
97+
createController();
98+
} else if (!isComponentMounted.current) {
99+
clearInterval(checkAPI);
100+
}
101+
}, 100);
102+
103+
setTimeout(() => {
104+
clearInterval(checkAPI);
105+
}, 10000);
106+
}
107+
return;
108+
}
109+
110+
// Create and load the script
111+
script = document.createElement('script');
112+
script.src = 'https://open.spotify.com/embed/iframe-api/v1';
113+
script.async = true;
114+
document.body.appendChild(script);
115+
};
116+
117+
const createController = () => {
118+
if (!containerRef.current || !window.spotifyIframeAPI || !isComponentMounted.current) return;
119+
120+
const options = {
121+
uri: spotifyUri,
122+
width: width,
123+
height: height
124+
};
125+
126+
const callback = (spotifyEmbedController: any) => {
127+
if (isComponentMounted.current) {
128+
setIsLoaded(true);
129+
// Register the controller globally for use in useMediaPause
130+
window.spotifyControllers.set(embedId.current, spotifyEmbedController);
131+
}
132+
};
133+
134+
try {
135+
window.spotifyIframeAPI.createController(containerRef.current, options, callback);
136+
} catch (error) {
137+
console.error('Error creating Spotify embed controller:', error);
138+
}
139+
};
140+
141+
// Set up the global callback if not already set
142+
if (!(window as any).onSpotifyIframeApiReady) {
143+
(window as any).onSpotifyIframeApiReady = (SpotifyIframeApi: any) => {
144+
window.spotifyIframeAPI = SpotifyIframeApi;
145+
if (isComponentMounted.current) {
146+
createController();
147+
}
148+
};
149+
} else if (isComponentMounted.current) {
150+
// If callback already exists, try to create controller
151+
createController();
152+
}
153+
154+
// Start the process
155+
loadSpotifyScript();
156+
157+
return () => {
158+
isComponentMounted.current = false;
159+
160+
// Remove from global registry
161+
try {
162+
window.spotifyControllers.delete(embedId.current);
163+
} catch (error) {
164+
// Ignore errors during cleanup
165+
}
166+
};
167+
}, [spotifyUri, width, height]);
168+
169+
return (
170+
<div
171+
ref={containerRef}
172+
id={embedId.current}
173+
className="spotify-embed-container"
174+
style={{
175+
width,
176+
height,
177+
borderRadius: '12px',
178+
overflow: 'hidden'
179+
}}
180+
>
181+
<iframe
182+
src={embedUrl}
183+
width={width}
184+
height={height}
185+
frameBorder="0"
186+
allow="encrypted-media"
187+
style={{
188+
borderRadius: '12px',
189+
border: 'none'
190+
}}
191+
onLoad={() => {
192+
if (isComponentMounted.current) {
193+
setIsLoaded(true);
194+
}
195+
}}
196+
/>
197+
</div>
198+
);
199+
}

0 commit comments

Comments
 (0)