#654 - Add the open on website action

This commit is contained in:
Elio Struyf
2023-09-07 16:25:52 +02:00
parent 3f578fdfa9
commit c6ec07fa17
22 changed files with 154 additions and 18 deletions

View File

@@ -335,5 +335,6 @@
"dashboard.steps.stepsToGetStarted.assetsFolder.other.description": "Wenn Sie einen anderen Ordner konfigurieren möchten, können Sie dies manuell in der frontmatter.json-Datei tun.",
"dashboard.steps.stepsToGetStarted.template.name": "Verwende eine Konfigurationsvorlage",
"dashboard.steps.stepsToGetStarted.template.description": "Wählen Sie eine Vorlage aus, um die Datei frontmatter.json mit den empfohlenen Einstellungen vorzufüllen.",
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Vorlagendateien kopiert."
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Vorlagendateien kopiert.",
"common.openOnWebsite": "Auf der Website öffnen"
}

View File

@@ -335,5 +335,6 @@
"panel.viewPanel.mediaInsert": "Continuer dans le tableau de bord des médias pour sélectionner l'image que vous voulez insérer.",
"dashboard.steps.stepsToGetStarted.template.name": "Utiliser un modèle de configuration",
"dashboard.steps.stepsToGetStarted.template.description": "Sélectionnez un modèle pour préremplir le fichier frontmatter.json avec les paramètres recommandés.",
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Fichiers de modèle copiés."
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Fichiers de modèle copiés.",
"common.openOnWebsite": "Ouvrir sur le site web"
}

View File

@@ -335,5 +335,6 @@
"panel.viewPanel.mediaInsert": "Continuare nella dashboard multimediale per selezionare l'immagine che si desidera inserire.",
"dashboard.steps.stepsToGetStarted.template.name": "Usa un modello di configurazione",
"dashboard.steps.stepsToGetStarted.template.description": "Seleziona un modello per riempire in anticipo il file frontmatter.json con le impostazioni consigliate.",
"listeners.dashboard.settingsListener.triggerTemplate.notification": "File del modello copiati."
"listeners.dashboard.settingsListener.triggerTemplate.notification": "File del modello copiati.",
"common.openOnWebsite": "Apri sul sito web"
}

View File

@@ -335,5 +335,6 @@
"dashboard.steps.stepsToGetStarted.assetsFolder.other.description": "別のフォルダを設定する場合は、frontmatter.jsonファイルで手動で行うことができます。",
"dashboard.steps.stepsToGetStarted.template.name": "設定テンプレートを使用する",
"dashboard.steps.stepsToGetStarted.template.description": "おすすめの設定でfrontmatter.jsonファイルを事前に埋めるテンプレートを選択します。",
"listeners.dashboard.settingsListener.triggerTemplate.notification": "テンプレートファイルがコピーされました。"
"listeners.dashboard.settingsListener.triggerTemplate.notification": "テンプレートファイルがコピーされました。",
"common.openOnWebsite": "ウェブサイトで開く"
}

View File

@@ -21,6 +21,7 @@
"common.support": "Support",
"common.remove.value": "Remove {0}",
"common.error.message": "Sorry, something went wrong.",
"common.openOnWebsite": "Open on website",
"developer.title": "Developer mode",
"developer.reload.title": "Reload the dashboard",

View File

@@ -1753,6 +1753,10 @@
"default": "yyyy-MM-dd",
"markdownDescription": "%setting.frontMatter.templates.prefix.markdownDescription%",
"scope": "Templates"
},
"frontMatter.website.host": {
"type": "string",
"markdownDescription": "%setting.frontMatter.website.host.markdownDescription%"
}
}
},

View File

@@ -240,5 +240,6 @@
"setting.frontMatter.taxonomy.dateField.deprecationMessage": "Diese Einstellung ist veraltet und wird in der nächsten Hauptversion entfernt. Bitte verwenden Sie stattdessen die neuen `isPublishDate`-Einstellungen in den Datumsfeldern Ihrer Content-Typen.",
"setting.frontMatter.taxonomy.modifiedField.deprecationMessage": "Diese Einstellung ist veraltet und wird in der nächsten Hauptversion entfernt. Bitte verwenden Sie stattdessen die neuen `isModifiedDate`-Einstellungen in den Datumsfeldern Ihrer Content-Typen.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "🚧: Specify the name of the custom field type to use.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "🚧: Specify if the empty values should be cleared."
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "🚧: Specify if the empty values should be cleared.",
"setting.frontMatter.website.host.markdownDescription": "🚧: Specify the host URL of your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.website.url)"
}

View File

@@ -241,5 +241,6 @@
"setting.frontMatter.taxonomy.modifiedField.deprecationMessage": "🚧: This setting is deprecated and will be removed in the next major version. Please use the new `isModifiedDate` settings instead in your content types date fields.",
"setting.frontMatter.global.disabledNotifications.markdownDescription": "🚧: This is an array with the notifications types that can be disabled for Front Matter CMS. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.global.disablednotifications)",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "🚧: Specify the name of the custom field type to use.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "🚧: Specify if the empty values should be cleared."
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "🚧: Specify if the empty values should be cleared.",
"setting.frontMatter.website.host.markdownDescription": "🚧: Specify the host URL of your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.website.url)"
}

View File

@@ -240,5 +240,6 @@
"setting.frontMatter.taxonomy.dateField.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please use the new `isPublishDate` settings instead in your content types date fields.",
"setting.frontMatter.taxonomy.modifiedField.deprecationMessage": "This setting is deprecated and will be removed in the next major version. Please use the new `isModifiedDate` settings instead in your content types date fields.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.customType.description": "Specify the name of the custom field type to use.",
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "Specify if the empty values should be cleared."
"setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description": "Specify if the empty values should be cleared.",
"setting.frontMatter.website.host.markdownDescription": "Specify the host URL of your website. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.website.url)"
}

View File

@@ -221,7 +221,7 @@ export class Preview {
* @param filePath
* @returns
*/
private static async getContentSlug(
public static async getContentSlug(
article: ParsedFrontMatter | null,
filePath?: string
): Promise<string | undefined> {

View File

@@ -8,6 +8,7 @@ export const GeneralCommands = {
toVSCode: {
openLink: 'openLink',
gitSync: 'gitSync',
getLocalization: 'getLocalization'
getLocalization: 'getLocalization',
openOnWebsite: 'openOnWebsite'
}
};

View File

@@ -103,6 +103,8 @@ export const SETTING_GIT_SUBMODULE_FOLDER = 'git.submodule.folder';
export const SETTING_SNIPPETS_WRAPPER = 'snippets.wrapper.enabled';
export const SETTING_WEBSITE_URL = 'website.host';
/**
* Sponsors only settings
*/

View File

@@ -1,6 +1,6 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { Menu } from '@headlessui/react';
import { EyeIcon, TerminalIcon, TrashIcon } from '@heroicons/react/outline';
import { EyeIcon, GlobeIcon, TerminalIcon, TrashIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { CustomScript, ScriptType } from '../../../models';
import { DashboardMessage } from '../../DashboardMessage';
@@ -11,6 +11,9 @@ import { useState } from 'react';
import useThemeColors from '../../hooks/useThemeColors';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { useRecoilValue } from 'recoil';
import { SettingsSelector } from '../../state';
import { GeneralCommands } from '../../../constants';
export interface IContentActionsProps {
title: string;
@@ -29,6 +32,7 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
}: React.PropsWithChildren<IContentActionsProps>) => {
const [showDeletionAlert, setShowDeletionAlert] = React.useState(false);
const { getColors } = useThemeColors();
const settings = useRecoilValue(SettingsSelector);
const [referenceElement, setReferenceElement] = useState<any>(null);
const [popperElement, setPopperElement] = useState<any>(null);
@@ -54,6 +58,16 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
setShowDeletionAlert(false);
};
const openOnWebsite = React.useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
if (settings?.websiteUrl && path) {
Messenger.send(GeneralCommands.toVSCode.openOnWebsite, {
websiteUrl: settings.websiteUrl,
filePath: path
});
}
}, [settings?.websiteUrl, path]);
const runCustomScript = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>, script: CustomScript) => {
e.stopPropagation();
@@ -101,11 +115,19 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
<Menu as="div" className={`relative flex text-left`}>
{!listView && (
<div className="hidden group-hover/card:flex">
<QuickAction title={`View content`} onClick={onView}>
<QuickAction title={l10n.t(LocalizationKey.dashboardContentsContentActionsMenuItemView)} onClick={onView}>
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
<QuickAction title={`Delete content`} onClick={onDelete}>
{
settings?.websiteUrl && (
<QuickAction title={l10n.t(LocalizationKey.commonOpenOnWebsite)} onClick={openOnWebsite}>
<GlobeIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
)
}
<QuickAction title={l10n.t(LocalizationKey.commonDelete)} onClick={onDelete}>
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
</div>
@@ -136,6 +158,20 @@ export const ContentActions: React.FunctionComponent<IContentActionsProps> = ({
onClick={(_, e) => onView(e)}
/>
{
settings?.websiteUrl && (
<MenuItem
title={
<div className="flex items-center">
<GlobeIcon className="mr-2 h-5 w-5 flex-shrink-0" aria-hidden={true} />{' '}
<span>{l10n.t(LocalizationKey.commonOpenOnWebsite)}</span>
</div>
}
onClick={(_, e) => openOnWebsite(e)}
/>
)
}
{customScriptActions}
<MenuItem

View File

@@ -33,6 +33,7 @@ export interface Settings {
contentTypes: ContentType[];
contentFolders: ContentFolder[];
crntFramework: string;
websiteUrl: string;
framework: Framework | null | undefined;
draftField: DraftField | null | undefined;
customSorting: SortingSetting[] | undefined;

View File

@@ -29,7 +29,8 @@ import {
SETTING_DASHBOARD_CONTENT_CARD_DATE,
SETTING_DASHBOARD_CONTENT_CARD_TITLE,
SETTING_DASHBOARD_CONTENT_CARD_STATE,
SETTING_DASHBOARD_CONTENT_CARD_DESCRIPTION
SETTING_DASHBOARD_CONTENT_CARD_DESCRIPTION,
SETTING_WEBSITE_URL
} from '../constants';
import {
DashboardViewType,
@@ -131,7 +132,8 @@ export class DashboardSettings {
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
snippets: Settings.get<Snippets>(SETTING_CONTENT_SNIPPETS),
snippetsWrapper: Settings.get<boolean>(SETTING_SNIPPETS_WRAPPER),
isBacker: await ext.getState<boolean | undefined>(CONTEXT.backer, 'global')
isBacker: await ext.getState<boolean | undefined>(CONTEXT.backer, 'global'),
websiteUrl: Settings.get<string>(SETTING_WEBSITE_URL)
} as ISettings;
return settings;

View File

@@ -1,4 +1,4 @@
import { SETTING_SPONSORS_AI_ENABLED } from './../constants/settings';
import { SETTING_SPONSORS_AI_ENABLED, SETTING_WEBSITE_URL } from './../constants/settings';
import { workspace } from 'vscode';
import { Extension, Settings, TaxonomyHelper } from '.';
import { Dashboard } from '../commands/Dashboard';
@@ -96,7 +96,8 @@ export class PanelSettings {
},
dataTypes: Settings.get<DataType[]>(SETTING_DATA_TYPES),
fieldGroups: Settings.get<FieldGroup[]>(SETTING_TAXONOMY_FIELD_GROUPS),
contentFolders: Folders.get()
contentFolders: Folders.get(),
websiteUrl: Settings.get<string>(SETTING_WEBSITE_URL) || ''
};
}

View File

@@ -1,10 +1,12 @@
import { GeneralCommands } from './../../constants/GeneralCommands';
import { Dashboard } from '../../commands/Dashboard';
import { PanelProvider } from '../../panelWebView/PanelProvider';
import { Extension } from '../../helpers';
import { ArticleHelper, Extension } from '../../helpers';
import { Logger } from '../../helpers/Logger';
import { commands, Uri } from 'vscode';
import { commands, Uri, window } from 'vscode';
import { PostMessageData } from '../../models';
import { Preview } from '../../commands';
import { urlJoin } from 'url-join-ts';
export abstract class BaseListener {
public static process(msg: PostMessageData) {
@@ -14,6 +16,9 @@ export abstract class BaseListener {
commands.executeCommand('vscode.open', Uri.parse(msg.payload));
}
break;
case GeneralCommands.toVSCode.openOnWebsite:
this.openOnWebsite(msg.payload);
break;
}
}
@@ -32,4 +37,38 @@ export abstract class BaseListener {
Dashboard.postWebviewMessage({ command: command as any, payload });
}
/**
* Open the page on the website
* @param param0
* @returns
*/
private static async openOnWebsite({
websiteUrl,
filePath
}: {
websiteUrl: string;
filePath?: string;
}) {
if (websiteUrl) {
const article = filePath
? await ArticleHelper.getFrontMatterByPath(filePath)
: ArticleHelper.getCurrent();
if (article) {
if (!filePath) {
const editor = window.activeTextEditor;
if (!editor) {
return;
}
filePath = editor.document.uri.fsPath;
}
const slug = await Preview.getContentSlug(article, filePath);
const fullUrl = urlJoin(websiteUrl, slug);
commands.executeCommand('vscode.open', fullUrl);
}
}
}
}

View File

@@ -87,6 +87,10 @@ export enum LocalizationKey {
* Sorry, something went wrong.
*/
commonErrorMessage = 'common.error.message',
/**
* Open on website
*/
commonOpenOnWebsite = 'common.openOnWebsite',
/**
* Developer mode
*/

View File

@@ -30,6 +30,7 @@ export interface PanelSettings {
commaSeparatedFields: string[];
aiEnabled: boolean;
contentFolders: ContentFolder[];
websiteUrl: string;
}
export interface FieldGroup {

View File

@@ -7,6 +7,7 @@ import { SlugAction } from './SlugAction';
import { StartServerButton } from './StartServerButton';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
import { OpenOnWebsiteAction } from './Actions/OpenOnWebsiteAction';
export interface IActionsProps {
metadata: any;
@@ -28,6 +29,8 @@ const Actions: React.FunctionComponent<IActionsProps> = ({
{settings?.preview?.host && <Preview slug={metadata.slug} />}
<OpenOnWebsiteAction baseUrl={settings.websiteUrl} slug={metadata.slug} />
<StartServerButton settings={settings} />
{settings && settings.scripts && settings.scripts.length > 0 && (

View File

@@ -0,0 +1,33 @@
import { messageHandler } from '@estruyf/vscode/dist/client';
import * as React from 'react';
import { ActionButton } from '../ActionButton';
import * as l10n from "@vscode/l10n"
import { LocalizationKey } from '../../../localization';
import { GeneralCommands } from '../../../constants';
export interface IOpenOnWebsiteActionProps {
baseUrl: string;
slug: string;
}
export const OpenOnWebsiteAction: React.FunctionComponent<IOpenOnWebsiteActionProps> = ({
baseUrl,
slug
}: React.PropsWithChildren<IOpenOnWebsiteActionProps>) => {
const open = () => {
messageHandler.send(GeneralCommands.toVSCode.openOnWebsite, {
websiteUrl: baseUrl,
});
};
if (!baseUrl || !slug) {
return null;
}
return (
<ActionButton
title={l10n.t(LocalizationKey.commonOpenOnWebsite)}
onClick={open} />
);
};

View File

@@ -0,0 +1 @@
export * from './OpenOnWebsiteAction';