mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 19:19:03 +01:00 
			
		
		
		
	chore(client/ts): port services/link
This commit is contained in:
		
							parent
							
								
									3df585c72a
								
							
						
					
					
						commit
						a759c1fbd2
					
				| @ -4,19 +4,19 @@ import appContext from "../components/app_context.js"; | |||||||
| import froca from "./froca.js"; | import froca from "./froca.js"; | ||||||
| import utils from "./utils.js"; | import utils from "./utils.js"; | ||||||
| 
 | 
 | ||||||
| function getNotePathFromUrl(url) { | function getNotePathFromUrl(url: string) { | ||||||
|     const notePathMatch = /#(root[A-Za-z0-9_/]*)$/.exec(url); |     const notePathMatch = /#(root[A-Za-z0-9_/]*)$/.exec(url); | ||||||
| 
 | 
 | ||||||
|     return notePathMatch === null ? null : notePathMatch[1]; |     return notePathMatch === null ? null : notePathMatch[1]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function getLinkIcon(noteId, viewMode) { | async function getLinkIcon(noteId: string, viewMode: ViewMode | undefined) { | ||||||
|     let icon; |     let icon; | ||||||
| 
 | 
 | ||||||
|     if (viewMode === 'default') { |     if (!viewMode || viewMode === 'default') { | ||||||
|         const note = await froca.getNote(noteId); |         const note = await froca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|         icon = note.getIcon(); |         icon = note?.getIcon(); | ||||||
|     } else if (viewMode === 'source') { |     } else if (viewMode === 'source') { | ||||||
|         icon = 'bx bx-code-curly'; |         icon = 'bx bx-code-curly'; | ||||||
|     } else if (viewMode === 'attachments') { |     } else if (viewMode === 'attachments') { | ||||||
| @ -25,7 +25,24 @@ async function getLinkIcon(noteId, viewMode) { | |||||||
|     return icon; |     return icon; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function createLink(notePath, options = {}) { | type ViewMode = "default" | "source" | "attachments" | string; | ||||||
|  | 
 | ||||||
|  | interface ViewScope { | ||||||
|  |     viewMode?: ViewMode; | ||||||
|  |     attachmentId?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface CreateLinkOptions { | ||||||
|  |     title?: string; | ||||||
|  |     showTooltip?: boolean; | ||||||
|  |     showNotePath?: boolean; | ||||||
|  |     showNoteIcon?: boolean; | ||||||
|  |     referenceLink?: boolean; | ||||||
|  |     autoConvertToImage?: boolean; | ||||||
|  |     viewScope?: ViewScope; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function createLink(notePath: string, options: CreateLinkOptions = {}) { | ||||||
|     if (!notePath || !notePath.trim()) { |     if (!notePath || !notePath.trim()) { | ||||||
|         logError("Missing note path"); |         logError("Missing note path"); | ||||||
| 
 | 
 | ||||||
| @ -45,6 +62,12 @@ async function createLink(notePath, options = {}) { | |||||||
|     const autoConvertToImage = options.autoConvertToImage === undefined ? false : options.autoConvertToImage; |     const autoConvertToImage = options.autoConvertToImage === undefined ? false : options.autoConvertToImage; | ||||||
| 
 | 
 | ||||||
|     const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath); |     const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath); | ||||||
|  |     if (!noteId) { | ||||||
|  |         logError("Missing note ID"); | ||||||
|  | 
 | ||||||
|  |         return $("<span>").text("[missing note]"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const viewScope = options.viewScope || {}; |     const viewScope = options.viewScope || {}; | ||||||
|     const viewMode = viewScope.viewMode || 'default'; |     const viewMode = viewScope.viewMode || 'default'; | ||||||
|     let linkTitle = options.title; |     let linkTitle = options.title; | ||||||
| @ -54,19 +77,19 @@ async function createLink(notePath, options = {}) { | |||||||
|             const attachment = await froca.getAttachment(viewScope.attachmentId); |             const attachment = await froca.getAttachment(viewScope.attachmentId); | ||||||
| 
 | 
 | ||||||
|             linkTitle = attachment ? attachment.title : '[missing attachment]'; |             linkTitle = attachment ? attachment.title : '[missing attachment]'; | ||||||
|         } else { |         } else if (noteId) { | ||||||
|             linkTitle = await treeService.getNoteTitle(noteId, parentNoteId); |             linkTitle = await treeService.getNoteTitle(noteId, parentNoteId); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const note = await froca.getNote(noteId); |     const note = await froca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (autoConvertToImage && ['image', 'canvas', 'mermaid'].includes(note.type) && viewMode === 'default') { |     if (autoConvertToImage && (note?.type && ['image', 'canvas', 'mermaid'].includes(note.type)) && viewMode === 'default') { | ||||||
|         const encodedTitle = encodeURIComponent(linkTitle); |         const encodedTitle = encodeURIComponent(linkTitle || ""); | ||||||
| 
 | 
 | ||||||
|         return $("<img>") |         return $("<img>") | ||||||
|             .attr("src", `api/images/${noteId}/${encodedTitle}?${Math.random()}`) |             .attr("src", `api/images/${noteId}/${encodedTitle}?${Math.random()}`) | ||||||
|             .attr("alt", linkTitle); |             .attr("alt", linkTitle || ""); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const $container = $("<span>"); |     const $container = $("<span>"); | ||||||
| @ -102,7 +125,7 @@ async function createLink(notePath, options = {}) { | |||||||
|     $container.append($noteLink); |     $container.append($noteLink); | ||||||
| 
 | 
 | ||||||
|     if (showNotePath) { |     if (showNotePath) { | ||||||
|         const resolvedPathSegments = await treeService.resolveNotePathToSegments(notePath); |         const resolvedPathSegments = await treeService.resolveNotePathToSegments(notePath) || []; | ||||||
|         resolvedPathSegments.pop(); // Remove last element
 |         resolvedPathSegments.pop(); // Remove last element
 | ||||||
| 
 | 
 | ||||||
|         const resolvedPath = resolvedPathSegments.join("/"); |         const resolvedPath = resolvedPathSegments.join("/"); | ||||||
| @ -118,7 +141,14 @@ async function createLink(notePath, options = {}) { | |||||||
|     return $container; |     return $container; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) { | interface CalculateHashOpts { | ||||||
|  |     notePath: string; | ||||||
|  |     ntxId?: string; | ||||||
|  |     hoistedNoteId?: string; | ||||||
|  |     viewScope: ViewScope; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}: CalculateHashOpts) { | ||||||
|     notePath = notePath || ""; |     notePath = notePath || ""; | ||||||
|     const params = [ |     const params = [ | ||||||
|         ntxId ? { ntxId: ntxId } : null, |         ntxId ? { ntxId: ntxId } : null, | ||||||
| @ -129,9 +159,9 @@ function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) { | |||||||
| 
 | 
 | ||||||
|     const paramStr = params.map(pair => { |     const paramStr = params.map(pair => { | ||||||
|         const name = Object.keys(pair)[0]; |         const name = Object.keys(pair)[0]; | ||||||
|         const value = pair[name]; |         const value = (pair as Record<string, string | undefined>)[name]; | ||||||
| 
 | 
 | ||||||
|         return `${encodeURIComponent(name)}=${encodeURIComponent(value)}`; |         return `${encodeURIComponent(name)}=${encodeURIComponent(value || "")}`; | ||||||
|     }).join("&"); |     }).join("&"); | ||||||
| 
 | 
 | ||||||
|     if (!notePath && !paramStr) { |     if (!notePath && !paramStr) { | ||||||
| @ -147,7 +177,7 @@ function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) { | |||||||
|     return hash; |     return hash; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function parseNavigationStateFromUrl(url) { | function parseNavigationStateFromUrl(url: string | undefined) { | ||||||
|     if (!url) { |     if (!url) { | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
| @ -164,7 +194,7 @@ function parseNavigationStateFromUrl(url) { | |||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const viewScope = { |     const viewScope: ViewScope = { | ||||||
|         viewMode: 'default' |         viewMode: 'default' | ||||||
|     }; |     }; | ||||||
|     let ntxId = null; |     let ntxId = null; | ||||||
| @ -184,7 +214,7 @@ function parseNavigationStateFromUrl(url) { | |||||||
|             } else if (name === 'searchString') { |             } else if (name === 'searchString') { | ||||||
|                 searchString = value; // supports triggering search from URL, e.g. #?searchString=blabla
 |                 searchString = value; // supports triggering search from URL, e.g. #?searchString=blabla
 | ||||||
|             } else if (['viewMode', 'attachmentId'].includes(name)) { |             } else if (['viewMode', 'attachmentId'].includes(name)) { | ||||||
|                 viewScope[name] = value; |                 (viewScope as any)[name] = value; | ||||||
|             } else { |             } else { | ||||||
|                 console.warn(`Unrecognized hash parameter '${name}'.`); |                 console.warn(`Unrecognized hash parameter '${name}'.`); | ||||||
|             } |             } | ||||||
| @ -201,14 +231,14 @@ function parseNavigationStateFromUrl(url) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function goToLink(evt) { | function goToLink(evt: MouseEvent) { | ||||||
|     const $link = $(evt.target).closest("a,.block-link"); |     const $link = $(evt.target as any).closest("a,.block-link"); | ||||||
|     const hrefLink = $link.attr('href') || $link.attr('data-href'); |     const hrefLink = $link.attr('href') || $link.attr('data-href'); | ||||||
| 
 | 
 | ||||||
|     return goToLinkExt(evt, hrefLink, $link); |     return goToLinkExt(evt, hrefLink, $link); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function goToLinkExt(evt, hrefLink, $link) { | function goToLinkExt(evt: MouseEvent, hrefLink: string | undefined, $link: JQuery<HTMLElement>) { | ||||||
|     if (hrefLink?.startsWith("data:")) { |     if (hrefLink?.startsWith("data:")) { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @ -230,7 +260,7 @@ function goToLinkExt(evt, hrefLink, $link) { | |||||||
|         if (openInNewTab) { |         if (openInNewTab) { | ||||||
|             appContext.tabManager.openTabWithNoteWithHoisting(notePath, {viewScope}); |             appContext.tabManager.openTabWithNoteWithHoisting(notePath, {viewScope}); | ||||||
|         } else if (isLeftClick) { |         } else if (isLeftClick) { | ||||||
|             const ntxId = $(evt.target).closest("[data-ntx-id]").attr("data-ntx-id"); |             const ntxId = $(evt.target as any).closest("[data-ntx-id]").attr("data-ntx-id"); | ||||||
| 
 | 
 | ||||||
|             const noteContext = ntxId |             const noteContext = ntxId | ||||||
|                 ? appContext.tabManager.getNoteContextById(ntxId) |                 ? appContext.tabManager.getNoteContextById(ntxId) | ||||||
| @ -275,8 +305,8 @@ function goToLinkExt(evt, hrefLink, $link) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function linkContextMenu(e) { | function linkContextMenu(e: Event) { | ||||||
|     const $link = $(e.target).closest("a"); |     const $link = $(e.target as any).closest("a"); | ||||||
|     const url = $link.attr("href") || $link.attr("data-href"); |     const url = $link.attr("href") || $link.attr("data-href"); | ||||||
| 
 | 
 | ||||||
|     const { notePath, viewScope } = parseNavigationStateFromUrl(url); |     const { notePath, viewScope } = parseNavigationStateFromUrl(url); | ||||||
| @ -290,7 +320,7 @@ function linkContextMenu(e) { | |||||||
|     linkContextMenuService.openContextMenu(notePath, e, viewScope, null); |     linkContextMenuService.openContextMenu(notePath, e, viewScope, null); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function loadReferenceLinkTitle($el, href = null) { | async function loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string | null | undefined = null) { | ||||||
|     const $link = $el[0].tagName === 'A' ? $el : $el.find("a"); |     const $link = $el[0].tagName === 'A' ? $el : $el.find("a"); | ||||||
| 
 | 
 | ||||||
|     href = href || $link.attr("href"); |     href = href || $link.attr("href"); | ||||||
| @ -300,6 +330,11 @@ async function loadReferenceLinkTitle($el, href = null) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const {noteId, viewScope} = parseNavigationStateFromUrl(href); |     const {noteId, viewScope} = parseNavigationStateFromUrl(href); | ||||||
|  |     if (!noteId) { | ||||||
|  |         console.warn("Missing note ID."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |      | ||||||
|     const note = await froca.getNote(noteId, true); |     const note = await froca.getNote(noteId, true); | ||||||
| 
 | 
 | ||||||
|     if (note) { |     if (note) { | ||||||
| @ -312,11 +347,13 @@ async function loadReferenceLinkTitle($el, href = null) { | |||||||
|     if (note) { |     if (note) { | ||||||
|         const icon = await getLinkIcon(noteId, viewScope.viewMode); |         const icon = await getLinkIcon(noteId, viewScope.viewMode); | ||||||
| 
 | 
 | ||||||
|  |         if (icon) { | ||||||
|             $el.prepend($("<span>").addClass(icon)); |             $el.prepend($("<span>").addClass(icon)); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function getReferenceLinkTitle(href) { | async function getReferenceLinkTitle(href: string) { | ||||||
|     const {noteId, viewScope} = parseNavigationStateFromUrl(href); |     const {noteId, viewScope} = parseNavigationStateFromUrl(href); | ||||||
|     if (!noteId) { |     if (!noteId) { | ||||||
|         return "[missing note]"; |         return "[missing note]"; | ||||||
| @ -336,7 +373,7 @@ async function getReferenceLinkTitle(href) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getReferenceLinkTitleSync(href) { | function getReferenceLinkTitleSync(href: string) { | ||||||
|     const {noteId, viewScope} = parseNavigationStateFromUrl(href); |     const {noteId, viewScope} = parseNavigationStateFromUrl(href); | ||||||
|     if (!noteId) { |     if (!noteId) { | ||||||
|         return "[missing note]"; |         return "[missing note]"; | ||||||
| @ -360,7 +397,11 @@ function getReferenceLinkTitleSync(href) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // TODO: Check why the event is not supported.
 | ||||||
|  | //@ts-ignore
 | ||||||
| $(document).on('click', "a", goToLink); | $(document).on('click', "a", goToLink); | ||||||
|  | // TODO: Check why the event is not supported.
 | ||||||
|  | //@ts-ignore
 | ||||||
| $(document).on('auxclick', "a", goToLink); // to handle the middle button
 | $(document).on('auxclick', "a", goToLink); // to handle the middle button
 | ||||||
| $(document).on('contextmenu', 'a', linkContextMenu); | $(document).on('contextmenu', 'a', linkContextMenu); | ||||||
| $(document).on('dblclick', "a", e => { | $(document).on('dblclick', "a", e => { | ||||||
| @ -99,7 +99,7 @@ function isMac() { | |||||||
|     return navigator.platform.indexOf('Mac') > -1; |     return navigator.platform.indexOf('Mac') > -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function isCtrlKey(evt: KeyboardEvent) { | function isCtrlKey(evt: KeyboardEvent | MouseEvent) { | ||||||
|     return (!isMac() && evt.ctrlKey) |     return (!isMac() && evt.ctrlKey) | ||||||
|         || (isMac() && evt.metaKey); |         || (isMac() && evt.metaKey); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran