Desktop: Fixes #15278: Fix video/audio in Markdown viewer continuing to play after hiding it or switching notes (#16299)

This commit is contained in:
Laurent Cozic authored and GitHub committed 2026-08-24 14:20:15 +01:00
1 parent 2a83642dbf
commit fc76a884f3
4 files changed
+46

No files matched your search

@@ -710,6 +710,14 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [renderedBody, webviewReady]);
// Stop media playing in the viewer once it's hidden - https://github.com/laurent22/joplin/issues/15278
const viewerVisible = props.visiblePanes.includes('viewer');
useEffect(() => {
if (!viewerVisible) {
webviewRef.current?.stopMediaPlayback();
}
}, [viewerVisible]);
const { onSetInitialMarkersRef } = useEditorSearchHandler({
setLocalSearchResultCount: props.setLocalSearchResultCount,
searchMarkers: props.searchMarkers,
@@ -297,6 +297,14 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
useRefocusOnVisiblePaneChange({ editorRef, webviewRef, visiblePanes: props.visiblePanes });
// Stop media playing in the viewer once it's hidden - https://github.com/laurent22/joplin/issues/15278
const viewerVisible = props.visiblePanes.includes('viewer');
useEffect(() => {
if (!viewerVisible) {
webviewRef.current?.stopMediaPlayback();
}
}, [viewerVisible]);
useEditorSearchHandler({
setLocalSearchResultCount: props.setLocalSearchResultCount,
searchMarkers: props.searchMarkers,
@@ -35,6 +35,7 @@ export interface NoteViewerControl {
focusLine(editorLine: number): void;
focus(): void;
hasFocus(): boolean;
stopMediaPlayback(): void;
}
const usePluginMessageResponder = (webviewRef: RefObject<HTMLIFrameElement>) => {
@@ -143,6 +144,10 @@ const NoteTextViewer = forwardRef((props: Props, ref: ForwardedRef<NoteViewerCon
if (channel === 'setMarkers') {
win.postMessage({ target: 'webview', name: 'setMarkers', data: { keywords: arg0, options: arg1 } }, '*');
}
if (channel === 'stopMediaPlayback') {
win.postMessage({ target: 'webview', name: 'stopMediaPlayback', data: {} }, '*');
}
},
focus: () => {
if (webviewRef.current) {
@@ -168,6 +173,9 @@ const NoteTextViewer = forwardRef((props: Props, ref: ForwardedRef<NoteViewerCon
}, 100);
}
},
stopMediaPlayback: () => {
result.send('stopMediaPlayback');
},
};
return result;
}, [parentDoc]);
@@ -430,6 +430,26 @@
}
};
// https://github.com/laurent22/joplin/issues/15278
// Embedded players (e.g. YouTube) are cross-origin iframes that can't be
// paused from here, so reassigning the src reloads them, which stops playback.
function stopMediaPlayback() {
for (const element of contentElement.querySelectorAll('video, audio')) {
if (!element.paused) element.pause();
}
// Exclude .media-pdf: it's the interactive PDF viewer, not a media
// player, and reloading it would reset the current page and scroll.
for (const iframe of contentElement.querySelectorAll('iframe:not(.media-pdf)')) {
const src = iframe.getAttribute('src');
if (src) iframe.setAttribute('src', src);
}
}
ipc.stopMediaPlayback = () => {
stopMediaPlayback();
};
ipc.setHtml = (event) => {
const html = event.html;
@@ -439,6 +459,8 @@
alreadyAllImagesLoaded = false;
stopMediaPlayback();
contentElement.innerHTML = html;
if (html.includes('file://')) {