diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16f3d8e9..6aab0436 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- [#64](https://github.com/estruyf/vscode-front-matter/issues/64): Publish toggle for easier publishing an article
- [#65](https://github.com/estruyf/vscode-front-matter/issues/65): Aggregate articles in draft
- [#66](https://github.com/estruyf/vscode-front-matter/issues/66): New dashboard webview on which you can manage all your content
+- [#69](https://github.com/estruyf/vscode-front-matter/issues/69): Welcome screen for getting started
## [2.5.1] - 2020-08-23
diff --git a/package.json b/package.json
index 9db8c101..e774eb07 100644
--- a/package.json
+++ b/package.json
@@ -267,18 +267,15 @@
},
{
"command": "frontMatter.createFromTemplate",
- "title": "New article from template",
- "category": "Front matter"
+ "title": "Front Matter: New article from template"
},
{
"command": "frontMatter.registerFolder",
- "title": "Register folder",
- "category": "Front matter"
+ "title": "Front Matter: Register folder"
},
{
"command": "frontMatter.unregisterFolder",
- "title": "Unregister folder",
- "category": "Front matter"
+ "title": "Front Matter: Unregister folder"
},
{
"command": "frontMatter.createContent",
diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts
index cb3fdb9c..e0b11a32 100644
--- a/src/commands/Dashboard.ts
+++ b/src/commands/Dashboard.ts
@@ -14,6 +14,7 @@ import { COMMAND_NAME } from '../constants/Extension';
import { Template } from './Template';
import { Notifications } from '../helpers/Notifications';
import { Settings } from '../pagesView/models/Settings';
+import { Extension } from '../helpers/Extension';
export class Dashboard {
@@ -92,6 +93,10 @@ export class Dashboard {
Dashboard.isDisposed = true;
});
+ workspace.onDidChangeConfiguration(() => {
+ Dashboard.getSettings();
+ });
+
Dashboard.webview.webview.onDidReceiveMessage(async (msg) => {
switch(msg.command) {
case DashboardMessage.getData:
@@ -107,6 +112,19 @@ export class Dashboard {
case DashboardMessage.updateSetting:
Dashboard.updateSetting(msg.data);
break;
+ case DashboardMessage.InitializeProject:
+ await commands.executeCommand(COMMAND_NAME.init);
+ // Delay to allow the project to be initialized
+ setTimeout(() => {
+ Dashboard.getSettings();
+ }, 1000);
+ break;
+ case DashboardMessage.Reload:
+ Dashboard.webview?.dispose();
+ setTimeout(() => {
+ Dashboard.open(Extension.getInstance().extensionPath);
+ }, 100);
+ break;
}
});
}
@@ -122,7 +140,8 @@ export class Dashboard {
initialized: await Template.isInitialized(),
tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag),
categories: SettingsHelper.getTaxonomy(TaxonomyType.Category),
- openOnStart: SettingsHelper.getConfig().get(SETTINGS_DASHBOARD_OPENONSTART)
+ openOnStart: SettingsHelper.getConfig().get(SETTINGS_DASHBOARD_OPENONSTART),
+ versionInfo: Extension.getInstance().getVersion()
} as Settings
});
}
@@ -213,6 +232,8 @@ export class Dashboard {
const nonce = getNonce();
+ const version = Extension.getInstance().getVersion();
+
return `
@@ -223,7 +244,7 @@ export class Dashboard {
Front Matter Dashboard
-
+
diff --git a/src/constants/Links.ts b/src/constants/Links.ts
new file mode 100644
index 00000000..23123acc
--- /dev/null
+++ b/src/constants/Links.ts
@@ -0,0 +1,4 @@
+export const GITHUB_LINK = "https://github.com/sponsors/estruyf";
+export const ISSUE_LINK = "https://github.com/estruyf/vscode-front-matter/issues";
+export const SPONSOR_LINK = "https://github.com/estruyf/vscode-front-matter";
+export const REVIEW_LINK = "https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter&ssr=false#review-details";
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index 6fedba3f..29ac2173 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -5,12 +5,12 @@ import { Folders } from './commands/Folders';
import { Preview } from './commands/Preview';
import { Project } from './commands/Project';
import { Template } from './commands/Template';
-import { COMMAND_NAME, EXTENSION_ID, EXTENSION_STATE_VERSION } from './constants/Extension';
+import { COMMAND_NAME } from './constants/Extension';
import { TaxonomyType } from './models';
import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider';
import { TagType } from './viewpanel/TagType';
import { ExplorerView } from './webview/ExplorerView';
-import { Notifications } from './helpers/Notifications';
+import { Extension } from './helpers/Extension';
let frontMatterStatusBar: vscode.StatusBarItem;
let statusDebouncer: { (fnc: any, time: number): void; };
@@ -20,11 +20,9 @@ let collection: vscode.DiagnosticCollection;
const mdSelector: vscode.DocumentSelector = { language: 'markdown', scheme: 'file' };
export async function activate({ subscriptions, extensionUri, extensionPath, globalState }: vscode.ExtensionContext) {
- collection = vscode.languages.createDiagnosticCollection('frontMatter');
+ const extension = Extension.getInstance(globalState, extensionPath);
- const frontMatter = vscode.extensions.getExtension(EXTENSION_ID)!;
- const frontMatterVersoin = frontMatter.packageJSON.version;
- const crntVersion = globalState.get(EXTENSION_STATE_VERSION);
+ collection = vscode.languages.createDiagnosticCollection('frontMatter');
// Pages dashboard
Dashboard.init(extensionPath);
@@ -32,7 +30,7 @@ export async function activate({ subscriptions, extensionUri, extensionPath, glo
Dashboard.open(extensionPath);
}));
- if (!crntVersion) {
+ if (!extension.getVersion().usedVersion) {
vscode.commands.executeCommand(COMMAND_NAME.dashboard);
}
diff --git a/src/helpers/Extension.ts b/src/helpers/Extension.ts
new file mode 100644
index 00000000..3526669e
--- /dev/null
+++ b/src/helpers/Extension.ts
@@ -0,0 +1,44 @@
+import { Memento, extensions } from "vscode";
+import { EXTENSION_ID, EXTENSION_STATE_VERSION } from "../constants/Extension";
+
+
+export class Extension {
+ private static instance: Extension;
+
+ private constructor(private globalState: Memento, private extPath: string) {}
+
+ /**
+ * Creates the singleton instance for the panel
+ * @param extPath
+ */
+ public static getInstance(globalState?: Memento, extPath?: string): Extension {
+ if (!Extension.instance && globalState && extPath) {
+ Extension.instance = new Extension(globalState, extPath);
+ }
+
+ return Extension.instance;
+ }
+
+ public getVersion(): { usedVersion: string | undefined, installedVersion: string } {
+ const frontMatter = extensions.getExtension(EXTENSION_ID)!;
+ const installedVersion = frontMatter.packageJSON.version;
+ const usedVersion = this.globalState.get(EXTENSION_STATE_VERSION);
+
+ if (!usedVersion) {
+ this.setVersion(installedVersion);
+ };
+
+ return {
+ usedVersion,
+ installedVersion
+ };
+ }
+
+ public setVersion(installedVersion: string): void {
+ this.globalState.update(EXTENSION_STATE_VERSION, installedVersion);
+ }
+
+ public get extensionPath(): string {
+ return this.extPath;
+ }
+}
\ No newline at end of file
diff --git a/src/models/VersionInfo.ts b/src/models/VersionInfo.ts
new file mode 100644
index 00000000..478a0188
--- /dev/null
+++ b/src/models/VersionInfo.ts
@@ -0,0 +1,6 @@
+
+
+export interface VersionInfo {
+ usedVersion: string | undefined;
+ installedVersion: string;
+}
\ No newline at end of file
diff --git a/src/pagesView/DashboardMessage.ts b/src/pagesView/DashboardMessage.ts
index 0382347b..9ccdac81 100644
--- a/src/pagesView/DashboardMessage.ts
+++ b/src/pagesView/DashboardMessage.ts
@@ -4,4 +4,6 @@ export enum DashboardMessage {
getTheme = 'getTheme',
createContent = 'createContent',
updateSetting = 'updateSetting',
+ InitializeProject = 'InitializeProject',
+ Reload = 'Reload',
}
\ No newline at end of file
diff --git a/src/pagesView/components/Dashboard.tsx b/src/pagesView/components/Dashboard.tsx
index 77575184..cd397d04 100644
--- a/src/pagesView/components/Dashboard.tsx
+++ b/src/pagesView/components/Dashboard.tsx
@@ -7,10 +7,14 @@ import { Tab } from '../constants/Tab';
import { SortOption } from '../constants/SortOption';
import useDarkMode from '../../hooks/useDarkMode';
import usePages from '../hooks/usePages';
+import { SponsorMsg } from './SponsorMsg';
+import { WelcomeScreen } from './WelcomeScreen';
-export interface IDashboardProps {}
+export interface IDashboardProps {
+ showWelcome: boolean;
+}
-export const Dashboard: React.FunctionComponent = ({}: React.PropsWithChildren) => {
+export const Dashboard: React.FunctionComponent = ({showWelcome}: React.PropsWithChildren) => {
const { loading, pages, settings } = useMessages();
const [ tab, setTab ] = React.useState(Tab.All);
const [ sorting, setSorting ] = React.useState(SortOption.LastModified);
@@ -22,6 +26,18 @@ export const Dashboard: React.FunctionComponent = ({}: React.Pr
useDarkMode();
const pageGroups = [...new Set(pages.map(page => page.fmGroup))];
+
+ if (!settings) {
+ return ;
+ }
+
+ if (showWelcome) {
+ return ;
+ }
+
+ if (!settings.initialized || settings.folders?.length === 0) {
+ return ;
+ }
return (
@@ -46,7 +62,7 @@ export const Dashboard: React.FunctionComponent = ({}: React.Pr
{ loading ? : }
- If you find FrontMatter useful, please consider sponsoring it, or if you use FrontMatter for work, please encourage your employer to sponsoring it.
+
);
diff --git a/src/pagesView/components/Overview.tsx b/src/pagesView/components/Overview.tsx
index aeb364c3..8644b110 100644
--- a/src/pagesView/components/Overview.tsx
+++ b/src/pagesView/components/Overview.tsx
@@ -1,6 +1,5 @@
import * as React from 'react';
import { FrontMatterIcon } from '../../viewpanel/components/Icons/FrontMatterIcon';
-import { MarkdownIcon } from '../../viewpanel/components/Icons/MarkdownIcon';
import { Page } from '../models/Page';
import { Settings } from '../models/Settings';
import { Item } from './Item';
@@ -18,13 +17,12 @@ export const Overview: React.FunctionComponent = ({pages, settin
return (
-
+
{
settings?.folders?.length > 0 ? (
No Markdown to show
) : (
<>
-
Hi, this might be the first time you opened the dashboard, or recently started using Front Matter.
Make sure you registered a content folder in your project to let Front Matter find the contents.
>
)
diff --git a/src/pagesView/components/SponsorMsg.tsx b/src/pagesView/components/SponsorMsg.tsx
new file mode 100644
index 00000000..3f186caf
--- /dev/null
+++ b/src/pagesView/components/SponsorMsg.tsx
@@ -0,0 +1,19 @@
+import { HeartIcon, StarIcon } from '@heroicons/react/outline';
+import * as React from 'react';
+import { REVIEW_LINK, SPONSOR_LINK } from '../../constants/Links';
+
+export interface ISponsorMsgProps {}
+
+export const SponsorMsg: React.FunctionComponent
= (props: React.PropsWithChildren) => {
+ return (
+
+
+ Sponsor
+
+ FrontMatter
+
+ Review
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/pagesView/components/Steps/Step.tsx b/src/pagesView/components/Steps/Step.tsx
new file mode 100644
index 00000000..7e961ef4
--- /dev/null
+++ b/src/pagesView/components/Steps/Step.tsx
@@ -0,0 +1,60 @@
+import { CheckIcon } from '@heroicons/react/outline';
+import * as React from 'react';
+import { Status } from '../../models/Status';
+
+export interface IStepProps {
+ name: string;
+ description: string;
+ status: Status;
+ showLine: boolean;
+ onClick?: () => void;
+}
+
+export const Step: React.FunctionComponent = ({name, description, status, showLine, onClick}: React.PropsWithChildren) => {
+ return (
+ <>
+ {
+ showLine ? (
+
+ ) : null
+ }
+
+ { if (onClick) { onClick(); } }} disabled={!onClick}>
+ {
+ status === Status.NotStarted && (
+
+
+
+
+
+ )
+ }
+
+ {
+ status === Status.Active && (
+
+
+
+
+
+ )
+ }
+
+ {
+ status === Status.Completed && (
+
+
+
+
+
+ )
+ }
+
+
+ {name}
+
+
+
+ >
+ );
+};
\ No newline at end of file
diff --git a/src/pagesView/components/Steps/StepsToGetStarted.tsx b/src/pagesView/components/Steps/StepsToGetStarted.tsx
new file mode 100644
index 00000000..3cf88d44
--- /dev/null
+++ b/src/pagesView/components/Steps/StepsToGetStarted.tsx
@@ -0,0 +1,45 @@
+import * as React from 'react';
+import { MessageHelper } from '../../../helpers/MessageHelper';
+import { DashboardMessage } from '../../DashboardMessage';
+import { Settings } from '../../models/Settings';
+import { Status } from '../../models/Status';
+import { Step } from './Step';
+
+export interface IStepsToGetStartedProps {
+ settings: Settings;
+}
+
+export const StepsToGetStarted: React.FunctionComponent = ({settings}: React.PropsWithChildren) => {
+
+ const steps = [
+ {
+ name: 'Initialize project',
+ description: 'Initialize the project with a template folder and sample markdown file. The template folder can be used to define your own templates. Start by clicking on this action .',
+ status: settings.initialized ? Status.Completed : Status.NotStarted,
+ onClick: settings.initialized ? undefined : () => { MessageHelper.sendMessage(DashboardMessage.InitializeProject); }
+ },
+ {
+ name: 'Register content folders (manual action)',
+ description: 'Register your content folder(s). You can perform this action by right-clicking on the folder in the explorer view, and selecting register folder . Once a folder is set, Front Matter can be used to list all contents and allow you to create content.',
+ status: settings.folders && settings.folders.length > 0 ? Status.Completed : Status.NotStarted
+ },
+ {
+ name: 'Show the dashboard',
+ description: 'Once both actions are completed, click on this action to load the dashboard.',
+ status: (settings.initialized && settings.folders && settings.folders.length > 0) ? Status.Active : Status.NotStarted,
+ onClick: (settings.initialized && settings.folders && settings.folders.length > 0) ? () => { MessageHelper.sendMessage(DashboardMessage.Reload); } : undefined
+ }
+ ];
+
+ return (
+
+
+ {steps.map((step, stepIdx) => (
+
+
+
+ ))}
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/pagesView/components/WelcomeScreen.tsx b/src/pagesView/components/WelcomeScreen.tsx
new file mode 100644
index 00000000..4aafc5a8
--- /dev/null
+++ b/src/pagesView/components/WelcomeScreen.tsx
@@ -0,0 +1,82 @@
+import { HeartIcon, StarIcon } from '@heroicons/react/outline';
+import * as React from 'react';
+import { GITHUB_LINK, REVIEW_LINK, SPONSOR_LINK } from '../../constants/Links';
+import { FrontMatterIcon } from '../../viewpanel/components/Icons/FrontMatterIcon';
+import { GitHubIcon } from '../../viewpanel/components/Icons/GitHubIcon';
+import { Settings } from '../models/Settings';
+import { StepsToGetStarted } from './Steps/StepsToGetStarted';
+
+export interface IWelcomeScreenProps {
+ settings: Settings;
+}
+
+export const WelcomeScreen: React.FunctionComponent = ({settings}: React.PropsWithChildren) => {
+
+ return (
+
+
+
+
+
+
+ Manage your static site with {' '}
+ Front Matter
+
+
+
+ Thank you for taking the time to test out Front Matter!
+
+
+
+ We try to aim to make Front Matter as easy to use as possible, but if you have any questions, please don't hesitate to reach out to us on GitHub.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Perform the next steps to get you started with the extension
+
+
+
+
+
+
+
+ Once you completed both actions, the dashboard will show its full potential. You can also use the extension from the Front Matter side panel. There you will find the actions you can perform specifically for your pages.
+
+
+
+
+
+ We hope you enjoy Front Matter!
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/pagesView/hooks/useMessages.tsx b/src/pagesView/hooks/useMessages.tsx
index cd812709..b3c54ea9 100644
--- a/src/pagesView/hooks/useMessages.tsx
+++ b/src/pagesView/hooks/useMessages.tsx
@@ -1,6 +1,5 @@
import { useState, useEffect } from 'react';
import { MessageHelper } from '../../helpers/MessageHelper';
-import { ContentFolder } from '../../models';
import { DashboardCommand } from '../DashboardCommand';
import { DashboardMessage } from '../DashboardMessage';
import { Page } from '../models/Page';
@@ -11,7 +10,7 @@ const vscode = MessageHelper.getVsCodeAPI();
export default function useMessages(options?: any) {
const [loading, setLoading] = useState(false);
const [pages, setPages] = useState([]);
- const [settings, setSettings] = useState({} as any);
+ const [settings, setSettings] = useState(undefined);
window.addEventListener('message', event => {
const message = event.data;
diff --git a/src/pagesView/index.tsx b/src/pagesView/index.tsx
index ba00dddf..7b0a6720 100644
--- a/src/pagesView/index.tsx
+++ b/src/pagesView/index.tsx
@@ -11,4 +11,5 @@ declare const acquireVsCodeApi: () => {
};
const elm = document.querySelector("#app");
-render( , elm);
\ No newline at end of file
+const welcome = elm?.getAttribute("data-showWelcome");
+render( , elm);
\ No newline at end of file
diff --git a/src/pagesView/models/Settings.ts b/src/pagesView/models/Settings.ts
index 1e64e53a..9c11cdb6 100644
--- a/src/pagesView/models/Settings.ts
+++ b/src/pagesView/models/Settings.ts
@@ -1,3 +1,4 @@
+import { VersionInfo } from '../../models/VersionInfo';
import { ContentFolder } from './../../models/ContentFolder';
export interface Settings {
@@ -6,4 +7,5 @@ export interface Settings {
tags: string[];
categories: string[];
openOnStart: boolean | null;
+ versionInfo: VersionInfo;
}
\ No newline at end of file
diff --git a/src/pagesView/models/Status.ts b/src/pagesView/models/Status.ts
new file mode 100644
index 00000000..074d0448
--- /dev/null
+++ b/src/pagesView/models/Status.ts
@@ -0,0 +1,7 @@
+
+
+export enum Status {
+ Active = 1,
+ Completed,
+ NotStarted
+}
\ No newline at end of file
diff --git a/src/viewpanel/components/Icons/FrontMatterIcon.tsx b/src/viewpanel/components/Icons/FrontMatterIcon.tsx
index 0478fcb4..3aa53268 100644
--- a/src/viewpanel/components/Icons/FrontMatterIcon.tsx
+++ b/src/viewpanel/components/Icons/FrontMatterIcon.tsx
@@ -6,6 +6,6 @@ export interface IFrontMatterIconProps {
export const FrontMatterIcon: React.FunctionComponent = ({className}: React.PropsWithChildren) => {
return (
-
+
);
};
\ No newline at end of file
diff --git a/src/viewpanel/components/Icons/GitHubIcon.tsx b/src/viewpanel/components/Icons/GitHubIcon.tsx
new file mode 100644
index 00000000..4f9e4afd
--- /dev/null
+++ b/src/viewpanel/components/Icons/GitHubIcon.tsx
@@ -0,0 +1,11 @@
+import * as React from 'react';
+
+export interface IGitHubIconProps {
+ className: string;
+}
+
+export const GitHubIcon: React.FunctionComponent = ({className}: React.PropsWithChildren) => {
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/src/viewpanel/components/OtherActions.tsx b/src/viewpanel/components/OtherActions.tsx
index fa3dd60b..6603fce3 100644
--- a/src/viewpanel/components/OtherActions.tsx
+++ b/src/viewpanel/components/OtherActions.tsx
@@ -11,6 +11,7 @@ import { SettingsIcon } from './Icons/SettingsIcon';
import { TemplateIcon } from './Icons/TemplateIcon';
import { WritingIcon } from './Icons/WritingIcon';
import { OtherActionButton } from './OtherActionButton';
+import { ISSUE_LINK } from '../../constants/Links';
export interface IOtherActionsProps {
isFile: boolean;
@@ -58,7 +59,7 @@ export const OtherActions: React.FunctionComponent = ({isFil
Reveal project folder
>
diff --git a/src/viewpanel/components/SponsorMsg.tsx b/src/viewpanel/components/SponsorMsg.tsx
index 4f876b98..c0c92b48 100644
--- a/src/viewpanel/components/SponsorMsg.tsx
+++ b/src/viewpanel/components/SponsorMsg.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { SPONSOR_LINK } from '../../constants/Links';
import { HeartIcon } from './Icons/HeartIcon';
export interface ISponsorMsgProps {}
@@ -6,7 +7,7 @@ export interface ISponsorMsgProps {}
export const SponsorMsg: React.FunctionComponent = (props: React.PropsWithChildren) => {
return (
-
+
Sponsor FrontMatter