diff --git a/src/commands/Preview.ts b/src/commands/Preview.ts index e7302816..ec492cd9 100644 --- a/src/commands/Preview.ts +++ b/src/commands/Preview.ts @@ -22,6 +22,7 @@ import { urlJoin } from 'url-join-ts'; import { WebviewHelper } from '@estruyf/vscode'; import { Folders } from './Folders'; import { DataListener } from '../listeners/panel'; +import { ParsedFrontMatter } from '../parsers'; export class Preview { public static filePath: string | undefined = undefined; @@ -55,105 +56,7 @@ export class Preview { } const article = editor ? ArticleHelper.getFrontMatter(editor) : null; - let slug = article?.data ? article.data.slug : ''; - - let pathname = settings.pathname; - - let selectedFolder: ContentFolder | undefined | null = null; - const filePath = parseWinPath(editor?.document.uri.fsPath); - - let contentType: ContentType | undefined = undefined; - if (article?.data) { - contentType = ArticleHelper.getContentType(article.data); - } - - // Check if there is a pathname defined on content folder level - const folders = Folders.get(); - if (folders.length > 0) { - const foldersWithPath = folders.filter((folder) => folder.previewPath); - - for (const folder of foldersWithPath) { - const folderPath = parseWinPath(folder.path); - if (filePath.startsWith(folderPath)) { - if (!selectedFolder || selectedFolder.path.length < folderPath.length) { - selectedFolder = folder; - } - } - } - - if (!selectedFolder && article?.data && contentType && !contentType.previewPath) { - // Try to find the folder by content type - const crntFolders = folders.filter((folder) => - folder.contentTypes?.includes((contentType as ContentType).name) - ); - - if (crntFolders && crntFolders.length === 1) { - selectedFolder = crntFolders[0]; - } else if (crntFolders && crntFolders.length > 1) { - selectedFolder = await Preview.askUserToPickFolder(crntFolders); - } else { - selectedFolder = await Preview.askUserToPickFolder(folders.filter((f) => f.previewPath)); - } - } - - if (selectedFolder && selectedFolder.previewPath) { - pathname = selectedFolder.previewPath; - } - } - - // Check if there is a pathname defined on content type level - if (article?.data) { - if (contentType && contentType.previewPath) { - pathname = contentType.previewPath; - } - } - - if (!slug) { - slug = Article.getSlug(); - } - - if (pathname) { - // Known placeholders - const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; - pathname = processKnownPlaceholders(pathname, article?.data?.title, dateFormat); - - // Custom placeholders - pathname = await ArticleHelper.processCustomPlaceholders( - pathname, - article?.data?.title, - filePath - ); - - // Process the path placeholders - {{pathToken.}} - if (filePath) { - const wsFolder = Folders.getWorkspaceFolder(); - // Get relative file path - const folderPath = wsFolder ? parseWinPath(wsFolder.fsPath) : ''; - const relativePath = filePath.replace(folderPath, ''); - pathname = processPathPlaceholders(pathname, relativePath, filePath, selectedFolder); - } - - // Support front matter placeholders - {{fm.}} - pathname = processFmPlaceholders(pathname, article?.data); - - try { - const articleDate = ArticleHelper.getDate(article); - slug = join( - format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string), - slug - ); - } catch (error) { - slug = join(pathname, slug); - } - } - - // Make sure there are no backslashes in the slug - slug = parseWinPath(slug); - - // Verify if the slug doesn't end with _index or index - if (slug.endsWith('_index') || slug.endsWith('index')) { - slug = slug.substring(0, slug.endsWith('_index') ? slug.length - 6 : slug.length - 5); - } + const slug = await this.getContentSlug(article, editor?.document.uri.fsPath); // Create the preview webview const webView = window.createWebviewPanel( @@ -177,8 +80,7 @@ export class Preview { light: Uri.file(join(extensionPath, 'assets/icons/frontmatter-short-light.svg')) }; - const crntUrl = settings.host.startsWith('http') ? settings.host : `http://${settings.host}`; - const localhostUrl = await env.asExternalUri(Uri.parse(crntUrl)); + const localhostUrl = await this.getLocalServerUrl(); const cspSource = webView.webview.cspSource; @@ -278,6 +180,154 @@ export class Preview { Telemetry.send(TelemetryEvent.openPreview); } + /** + * Update the url of the preview webview + * @param filePath + * @param slug + */ + public static async updatePageUrl(filePath: string, slug?: string) { + const webView = this.webviews[filePath]; + if (webView) { + const localhost = await this.getLocalServerUrl(); + const article = await ArticleHelper.getFrontMatterByPath(filePath); + const slug = await this.getContentSlug(article, filePath); + + webView.webview.postMessage({ + command: PreviewCommands.toWebview.updateUrl, + payload: urlJoin(localhost.toString(), slug || '') + }); + } + } + + /** + * Retrieve the slug of the content + * @param article + * @param filePath + * @returns + */ + private static async getContentSlug( + article: ParsedFrontMatter | null, + filePath?: string + ): Promise { + if (!filePath) { + return; + } + + let slug = article?.data ? article.data.slug : ''; + + const settings = this.getSettings(); + let pathname = settings.pathname; + + let selectedFolder: ContentFolder | undefined | null = null; + filePath = parseWinPath(filePath); + + let contentType: ContentType | undefined = undefined; + if (article?.data) { + contentType = ArticleHelper.getContentType(article.data); + } + + // Check if there is a pathname defined on content folder level + const folders = Folders.get(); + if (folders.length > 0) { + const foldersWithPath = folders.filter((folder) => folder.previewPath); + + for (const folder of foldersWithPath) { + const folderPath = parseWinPath(folder.path); + if (filePath.startsWith(folderPath)) { + if (!selectedFolder || selectedFolder.path.length < folderPath.length) { + selectedFolder = folder; + } + } + } + + if (!selectedFolder && article?.data && contentType && !contentType.previewPath) { + // Try to find the folder by content type + const crntFolders = folders.filter((folder) => + folder.contentTypes?.includes((contentType as ContentType).name) + ); + + if (crntFolders && crntFolders.length === 1) { + selectedFolder = crntFolders[0]; + } else if (crntFolders && crntFolders.length > 1) { + selectedFolder = await Preview.askUserToPickFolder(crntFolders); + } else { + selectedFolder = await Preview.askUserToPickFolder(folders.filter((f) => f.previewPath)); + } + } + + if (selectedFolder && selectedFolder.previewPath) { + pathname = selectedFolder.previewPath; + } + } + + // Check if there is a pathname defined on content type level + if (article?.data) { + if (contentType && contentType.previewPath) { + pathname = contentType.previewPath; + } + } + + if (!slug) { + slug = Article.getSlug(); + } + + if (pathname) { + // Known placeholders + const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; + pathname = processKnownPlaceholders(pathname, article?.data?.title, dateFormat); + + // Custom placeholders + pathname = await ArticleHelper.processCustomPlaceholders( + pathname, + article?.data?.title, + filePath + ); + + // Process the path placeholders - {{pathToken.}} + if (filePath) { + const wsFolder = Folders.getWorkspaceFolder(); + // Get relative file path + const folderPath = wsFolder ? parseWinPath(wsFolder.fsPath) : ''; + const relativePath = filePath.replace(folderPath, ''); + pathname = processPathPlaceholders(pathname, relativePath, filePath, selectedFolder); + } + + // Support front matter placeholders - {{fm.}} + pathname = processFmPlaceholders(pathname, article?.data); + + try { + const articleDate = ArticleHelper.getDate(article); + slug = join( + format(articleDate || new Date(), DateHelper.formatUpdate(pathname) as string), + slug + ); + } catch (error) { + slug = join(pathname, slug); + } + } + + // Make sure there are no backslashes in the slug + slug = parseWinPath(slug); + + // Verify if the slug doesn't end with _index or index + if (slug.endsWith('_index') || slug.endsWith('index')) { + slug = slug.substring(0, slug.endsWith('_index') ? slug.length - 6 : slug.length - 5); + } + + return slug; + } + + /** + * Retrieve the localhost url + * @returns + */ + private static async getLocalServerUrl() { + const settings = Preview.getSettings(); + const crntUrl = settings?.host?.startsWith('http') ? settings.host : `http://${settings.host}`; + const localhostUrl = await env.asExternalUri(Uri.parse(crntUrl)); + return localhostUrl; + } + /** * Retrieve all settings related to the preview command */ diff --git a/src/constants/PreviewCommands.ts b/src/constants/PreviewCommands.ts index 6fafa19c..3212c2cb 100644 --- a/src/constants/PreviewCommands.ts +++ b/src/constants/PreviewCommands.ts @@ -2,5 +2,7 @@ export const PreviewCommands = { toVSCode: { open: `preview.open` }, - fromVSCode: {} + toWebview: { + updateUrl: `preview.updateUrl` + } }; diff --git a/src/dashboardWebView/components/Preview/Preview.tsx b/src/dashboardWebView/components/Preview/Preview.tsx index 1e9c494d..d3b5ac39 100644 --- a/src/dashboardWebView/components/Preview/Preview.tsx +++ b/src/dashboardWebView/components/Preview/Preview.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { useEffect, useRef, useState } from 'react'; import { PreviewCommands } from '../../../constants'; import useThemeColors from '../../hooks/useThemeColors'; +import { EventData } from '@estruyf/vscode/dist/models'; export interface IPreviewProps { url: string | null; @@ -35,10 +36,24 @@ export const Preview: React.FunctionComponent = ({ iframeRef.current!.src = navUrl; }; + const msgListener = (message: MessageEvent>) => { + if (message.data.command === PreviewCommands.toWebview.updateUrl) { + setCrntUrl(message.data.payload); + } + }; + useEffect(() => { setCrntUrl(url); }, [url]); + useEffect(() => { + Messenger.listen(msgListener); + + return () => { + Messenger.unlisten(msgListener); + }; + }) + return (
= ({