mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	feat(client/print): print presentations with waiting for slides to load
This commit is contained in:
		| @@ -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); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user