diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2f8d9a..39f0ecbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### ✨ New features +- [#731](https://github.com/estruyf/vscode-front-matter/issues/731): Added the ability to map/unmap taxonomy to multiple pages at once + ### 🎨 Enhancements - [#727](https://github.com/estruyf/vscode-front-matter/pull/727): Updated Japanese translations thanks to [mayumihara](https://github.com/mayumih387) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index ced9308e..d3dfda6f 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -3,6 +3,7 @@ "common.edit": "Edit", "common.delete": "Delete", "common.cancel": "Cancel", + "common.apply": "Apply", "common.clear": "Clear", "common.clear.value": "Clear value", "common.search": "Search", @@ -20,6 +21,7 @@ "common.slug": "Slug", "common.support": "Support", "common.remove.value": "Remove {0}", + "common.filter": "Back", "common.filter.value": "Filter by {0}", "common.error.message": "Sorry, something went wrong.", "common.openOnWebsite": "Open on website", @@ -32,6 +34,7 @@ "common.yes": "yes", "common.no": "no", "common.openSettings": "Open settings", + "common.back": "Back", "notifications.outputChannel.link": "output window", "notifications.outputChannel.description": "Check the {0} for more details.", @@ -294,6 +297,10 @@ "dashboard.taxonomyView.taxonomyManager.table.heading.action": "Action", "dashboard.taxonomyView.taxonomyManager.table.row.empty": "No {0} found", "dashboard.taxonomyView.taxonomyManager.table.unmapped.title": "Missing in your settings", + "dashboard.taxonomyView.taxonomyManager.filterInput.placeholder": "Filter", + + "dashboard.taxonomyView.taxonomyTagging.pageTitle": "Map your content with: {0}", + "dashboard.taxonomyView.taxonomyTagging.checkbox": "Tag page with {0}", "dashboard.taxonomyView.taxonomyView.navigationBar.title": "Select the taxonomy", "dashboard.taxonomyView.taxonomyView.button.import": "Import taxonomy", @@ -661,9 +668,11 @@ "helpers.taxonomyHelper.createNew.input.placeholder": "Enter the value you want to add", "helpers.taxonomyHelper.createNew.input.validate.noValue": "A value must be provided.", "helpers.taxonomyHelper.createNew.input.validate.exists": "The value already exists.", + "helpers.taxonomyHelper.process.insert": "{0}: Inserting \"{1}\" to your selected pages.", "helpers.taxonomyHelper.process.edit": "{0}: Renaming \"{1}\" from {2} to {3}.", "helpers.taxonomyHelper.process.merge": "{0}: Merging \"{1}\" from {2} to {3}.", "helpers.taxonomyHelper.process.delete": "{0}: Deleting \"{1}\" from {2}.", + "helpers.taxonomyHelper.process.insert.success": "Insert completed.", "helpers.taxonomyHelper.process.edit.success": "Edit completed.", "helpers.taxonomyHelper.process.merge.success": "Merge completed.", "helpers.taxonomyHelper.process.delete.success": "Deletion completed.", diff --git a/package.json b/package.json index b56558ba..8957161e 100644 --- a/package.json +++ b/package.json @@ -2180,6 +2180,12 @@ } ], "menus": { + "webview/context": [ + { + "command": "workbench.action.webview.openDeveloperTools", + "when": "frontMatter:isDevelopment" + } + ], "editor/title": [ { "command": "frontMatter.markup.heading", diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts index a1286500..7b5001bb 100644 --- a/src/commands/Dashboard.ts +++ b/src/commands/Dashboard.ts @@ -7,7 +7,7 @@ import { } from '../constants'; import { join } from 'path'; import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from 'vscode'; -import { Logger, Settings as SettingsHelper } from '../helpers'; +import { DashboardSettings, Logger, Settings as SettingsHelper } from '../helpers'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; import { Extension } from '../helpers/Extension'; import { WebviewHelper } from '@estruyf/vscode'; @@ -160,6 +160,7 @@ export class Dashboard { Dashboard.isDisposed = true; Dashboard._viewData = undefined; PanelMediaListener.getMediaSelection(); + DashboardSettings.updateAfterClose(); await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, false); }); diff --git a/src/constants/context.ts b/src/constants/context.ts index 3fc67cda..9b68b2aa 100644 --- a/src/constants/context.ts +++ b/src/constants/context.ts @@ -6,6 +6,7 @@ export const CONTEXT = { wysiwyg: 'frontMatter:markdown:wysiwyg', backer: 'frontMatter:backers:supporter', isValidFile: 'frontMatter:file:isValid', + isDevelopment: 'frontMatter:isDevelopment', hasViewModes: 'frontMatter:has:modes', @@ -14,5 +15,5 @@ export const CONTEXT = { isGitEnabled: 'frontMatter:git:enabled', - projectSwitchEnabled: 'frontMatter:project:switch:enabled', + projectSwitchEnabled: 'frontMatter:project:switch:enabled' }; diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index 9c8a2f7a..ab53e96d 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -64,11 +64,13 @@ export enum DashboardMessage { createTaxonomy = 'createTaxonomy', importTaxonomy = 'importTaxonomy', moveTaxonomy = 'moveTaxonomy', + mapTaxonomy = 'mapTaxonomy', // Other getTheme = 'getTheme', updateSetting = 'updateSetting', setState = 'setState', + getState = 'getState', runCustomScript = 'runCustomScript', sendTelemetry = 'sendTelemetry', logError = 'logError', diff --git a/src/dashboardWebView/components/Common/Button.tsx b/src/dashboardWebView/components/Common/Button.tsx index 140d3f88..68384012 100644 --- a/src/dashboardWebView/components/Common/Button.tsx +++ b/src/dashboardWebView/components/Common/Button.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import useThemeColors from '../../hooks/useThemeColors'; export interface IButtonProps { secondary?: boolean; @@ -15,19 +14,14 @@ export const Button: React.FunctionComponent = ({ secondary, children }: React.PropsWithChildren) => { - const { getColors } = useThemeColors(); return ( + + + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/TaxonomyView/TaxonomyView.tsx b/src/dashboardWebView/components/TaxonomyView/TaxonomyView.tsx index 5e64f7bf..b92671a4 100644 --- a/src/dashboardWebView/components/TaxonomyView/TaxonomyView.tsx +++ b/src/dashboardWebView/components/TaxonomyView/TaxonomyView.tsx @@ -1,4 +1,4 @@ -import { Messenger } from '@estruyf/vscode/dist/client'; +import { Messenger, messageHandler } from '@estruyf/vscode/dist/client'; import { ChevronRightIcon, ArrowDownTrayIcon } from '@heroicons/react/24/outline'; import * as React from 'react'; import { useEffect, useState } from 'react'; @@ -6,7 +6,7 @@ import { useRecoilValue } from 'recoil'; import { TelemetryEvent } from '../../../constants'; import { TaxonomyData } from '../../../models'; import { DashboardMessage } from '../../DashboardMessage'; -import { Page } from '../../models'; +import { Page, PageMappings } from '../../models'; import { SettingsSelector } from '../../state'; import { NavigationBar, NavigationItem } from '../Layout'; import { PageLayout } from '../Layout/PageLayout'; @@ -14,6 +14,7 @@ import { SponsorMsg } from '../Layout/SponsorMsg'; import { TaxonomyManager } from './TaxonomyManager'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import { TaxonomyTagging } from './TaxonomyTagging'; export interface ITaxonomyViewProps { pages: Page[]; @@ -25,11 +26,24 @@ export const TaxonomyView: React.FunctionComponent = ({ const settings = useRecoilValue(SettingsSelector); const [taxonomySettings, setTaxonomySettings] = useState(); const [selectedTaxonomy, setSelectedTaxonomy] = useState(`tags`); + const [contentTagging, setContentTagging] = useState(null); const onImport = () => { Messenger.send(DashboardMessage.importTaxonomy); }; + const onContentMapping = React.useCallback((value: string, pageMappings: PageMappings) => { + messageHandler.request(DashboardMessage.mapTaxonomy, { + taxonomy: selectedTaxonomy, + value, + pageMappings + }).then(() => { + setContentTagging(null); + }).catch(() => { + setContentTagging(null); + }); + }, [selectedTaxonomy]); + useEffect(() => { setTaxonomySettings({ tags: settings?.tags || [], @@ -62,7 +76,10 @@ export const TaxonomyView: React.FunctionComponent = ({ > setSelectedTaxonomy(`tags`)} + onClick={() => { + setSelectedTaxonomy(`tags`); + setContentTagging(null); + }} > {l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyViewNavigationItemTags)} @@ -70,7 +87,10 @@ export const TaxonomyView: React.FunctionComponent = ({ setSelectedTaxonomy(`categories`)} + onClick={() => { + setSelectedTaxonomy(`categories`); + setContentTagging(null); + }} > {l10n.t(LocalizationKey.dashboardTaxonomyViewTaxonomyViewNavigationItemCategories)} @@ -81,7 +101,10 @@ export const TaxonomyView: React.FunctionComponent = ({ setSelectedTaxonomy(taxonomy.id)} + onClick={() => { + setSelectedTaxonomy(taxonomy.id); + setContentTagging(null); + }} > {taxonomy.id} @@ -90,10 +113,22 @@ export const TaxonomyView: React.FunctionComponent = ({
- + { + contentTagging ? ( + onContentMapping(value, pageMappings)} + onDismiss={() => setContentTagging(null)} /> + ) : ( + setContentTagging(value)} /> + ) + }
diff --git a/src/dashboardWebView/hooks/usePages.tsx b/src/dashboardWebView/hooks/usePages.tsx index 46640188..e3629e15 100644 --- a/src/dashboardWebView/hooks/usePages.tsx +++ b/src/dashboardWebView/hooks/usePages.tsx @@ -14,12 +14,13 @@ import { TabSelector, TagSelector } from '../state'; -import { SortOrder, SortType } from '../../models'; -import { Sorting } from '../../helpers/Sorting'; -import { Messenger } from '@estruyf/vscode/dist/client'; +import { Messenger, messageHandler } from '@estruyf/vscode/dist/client'; import { DashboardMessage } from '../DashboardMessage'; import { EventData } from '@estruyf/vscode/dist/models'; import { parseWinPath } from '../../helpers/parseWinPath'; +import { sortPages } from '../../utils/sortPages'; +import { ExtensionState } from '../../constants'; +import { SortingOption } from '../models'; export default function usePages(pages: Page[]) { const [pageItems, setPageItems] = useRecoilState(AllPagesAtom); @@ -37,7 +38,7 @@ export default function usePages(pages: Page[]) { * Process all the pages by applying the sorting, filtering and searching. */ const processPages = useCallback( - (searchedPages: Page[], fullProcess: boolean = true) => { + (searchedPages: Page[]) => { const framework = settings?.crntFramework; // Filter the pages @@ -67,35 +68,7 @@ export default function usePages(pages: Page[]) { // Sort the pages let pagesSorted: Page[] = Object.assign([], pagesToShow); if (!search) { - if (sorting && sorting.id === SortOption.FileNameAsc) { - pagesSorted = pagesSorted.sort(Sorting.alphabetically('fmFileName')); - } else if (sorting && sorting.id === SortOption.FileNameDesc) { - pagesSorted = pagesSorted.sort(Sorting.alphabetically('fmFileName')).reverse(); - } else if (sorting && sorting.id === SortOption.PublishedAsc) { - pagesSorted = pagesSorted.sort(Sorting.number('fmPublished')); - } else if (sorting && sorting.id === SortOption.LastModifiedAsc) { - pagesSorted = pagesSorted.sort(Sorting.number('fmModified')); - } else if (sorting && sorting.id === SortOption.PublishedDesc) { - pagesSorted = pagesSorted.sort(Sorting.number('fmPublished')).reverse(); - } else if (sorting && sorting.id === SortOption.LastModifiedDesc) { - pagesSorted = pagesSorted.sort(Sorting.number('fmModified')).reverse(); - } else if (sorting && sorting.id && sorting.name) { - const { order, name, type } = sorting; - - if (type === SortType.string) { - pagesSorted = pagesSorted.sort(Sorting.alphabetically(name)); - } else if (type === SortType.date) { - pagesSorted = pagesSorted.sort(Sorting.date(name)); - } else if (type === SortType.number) { - pagesSorted = pagesSorted.sort(Sorting.number(name)); - } - - if (order === SortOrder.desc) { - pagesSorted = pagesSorted.reverse(); - } - } else { - pagesSorted = pagesSorted.sort(Sorting.number('fmModified')).reverse(); - } + pagesSorted = sortPages(pagesSorted, sorting); } if (folder) { @@ -208,20 +181,27 @@ export default function usePages(pages: Page[]) { useEffect(() => { let usedSorting = sorting; - if (!usedSorting) { - const lastSort = settings?.dashboardState.contents.sorting; - if (lastSort) { - setSorting(lastSort); - return; + const startPageProcessing = () => { + // Check if search needs to be performed + let searchedPages = pages; + if (search) { + Messenger.send(DashboardMessage.searchPages, { query: search }); + } else { + processPages(searchedPages); } } - // Check if search needs to be performed - let searchedPages = pages; - if (search) { - Messenger.send(DashboardMessage.searchPages, { query: search }); + if (!usedSorting) { + messageHandler.request<{ key: string; value: SortingOption; }>(DashboardMessage.getState, { + key: ExtensionState.Dashboard.Contents.Sorting + }).then(({ key, value }) => { + if (key === ExtensionState.Dashboard.Contents.Sorting && value) { + setSorting(value); + return; + } + }); } else { - processPages(searchedPages); + startPageProcessing(); } }, [settings?.draftField, pages, sorting, search, tag, category, folder]); diff --git a/src/dashboardWebView/models/PageMappings.ts b/src/dashboardWebView/models/PageMappings.ts new file mode 100644 index 00000000..8c15023a --- /dev/null +++ b/src/dashboardWebView/models/PageMappings.ts @@ -0,0 +1,6 @@ +import { Page } from './Page'; + +export interface PageMappings { + tagged: Page[]; + untagged: Page[]; +} diff --git a/src/dashboardWebView/models/index.ts b/src/dashboardWebView/models/index.ts index 06a24ac9..d64691fa 100644 --- a/src/dashboardWebView/models/index.ts +++ b/src/dashboardWebView/models/index.ts @@ -1,6 +1,7 @@ export * from './DashboardViewType'; export * from './NavigationType'; export * from './Page'; +export * from './PageMappings'; export * from './Settings'; export * from './SortingOption'; export * from './Status'; diff --git a/src/dashboardWebView/styles.css b/src/dashboardWebView/styles.css index 7c7e3e30..b958fded 100644 --- a/src/dashboardWebView/styles.css +++ b/src/dashboardWebView/styles.css @@ -130,10 +130,10 @@ } input[type='submit'] { - @apply mt-4 inline-flex w-auto cursor-pointer items-center border border-transparent bg-teal-600 px-3 py-2 text-sm font-medium leading-4 text-white; + @apply mt-4 inline-flex w-auto items-center rounded border border-transparent bg-[var(--frontmatter-button-background)] px-3 py-2 text-sm font-medium leading-4 text-[var(--vscode-button-foreground)]; &:hover { - @apply bg-teal-700; + @apply bg-[var(--frontmatter-button-hoverBackground)]; } &:focus { @@ -141,7 +141,7 @@ } &:disabled { - @apply bg-gray-500 opacity-50; + @apply opacity-50; } } diff --git a/src/extension.ts b/src/extension.ts index f88b54d7..80c3b6a2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,6 +1,6 @@ import { GitListener } from './listeners/general/GitListener'; import * as vscode from 'vscode'; -import { COMMAND_NAME, EXTENSION_NAME, TelemetryEvent } from './constants'; +import { COMMAND_NAME, CONTEXT, EXTENSION_NAME, TelemetryEvent } from './constants'; import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider'; import { TagType } from './panelWebView/TagType'; import { PanelProvider } from './panelWebView/PanelProvider'; @@ -46,6 +46,11 @@ export async function activate(context: vscode.ExtensionContext) { const { subscriptions, extensionUri, extensionPath } = context; const extension = Extension.getInstance(context); + // Set development context + if (!Extension.getInstance().isProductionMode) { + vscode.commands.executeCommand('setContext', CONTEXT.isDevelopment, true); + } + Backers.init(context).then(() => {}); // Make sure the EN language file is loaded diff --git a/src/helpers/DashboardSettings.ts b/src/helpers/DashboardSettings.ts index 64e1d617..fc277e2c 100644 --- a/src/helpers/DashboardSettings.ts +++ b/src/helpers/DashboardSettings.ts @@ -59,6 +59,22 @@ export class DashboardSettings { return this.cachedSettings; } + public static async updateAfterClose() { + if (this.cachedSettings) { + this.cachedSettings = await this.getSettings(); + + const ext = Extension.getInstance(); + + // Update states + this.cachedSettings.dashboardState.contents.sorting = await ext.getState< + SortingOption | undefined + >(ExtensionState.Dashboard.Contents.Sorting, 'workspace'); + this.cachedSettings.dashboardState.media.sorting = await ext.getState< + SortingOption | undefined + >(ExtensionState.Dashboard.Media.Sorting, 'workspace'); + } + } + public static async getSettings() { const ext = Extension.getInstance(); const wsFolder = Folders.getWorkspaceFolder(); diff --git a/src/helpers/TaxonomyHelper.ts b/src/helpers/TaxonomyHelper.ts index 230584cf..b8dc5ad9 100644 --- a/src/helpers/TaxonomyHelper.ts +++ b/src/helpers/TaxonomyHelper.ts @@ -24,6 +24,8 @@ import { SettingsListener as PanelSettingsListener } from '../listeners/panel'; import { SettingsListener as DashboardSettingsListener } from '../listeners/dashboard'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../localization'; +import { Page } from '../dashboardWebView/models'; +import { Logger } from './Logger'; export class TaxonomyHelper { private static db: JsonDB; @@ -254,21 +256,26 @@ export class TaxonomyHelper { } /** - * Process the taxonomy changes - * @param type - * @param taxonomyType - * @param oldValue - * @param newValue - * @returns + * Processes the taxonomy changes based on the specified type. + * @param type - The type of taxonomy change ('insert', 'edit', 'merge', 'delete'). + * @param taxonomyType - The type of taxonomy ('Tag', 'Category', or custom taxonomy name). + * @param oldValue - The old value of the taxonomy. + * @param newValue - The new value of the taxonomy (optional). + * @param pages - The array of Page objects (optional). + * @param needsSettingsUpdate - Indicates whether the settings need to be updated (default: true). */ public static async process( - type: 'edit' | 'merge' | 'delete', + type: 'insert' | 'edit' | 'merge' | 'delete', taxonomyType: TaxonomyType | string, oldValue: string, - newValue?: string + newValue?: string, + pages?: Page[], + needsSettingsUpdate: boolean = true ) { // Retrieve all the markdown files - const allFiles = await FilesHelper.getAllFiles(); + const allFiles = pages + ? pages?.map((p) => ({ fsPath: p.fmFilePath })) + : await FilesHelper.getAllFiles(); if (!allFiles) { return; } @@ -284,7 +291,13 @@ export class TaxonomyHelper { let progressText = ``; - if (type === 'edit') { + if (type === 'insert') { + progressText = l10n.t( + LocalizationKey.helpersTaxonomyHelperProcessInsert, + EXTENSION_NAME, + newValue || '' + ); + } else if (type === 'edit') { progressText = l10n.t( LocalizationKey.helpersTaxonomyHelperProcessEdit, EXTENSION_NAME, @@ -309,7 +322,7 @@ export class TaxonomyHelper { ); } - window.withProgress( + await window.withProgress( { location: ProgressLocation.Notification, title: progressText, @@ -342,7 +355,18 @@ export class TaxonomyHelper { taxonomies = taxonomies.split(`,`); } - if (taxonomies && taxonomies.length > 0) { + let needsFileUpdate = false; + if (type === 'insert' && newValue) { + if (taxonomies && taxonomies.length > 0) { + taxonomies.push(newValue); + } else { + taxonomies = [newValue]; + } + + const newTaxValue = [...new Set(taxonomies)].sort(); + ContentType.setFieldValue(data, fieldNames, newTaxValue); + needsFileUpdate = true; + } else if (type !== 'insert' && taxonomies && taxonomies.length > 0) { const idx = taxonomies.findIndex((o) => o === oldValue); if (idx !== -1) { @@ -354,28 +378,36 @@ export class TaxonomyHelper { const newTaxValue = [...new Set(taxonomies)].sort(); ContentType.setFieldValue(data, fieldNames, newTaxValue); - - const spaces = window.activeTextEditor?.options?.tabSize; - // Update the file - await writeFileAsync( - parseWinPath(file.fsPath), - FrontMatterParser.toFile(article.content, article.data, mdFile, { - indent: spaces || 2 - } as DumpOptions as any), - { encoding: 'utf8' } - ); + needsFileUpdate = true; } } + + // Update the file when needed + if (needsFileUpdate) { + const spaces = window.activeTextEditor?.options?.tabSize; + + await writeFileAsync( + parseWinPath(file.fsPath), + FrontMatterParser.toFile(article.content, article.data, mdFile, { + indent: spaces || 2 + } as DumpOptions as any), + { encoding: 'utf8' } + ); + } } } catch (e) { - // Continue with the next file + Logger.error(`Failed to ${type} taxonomy value in ${file.fsPath}`); } } } - await this.addToSettings(taxonomyType, oldValue, newValue); + if (needsSettingsUpdate) { + await this.addToSettings(taxonomyType, oldValue, newValue); + } - if (type === 'edit') { + if (type === 'insert') { + Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessInsertSuccess)); + } else if (type === 'edit') { Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessEditSuccess)); } else if (type === 'merge') { Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessMergeSuccess)); @@ -502,6 +534,21 @@ export class TaxonomyHelper { ); } + /** + * Retrieve the taxonomy type based from the string + * @param taxonomyType + * @returns + */ + public static getTypeFromString(taxonomyType: string): TaxonomyType | string { + if (taxonomyType === 'tags') { + return TaxonomyType.Tag; + } else if (taxonomyType === 'categories') { + return TaxonomyType.Category; + } else { + return taxonomyType; + } + } + /** * Retrieve the fields for the taxonomy field * @returns @@ -587,19 +634,4 @@ export class TaxonomyHelper { return options; } - - /** - * Retrieve the taxonomy type based from the string - * @param taxonomyType - * @returns - */ - private static getTypeFromString(taxonomyType: string): TaxonomyType | string { - if (taxonomyType === 'tags') { - return TaxonomyType.Tag; - } else if (taxonomyType === 'categories') { - return TaxonomyType.Category; - } else { - return taxonomyType; - } - } } diff --git a/src/listeners/dashboard/ExtensionListener.ts b/src/listeners/dashboard/ExtensionListener.ts index af7b5eb5..9a594d1a 100644 --- a/src/listeners/dashboard/ExtensionListener.ts +++ b/src/listeners/dashboard/ExtensionListener.ts @@ -27,6 +27,9 @@ export class ExtensionListener extends BaseListener { case DashboardMessage.setState: this.setState(msg?.payload); break; + case DashboardMessage.getState: + this.getState(msg.command, msg?.payload, msg.requestId); + break; } } @@ -36,4 +39,17 @@ export class ExtensionListener extends BaseListener { Extension.getInstance().setState(key, value, 'workspace'); } } + + private static async getState(command: string, data: any, requestId?: string) { + if (!command || !requestId || !data) { + return; + } + + const { key } = data; + if (key) { + const value = await Extension.getInstance().getState(key, 'workspace'); + + this.sendRequest(command as any, requestId, { key, value }); + } + } } diff --git a/src/listeners/dashboard/TaxonomyListener.ts b/src/listeners/dashboard/TaxonomyListener.ts index 5df365a7..e566293e 100644 --- a/src/listeners/dashboard/TaxonomyListener.ts +++ b/src/listeners/dashboard/TaxonomyListener.ts @@ -5,6 +5,7 @@ import { DashboardMessage } from '../../dashboardWebView/DashboardMessage'; import { TaxonomyHelper } from '../../helpers'; import { PostMessageData } from '../../models'; import { BaseListener } from './BaseListener'; +import { Page, PageMappings } from '../../dashboardWebView/models'; export class TaxonomyListener extends BaseListener { /** @@ -39,6 +40,51 @@ export class TaxonomyListener extends BaseListener { case DashboardMessage.importTaxonomy: commands.executeCommand(COMMAND_NAME.exportTaxonomy); break; + case DashboardMessage.mapTaxonomy: + TaxonomyListener.mapTaxonomy(msg); + break; + } + } + + private static async mapTaxonomy({ + command, + requestId, + payload: { taxonomy, value, pageMappings } + }: { + command: string; + requestId?: string; + payload: { taxonomy: string; value: string; pageMappings: PageMappings }; + }) { + if (!command || !requestId || !taxonomy || !value || !pageMappings) { + return; + } + + try { + if (pageMappings.tagged.length > 0) { + await TaxonomyHelper.process( + 'insert', + TaxonomyHelper.getTypeFromString(taxonomy), + '', + value, + pageMappings.tagged, + false + ); + } + + if (pageMappings.untagged.length > 0) { + await TaxonomyHelper.process( + 'delete', + TaxonomyHelper.getTypeFromString(taxonomy), + value, + '', + pageMappings.untagged, + false + ); + } + + this.sendRequest(command as any, requestId, {}); + } catch (e) { + this.sendError(command as any, requestId, e); } } diff --git a/src/localization/localization.enum.ts b/src/localization/localization.enum.ts index f6d4d2a6..c8062bd1 100644 --- a/src/localization/localization.enum.ts +++ b/src/localization/localization.enum.ts @@ -15,6 +15,10 @@ export enum LocalizationKey { * Cancel */ commonCancel = 'common.cancel', + /** + * Apply + */ + commonApply = 'common.apply', /** * Clear */ @@ -83,6 +87,10 @@ export enum LocalizationKey { * Remove {0} */ commonRemoveValue = 'common.remove.value', + /** + * Back + */ + commonFilter = 'common.filter', /** * Filter by {0} */ @@ -131,6 +139,10 @@ export enum LocalizationKey { * Open settings */ commonOpenSettings = 'common.openSettings', + /** + * Back + */ + commonBack = 'common.back', /** * output window */ @@ -975,6 +987,18 @@ export enum LocalizationKey { * Missing in your settings */ dashboardTaxonomyViewTaxonomyManagerTableUnmappedTitle = 'dashboard.taxonomyView.taxonomyManager.table.unmapped.title', + /** + * Filter + */ + dashboardTaxonomyViewTaxonomyManagerFilterInputPlaceholder = 'dashboard.taxonomyView.taxonomyManager.filterInput.placeholder', + /** + * Map your content with: {0} + */ + dashboardTaxonomyViewTaxonomyTaggingPageTitle = 'dashboard.taxonomyView.taxonomyTagging.pageTitle', + /** + * Tag page with {0} + */ + dashboardTaxonomyViewTaxonomyTaggingCheckbox = 'dashboard.taxonomyView.taxonomyTagging.checkbox', /** * Select the taxonomy */ @@ -2172,6 +2196,10 @@ export enum LocalizationKey { * The value already exists. */ helpersTaxonomyHelperCreateNewInputValidateExists = 'helpers.taxonomyHelper.createNew.input.validate.exists', + /** + * {0}: Inserting "{1}" to your selected pages. + */ + helpersTaxonomyHelperProcessInsert = 'helpers.taxonomyHelper.process.insert', /** * {0}: Renaming "{1}" from {2} to {3}. */ @@ -2184,6 +2212,10 @@ export enum LocalizationKey { * {0}: Deleting "{1}" from {2}. */ helpersTaxonomyHelperProcessDelete = 'helpers.taxonomyHelper.process.delete', + /** + * Insert completed. + */ + helpersTaxonomyHelperProcessInsertSuccess = 'helpers.taxonomyHelper.process.insert.success', /** * Edit completed. */ diff --git a/src/utils/sortPages.ts b/src/utils/sortPages.ts new file mode 100644 index 00000000..81d1a2b3 --- /dev/null +++ b/src/utils/sortPages.ts @@ -0,0 +1,38 @@ +import { SortOption } from '../dashboardWebView/constants/SortOption'; +import { Page, SortingOption } from '../dashboardWebView/models'; +import { Sorting } from '../helpers/Sorting'; +import { SortOrder, SortType } from '../models'; + +export const sortPages = (pages: Page[], sorting: SortingOption | null) => { + if (sorting && sorting.id === SortOption.FileNameAsc) { + pages = pages.sort(Sorting.alphabetically('fmFileName')); + } else if (sorting && sorting.id === SortOption.FileNameDesc) { + pages = pages.sort(Sorting.alphabetically('fmFileName')).reverse(); + } else if (sorting && sorting.id === SortOption.PublishedAsc) { + pages = pages.sort(Sorting.number('fmPublished')); + } else if (sorting && sorting.id === SortOption.LastModifiedAsc) { + pages = pages.sort(Sorting.number('fmModified')); + } else if (sorting && sorting.id === SortOption.PublishedDesc) { + pages = pages.sort(Sorting.number('fmPublished')).reverse(); + } else if (sorting && sorting.id === SortOption.LastModifiedDesc) { + pages = pages.sort(Sorting.number('fmModified')).reverse(); + } else if (sorting && sorting.id && sorting.name) { + const { order, name, type } = sorting; + + if (type === SortType.string) { + pages = pages.sort(Sorting.alphabetically(name)); + } else if (type === SortType.date) { + pages = pages.sort(Sorting.date(name)); + } else if (type === SortType.number) { + pages = pages.sort(Sorting.number(name)); + } + + if (order === SortOrder.desc) { + pages = pages.reverse(); + } + } else { + pages = pages.sort(Sorting.number('fmModified')).reverse(); + } + + return pages; +};