mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 21:19:01 +01:00 
			
		
		
		
	feat(client/print): print presentations with waiting for slides to load
This commit is contained in:
		
							parent
							
								
									89dac52f49
								
							
						
					
					
						commit
						64576458b7
					
				@ -2,6 +2,7 @@ import FNote from "./entities/fnote";
 | 
				
			|||||||
import { render } from "preact";
 | 
					import { render } from "preact";
 | 
				
			||||||
import { CustomNoteList } from "./widgets/collections/NoteList";
 | 
					import { CustomNoteList } from "./widgets/collections/NoteList";
 | 
				
			||||||
import "./print.css";
 | 
					import "./print.css";
 | 
				
			||||||
 | 
					import { useCallback, useRef } from "preact/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function main() {
 | 
					async function main() {
 | 
				
			||||||
    const notePath = window.location.hash.substring(1);
 | 
					    const notePath = window.location.hash.substring(1);
 | 
				
			||||||
@ -12,10 +13,25 @@ async function main() {
 | 
				
			|||||||
    const note = await froca.getNote(noteId);
 | 
					    const note = await froca.getNote(noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!note) return;
 | 
					    if (!note) return;
 | 
				
			||||||
    render(getElementForNote(note), document.body);
 | 
					    render(<App note={note} />, document.body);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getElementForNote(note: FNote) {
 | 
					function App({ note }: { note: FNote }) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					            <ContentRenderer note={note} />
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ContentRenderer({ note }: { note: FNote }) {
 | 
				
			||||||
 | 
					    const sentReadyEvent = useRef(false);
 | 
				
			||||||
 | 
					    const onReady = useCallback(() => {
 | 
				
			||||||
 | 
					        if (sentReadyEvent.current) return;
 | 
				
			||||||
 | 
					        window.dispatchEvent(new Event("note-ready"));
 | 
				
			||||||
 | 
					        sentReadyEvent.current = true;
 | 
				
			||||||
 | 
					    }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Collections.
 | 
					    // Collections.
 | 
				
			||||||
    if (note.type === "book") {
 | 
					    if (note.type === "book") {
 | 
				
			||||||
        return <CustomNoteList
 | 
					        return <CustomNoteList
 | 
				
			||||||
@ -25,6 +41,7 @@ function getElementForNote(note: FNote) {
 | 
				
			|||||||
            ntxId="print"
 | 
					            ntxId="print"
 | 
				
			||||||
            highlightedTokens={null}
 | 
					            highlightedTokens={null}
 | 
				
			||||||
            media="print"
 | 
					            media="print"
 | 
				
			||||||
 | 
					            onReady={onReady}
 | 
				
			||||||
        />;
 | 
					        />;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2423,3 +2423,13 @@ footer.webview-footer button {
 | 
				
			|||||||
    background: rgba(255, 100, 100, 0.5);
 | 
					    background: rgba(255, 100, 100, 0.5);
 | 
				
			||||||
    text-decoration: line-through;
 | 
					    text-decoration: line-through;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					iframe.print-iframe {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: -600px;
 | 
				
			||||||
 | 
					    right: -600px;
 | 
				
			||||||
 | 
					    bottom: 0;
 | 
				
			||||||
 | 
					    width: 0;
 | 
				
			||||||
 | 
					    height: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -23,9 +23,10 @@ interface NoteListProps {
 | 
				
			|||||||
    isEnabled: boolean;
 | 
					    isEnabled: boolean;
 | 
				
			||||||
    ntxId: string | null | undefined;
 | 
					    ntxId: string | null | undefined;
 | 
				
			||||||
    media: ViewModeMedia;
 | 
					    media: ViewModeMedia;
 | 
				
			||||||
 | 
					    onReady: () => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function NoteList<T extends object>(props: Pick<NoteListProps, "displayOnlyCollections" | "media">) {
 | 
					export default function NoteList<T extends object>(props: Pick<NoteListProps, "displayOnlyCollections" | "media" | "onReady">) {
 | 
				
			||||||
    const { note, noteContext, notePath, ntxId } = useNoteContext();
 | 
					    const { note, noteContext, notePath, ntxId } = useNoteContext();
 | 
				
			||||||
    const isEnabled = noteContext?.hasNoteList();
 | 
					    const isEnabled = noteContext?.hasNoteList();
 | 
				
			||||||
    return <CustomNoteList note={note} isEnabled={!!isEnabled} notePath={notePath} ntxId={ntxId} {...props} />
 | 
					    return <CustomNoteList note={note} isEnabled={!!isEnabled} notePath={notePath} ntxId={ntxId} {...props} />
 | 
				
			||||||
 | 
				
			|||||||
@ -16,4 +16,5 @@ export interface ViewModeProps<T extends object> {
 | 
				
			|||||||
    viewConfig: T | undefined;
 | 
					    viewConfig: T | undefined;
 | 
				
			||||||
    saveConfig(newConfig: T): void;
 | 
					    saveConfig(newConfig: T): void;
 | 
				
			||||||
    media: ViewModeMedia;
 | 
					    media: ViewModeMedia;
 | 
				
			||||||
 | 
					    onReady(): void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ import { t } from "../../../services/i18n";
 | 
				
			|||||||
import { DEFAULT_THEME, loadPresentationTheme } from "./themes";
 | 
					import { DEFAULT_THEME, loadPresentationTheme } from "./themes";
 | 
				
			||||||
import FNote from "../../../entities/fnote";
 | 
					import FNote from "../../../entities/fnote";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function PresentationView({ note, noteIds, media }: ViewModeProps<{}>) {
 | 
					export default function PresentationView({ note, noteIds, media, onReady }: ViewModeProps<{}>) {
 | 
				
			||||||
    const [ presentation, setPresentation ] = useState<PresentationModel>();
 | 
					    const [ presentation, setPresentation ] = useState<PresentationModel>();
 | 
				
			||||||
    const containerRef = useRef<HTMLDivElement>(null);
 | 
					    const containerRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
    const [ api, setApi ] = useState<Reveal.Api>();
 | 
					    const [ api, setApi ] = useState<Reveal.Api>();
 | 
				
			||||||
@ -33,6 +33,14 @@ export default function PresentationView({ note, noteIds, media }: ViewModeProps
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    useLayoutEffect(refresh, [ note, noteIds ]);
 | 
					    useLayoutEffect(refresh, [ note, noteIds ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        // We need to wait for Reveal.js to initialize (by setting api) and for the presentation to become available.
 | 
				
			||||||
 | 
					        if (api && presentation) {
 | 
				
			||||||
 | 
					            // Timeout is necessary because it otherwise can cause flakiness by rendering only the first slide.
 | 
				
			||||||
 | 
					            setTimeout(onReady, 200);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, [ api, presentation ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!presentation || !stylesheets) return;
 | 
					    if (!presentation || !stylesheets) return;
 | 
				
			||||||
    const content = (
 | 
					    const content = (
 | 
				
			||||||
        <>
 | 
					        <>
 | 
				
			||||||
 | 
				
			|||||||
@ -297,8 +297,19 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Trigger in timeout to dismiss the menu while printing.
 | 
					        const iframe = document.createElement('iframe');
 | 
				
			||||||
        setTimeout(window.print, 0);
 | 
					        iframe.src = `?print#${this.notePath}`;
 | 
				
			||||||
 | 
					        iframe.className = "print-iframe";
 | 
				
			||||||
 | 
					        document.body.appendChild(iframe);
 | 
				
			||||||
 | 
					        iframe.onload = () => {
 | 
				
			||||||
 | 
					            console.log("Got ", iframe, iframe.contentWindow);
 | 
				
			||||||
 | 
					            if (iframe.contentWindow) {
 | 
				
			||||||
 | 
					                iframe.contentWindow.addEventListener("note-ready", () => {
 | 
				
			||||||
 | 
					                    iframe.contentWindow?.print();
 | 
				
			||||||
 | 
					                    document.body.removeChild(iframe);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async exportAsPdfEvent() {
 | 
					    async exportAsPdfEvent() {
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
 | 
				
			|||||||
  const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
 | 
					  const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
 | 
				
			||||||
  const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
 | 
					  const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
 | 
				
			||||||
  const isInOptions = note.noteId.startsWith("_options");
 | 
					  const isInOptions = note.noteId.startsWith("_options");
 | 
				
			||||||
  const isPrintable = ["text", "code"].includes(note.type);
 | 
					  const isPrintable = ["text", "code", "book"].includes(note.type);
 | 
				
			||||||
  const isElectron = getIsElectron();
 | 
					  const isElectron = getIsElectron();
 | 
				
			||||||
  const isMac = getIsMac();
 | 
					  const isMac = getIsMac();
 | 
				
			||||||
  const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap"].includes(note.type);
 | 
					  const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap"].includes(note.type);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user