mirror of
https://github.com/devonthesofa/obsidian-note-status.git
synced 2026-07-22 05:45:04 +00:00
feat: status scoped by template
This commit is contained in:
parent
595b76e081
commit
ddcbb47bb0
12 changed files with 403 additions and 112 deletions
|
|
@ -26,7 +26,7 @@ export const CurrentStatusChips: React.FC<Props> = ({
|
|||
>
|
||||
{currentStatuses.map((status) => (
|
||||
<StatusDisplay
|
||||
key={status.name}
|
||||
key={`${status.templateId || "custom"}:${status.name}`}
|
||||
status={status}
|
||||
variant="chip"
|
||||
removable
|
||||
|
|
|
|||
|
|
@ -22,11 +22,16 @@ export const useKeyboardNavigation = ({
|
|||
const filteredStatuses = useMemo(
|
||||
() =>
|
||||
searchFilter
|
||||
? availableStatuses.filter((status) =>
|
||||
status.name
|
||||
.toLowerCase()
|
||||
.includes(searchFilter.toLowerCase()),
|
||||
)
|
||||
? availableStatuses.filter((status) => {
|
||||
const searchTerm = searchFilter.toLowerCase();
|
||||
return (
|
||||
status.name.toLowerCase().includes(searchTerm) ||
|
||||
(status.templateId &&
|
||||
status.templateId
|
||||
.toLowerCase()
|
||||
.includes(searchTerm))
|
||||
);
|
||||
})
|
||||
: availableStatuses,
|
||||
[searchFilter, availableStatuses],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { PluginSettings } from "@/types/pluginSettings";
|
||||
import { UISettings } from "./UISettings";
|
||||
import { PREDEFINED_TEMPLATES } from "@/constants/defaultSettings";
|
||||
import { TemplateSettings } from "./TemplateSettings";
|
||||
import { BehaviourSettings } from "./BehaviourSettings";
|
||||
import { CustomStatusSettings } from "./CustomStatusSettings";
|
||||
import { QuickCommandsSettings } from "./QuickCommandsSettings";
|
||||
import { PREDEFINED_TEMPLATES } from "@/constants/predefinedTemplates";
|
||||
|
||||
export type Props = {
|
||||
settings: PluginSettings;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@ export const StatusDisplay: FC<StatusDisplayProps> = memo(
|
|||
({ status, variant, removable = false, onRemove, onClick }) => {
|
||||
const [isRemoving, setIsRemoving] = useState(false);
|
||||
|
||||
const getDisplayName = () => {
|
||||
return status.templateId
|
||||
? `${status.name} (${status.templateId})`
|
||||
: status.name;
|
||||
};
|
||||
|
||||
const handleRemove = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (onRemove) {
|
||||
|
|
@ -55,7 +61,9 @@ export const StatusDisplay: FC<StatusDisplayProps> = memo(
|
|||
<span className="note-status-chip-icon">
|
||||
{status.icon ? status.icon : "📝"}
|
||||
</span>
|
||||
<span className="note-status-chip-text">{status.name}</span>
|
||||
<span className="note-status-chip-text">
|
||||
{getDisplayName()}
|
||||
</span>
|
||||
{removable && (
|
||||
<div
|
||||
className="note-status-chip-remove"
|
||||
|
|
@ -104,7 +112,9 @@ export const StatusDisplay: FC<StatusDisplayProps> = memo(
|
|||
<span className="status-badge-icon">
|
||||
{status.icon ? status.icon : "📝"}
|
||||
</span>
|
||||
<span className="status-badge-text">{status.name}</span>
|
||||
<span className="status-badge-text">
|
||||
{getDisplayName()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -122,7 +132,7 @@ export const StatusDisplay: FC<StatusDisplayProps> = memo(
|
|||
}
|
||||
/>
|
||||
<span>
|
||||
{status.icon ? status.icon : "📝"} {status.name}
|
||||
{status.icon ? status.icon : "📝"} {getDisplayName()}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ interface StatusOptionProps {
|
|||
|
||||
export const StatusModalOption: React.FC<StatusOptionProps> = memo(
|
||||
({ status, isSelected, isFocused, onSelect }) => {
|
||||
const displayName = status.templateId
|
||||
? `${status.name} (${status.templateId})`
|
||||
: status.name;
|
||||
|
||||
return (
|
||||
<SelectableListItem
|
||||
selected={isSelected}
|
||||
|
|
@ -23,7 +27,7 @@ export const StatusModalOption: React.FC<StatusOptionProps> = memo(
|
|||
status.description ? getStatusTooltip(status) : undefined
|
||||
}
|
||||
>
|
||||
{status.name}
|
||||
{displayName}
|
||||
</SelectableListItem>
|
||||
);
|
||||
},
|
||||
|
|
@ -63,7 +67,7 @@ export const StatusSelector: React.FC<Props> = ({
|
|||
>
|
||||
{availableStatuses.map((status, index) => (
|
||||
<StatusModalOption
|
||||
key={`${status.name}${status.description}${status.color}${status.icon}`}
|
||||
key={`${status.templateId || "custom"}:${status.name}:${status.description}:${status.color}:${status.icon}`}
|
||||
status={status}
|
||||
isSelected={isStatusSelected(status, currentStatuses)}
|
||||
isFocused={index === focusedIndex}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,5 @@
|
|||
import { PluginSettings, StatusTemplate } from "types/pluginSettings";
|
||||
|
||||
export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
||||
{
|
||||
id: "colorful",
|
||||
name: "Colorful workflow",
|
||||
description:
|
||||
"A colorful set of workflow statuses with descriptive icons",
|
||||
statuses: [
|
||||
{ name: "idea", icon: "💡", color: "#FFEB3B" },
|
||||
{ name: "draft", icon: "📝", color: "#E0E0E0" },
|
||||
{ name: "inProgress", icon: "🔧", color: "#FFC107" },
|
||||
{ name: "editing", icon: "🖊️", color: "#2196F3" },
|
||||
{ name: "pending", icon: "⏳", color: "#9C27B0" },
|
||||
{ name: "onHold", icon: "⏸", color: "#9E9E9E" },
|
||||
{ name: "needsUpdate", icon: "🔄", color: "#FF5722" },
|
||||
{ name: "completed", icon: "✅", color: "#4CAF50" },
|
||||
{ name: "archived", icon: "📦", color: "#795548" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "minimal",
|
||||
name: "Minimal workflow",
|
||||
description: "A simplified set of essential workflow statuses",
|
||||
statuses: [
|
||||
{ name: "todo", icon: "📌", color: "#F44336" },
|
||||
{ name: "inProgress", icon: "⚙️", color: "#2196F3" },
|
||||
{ name: "review", icon: "👀", color: "#9C27B0" },
|
||||
{ name: "done", icon: "✓", color: "#4CAF50" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "academic",
|
||||
name: "Academic research",
|
||||
description: "Status workflow for academic research and writing",
|
||||
statuses: [
|
||||
{ name: "research", icon: "🔍", color: "#2196F3" },
|
||||
{ name: "outline", icon: "📑", color: "#9E9E9E" },
|
||||
{ name: "draft", icon: "✏️", color: "#FFC107" },
|
||||
{ name: "review", icon: "🔬", color: "#9C27B0" },
|
||||
{ name: "revision", icon: "📝", color: "#FF5722" },
|
||||
{ name: "final", icon: "📚", color: "#4CAF50" },
|
||||
{ name: "published", icon: "🎓", color: "#795548" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "project",
|
||||
name: "Project management",
|
||||
description: "Status workflow for project management and tracking",
|
||||
statuses: [
|
||||
{ name: "planning", icon: "🗓️", color: "#9E9E9E" },
|
||||
{ name: "backlog", icon: "📋", color: "#E0E0E0" },
|
||||
{ name: "ready", icon: "🚦", color: "#8BC34A" },
|
||||
{ name: "inDevelopment", icon: "👨💻", color: "#2196F3" },
|
||||
{ name: "testing", icon: "🧪", color: "#9C27B0" },
|
||||
{ name: "review", icon: "👁️", color: "#FFC107" },
|
||||
{ name: "approved", icon: "👍", color: "#4CAF50" },
|
||||
{ name: "live", icon: "🚀", color: "#3F51B5" },
|
||||
],
|
||||
},
|
||||
];
|
||||
import { PluginSettings } from "types/pluginSettings";
|
||||
import { DEFAULT_ENABLED_TEMPLATES } from "./predefinedTemplates";
|
||||
|
||||
export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = {
|
||||
fileExplorerIconPosition: "absolute-right",
|
||||
|
|
@ -74,7 +15,7 @@ export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = {
|
|||
customStatuses: [],
|
||||
showStatusIconsInExplorer: true,
|
||||
hideUnknownStatusInExplorer: true, // Default to hide unknown status
|
||||
enabledTemplates: [PREDEFINED_TEMPLATES[0].id],
|
||||
enabledTemplates: DEFAULT_ENABLED_TEMPLATES,
|
||||
useCustomStatusesOnly: false,
|
||||
useMultipleStatuses: true,
|
||||
tagPrefix: "obsidian-note-status",
|
||||
|
|
|
|||
209
constants/predefinedTemplates.ts
Normal file
209
constants/predefinedTemplates.ts
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import { StatusTemplate } from "types/pluginSettings";
|
||||
|
||||
/**
|
||||
* Predefined status templates
|
||||
*/
|
||||
export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
||||
{
|
||||
id: "colorful",
|
||||
name: "Colorful workflow",
|
||||
description:
|
||||
"A colorful set of workflow statuses with descriptive icons",
|
||||
statuses: [
|
||||
{
|
||||
name: "idea",
|
||||
icon: "💡",
|
||||
color: "#FFEB3B",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "draft",
|
||||
icon: "📝",
|
||||
color: "#E0E0E0",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "inProgress",
|
||||
icon: "🔧",
|
||||
color: "#FFC107",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "editing",
|
||||
icon: "🖊️",
|
||||
color: "#2196F3",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "pending",
|
||||
icon: "⏳",
|
||||
color: "#9C27B0",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "onHold",
|
||||
icon: "⏸",
|
||||
color: "#9E9E9E",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "needsUpdate",
|
||||
icon: "🔄",
|
||||
color: "#FF5722",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "completed",
|
||||
icon: "✅",
|
||||
color: "#4CAF50",
|
||||
templateId: "colorful",
|
||||
},
|
||||
{
|
||||
name: "archived",
|
||||
icon: "📦",
|
||||
color: "#795548",
|
||||
templateId: "colorful",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "minimal",
|
||||
name: "Minimal workflow",
|
||||
description: "A simplified set of essential workflow statuses",
|
||||
statuses: [
|
||||
{
|
||||
name: "todo",
|
||||
icon: "📌",
|
||||
color: "#F44336",
|
||||
templateId: "minimal",
|
||||
},
|
||||
{
|
||||
name: "inProgress",
|
||||
icon: "⚙️",
|
||||
color: "#2196F3",
|
||||
templateId: "minimal",
|
||||
},
|
||||
{
|
||||
name: "review",
|
||||
icon: "👀",
|
||||
color: "#9C27B0",
|
||||
templateId: "minimal",
|
||||
},
|
||||
{
|
||||
name: "done",
|
||||
icon: "✓",
|
||||
color: "#4CAF50",
|
||||
templateId: "minimal",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "academic",
|
||||
name: "Academic research",
|
||||
description: "Status workflow for academic research and writing",
|
||||
statuses: [
|
||||
{
|
||||
name: "research",
|
||||
icon: "🔍",
|
||||
color: "#2196F3",
|
||||
templateId: "academic",
|
||||
},
|
||||
{
|
||||
name: "outline",
|
||||
icon: "📑",
|
||||
color: "#9E9E9E",
|
||||
templateId: "academic",
|
||||
},
|
||||
{
|
||||
name: "draft",
|
||||
icon: "✏️",
|
||||
color: "#FFC107",
|
||||
templateId: "academic",
|
||||
},
|
||||
{
|
||||
name: "review",
|
||||
icon: "🔬",
|
||||
color: "#9C27B0",
|
||||
templateId: "academic",
|
||||
},
|
||||
{
|
||||
name: "revision",
|
||||
icon: "📝",
|
||||
color: "#FF5722",
|
||||
templateId: "academic",
|
||||
},
|
||||
{
|
||||
name: "final",
|
||||
icon: "📚",
|
||||
color: "#4CAF50",
|
||||
templateId: "academic",
|
||||
},
|
||||
{
|
||||
name: "published",
|
||||
icon: "🎓",
|
||||
color: "#795548",
|
||||
templateId: "academic",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "project",
|
||||
name: "Project management",
|
||||
description: "Status workflow for project management and tracking",
|
||||
statuses: [
|
||||
{
|
||||
name: "planning",
|
||||
icon: "🗓️",
|
||||
color: "#9E9E9E",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "backlog",
|
||||
icon: "📋",
|
||||
color: "#E0E0E0",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "ready",
|
||||
icon: "🚦",
|
||||
color: "#8BC34A",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "inDevelopment",
|
||||
icon: "👨💻",
|
||||
color: "#2196F3",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "testing",
|
||||
icon: "🧪",
|
||||
color: "#9C27B0",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "review",
|
||||
icon: "👁️",
|
||||
color: "#FFC107",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "approved",
|
||||
icon: "👍",
|
||||
color: "#4CAF50",
|
||||
templateId: "project",
|
||||
},
|
||||
{
|
||||
name: "live",
|
||||
icon: "🚀",
|
||||
color: "#3F51B5",
|
||||
templateId: "project",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Default template IDs that should be enabled by default
|
||||
*/
|
||||
export const DEFAULT_ENABLED_TEMPLATES = ["colorful"];
|
||||
|
|
@ -59,9 +59,7 @@ export class CommandsService {
|
|||
!settingsService.settings.useMultipleStatuses
|
||||
) {
|
||||
const allStatuses =
|
||||
BaseNoteStatusService.getAllAvailableStatuses().map(
|
||||
(s) => s.name,
|
||||
);
|
||||
BaseNoteStatusService.getAllAvailableStatuses();
|
||||
|
||||
if (allStatuses.length === 0) {
|
||||
new Notice("No statuses available");
|
||||
|
|
@ -80,7 +78,9 @@ export class CommandsService {
|
|||
: undefined;
|
||||
let nextIndex = 0;
|
||||
if (currentStatus) {
|
||||
const currentIndex = allStatuses.indexOf(currentStatus);
|
||||
const currentIndex = allStatuses.findIndex(
|
||||
(s) => s.name === currentStatus,
|
||||
);
|
||||
if (currentIndex !== -1) {
|
||||
nextIndex =
|
||||
currentIndex === -1
|
||||
|
|
@ -89,15 +89,21 @@ export class CommandsService {
|
|||
}
|
||||
}
|
||||
|
||||
const nextStatus = allStatuses[nextIndex];
|
||||
const scopedIdentifier = nextStatus.templateId
|
||||
? BaseNoteStatusService.formatStatusIdentifier({
|
||||
templateId: nextStatus.templateId,
|
||||
name: nextStatus.name,
|
||||
})
|
||||
: nextStatus.name;
|
||||
|
||||
statusService
|
||||
.addStatus(
|
||||
settingsService.settings.tagPrefix,
|
||||
allStatuses[nextIndex],
|
||||
scopedIdentifier,
|
||||
)
|
||||
.then((resolve) => {
|
||||
new Notice(
|
||||
`Status changed to ${allStatuses[nextIndex]}`,
|
||||
);
|
||||
new Notice(`Status changed to ${nextStatus.name}`);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
|
@ -250,10 +256,17 @@ export class CommandsService {
|
|||
!settingsService.settings.useMultipleStatuses
|
||||
) {
|
||||
const statusService = this.createStatusService(file);
|
||||
const scopedIdentifier = status.templateId
|
||||
? BaseNoteStatusService.formatStatusIdentifier({
|
||||
templateId: status.templateId,
|
||||
name: status.name,
|
||||
})
|
||||
: status.name;
|
||||
|
||||
statusService
|
||||
.overrideStatuses(
|
||||
settingsService.settings.tagPrefix,
|
||||
[statusName],
|
||||
[scopedIdentifier],
|
||||
)
|
||||
.then(() => {
|
||||
new Notice(`Status set to ${statusName}`);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ import {
|
|||
GroupedStatuses,
|
||||
NoteStatus,
|
||||
NoteStatus as NoteStatusType,
|
||||
StatusIdentifier,
|
||||
ScopedStatusName,
|
||||
} from "@/types/noteStatus";
|
||||
import { PREDEFINED_TEMPLATES } from "@/constants/defaultSettings";
|
||||
import settingsService from "@/core/settingsService";
|
||||
import eventBus from "./eventBus";
|
||||
import { PREDEFINED_TEMPLATES } from "@/constants/predefinedTemplates";
|
||||
|
||||
export abstract class BaseNoteStatusService {
|
||||
static app: App;
|
||||
|
|
@ -20,6 +22,44 @@ export abstract class BaseNoteStatusService {
|
|||
BaseNoteStatusService.app = app;
|
||||
}
|
||||
|
||||
static parseStatusIdentifier(
|
||||
identifier: StatusIdentifier,
|
||||
): ScopedStatusName {
|
||||
if (typeof identifier === "string") {
|
||||
if (identifier.includes(":")) {
|
||||
const [templateId, name] = identifier.split(":", 2);
|
||||
return { templateId, name };
|
||||
}
|
||||
return { name: identifier };
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
static formatStatusIdentifier(scopedName: ScopedStatusName): string {
|
||||
if (scopedName.templateId) {
|
||||
return `${scopedName.templateId}:${scopedName.name}`;
|
||||
}
|
||||
return scopedName.name;
|
||||
}
|
||||
|
||||
static resolveStatusFromIdentifier(
|
||||
identifier: StatusIdentifier,
|
||||
): NoteStatus | undefined {
|
||||
const parsed = BaseNoteStatusService.parseStatusIdentifier(identifier);
|
||||
const availableStatuses =
|
||||
BaseNoteStatusService.getAllAvailableStatuses();
|
||||
|
||||
if (parsed.templateId) {
|
||||
return availableStatuses.find(
|
||||
(s) =>
|
||||
s.name === parsed.name &&
|
||||
s.templateId === parsed.templateId,
|
||||
);
|
||||
}
|
||||
|
||||
return availableStatuses.find((s) => s.name === parsed.name);
|
||||
}
|
||||
|
||||
private static allEnabledTemplatesStatuses(): NoteStatusType[] {
|
||||
if (settingsService.settings.useCustomStatusesOnly) {
|
||||
return [];
|
||||
|
|
@ -53,11 +93,13 @@ export abstract class BaseNoteStatusService {
|
|||
// );
|
||||
// }
|
||||
|
||||
protected statusNameToObject(statusName: NoteStatusType["name"]) {
|
||||
const availableStatuses =
|
||||
BaseNoteStatusService.getAllAvailableStatuses();
|
||||
const s = availableStatuses.find((f) => f.name === statusName);
|
||||
return s;
|
||||
protected statusNameToObject(statusName: string | StatusIdentifier) {
|
||||
if (typeof statusName === "string") {
|
||||
return BaseNoteStatusService.resolveStatusFromIdentifier(
|
||||
statusName,
|
||||
);
|
||||
}
|
||||
return BaseNoteStatusService.resolveStatusFromIdentifier(statusName);
|
||||
}
|
||||
|
||||
protected getStatusMetadataKeys(): string[] {
|
||||
|
|
@ -71,7 +113,7 @@ export abstract class BaseNoteStatusService {
|
|||
): Promise<boolean>;
|
||||
abstract addStatus(
|
||||
frontmatterTagName: string,
|
||||
statusIdentifier: NoteStatus["name"],
|
||||
statusIdentifier: StatusIdentifier,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +171,13 @@ export class NoteStatusService extends BaseNoteStatusService {
|
|||
status: NoteStatus,
|
||||
): Promise<boolean> {
|
||||
let removed = false;
|
||||
const targetIdentifier = status.templateId
|
||||
? BaseNoteStatusService.formatStatusIdentifier({
|
||||
templateId: status.templateId,
|
||||
name: status.name,
|
||||
})
|
||||
: status.name;
|
||||
|
||||
await BaseNoteStatusService.app.fileManager.processFrontMatter(
|
||||
this.file,
|
||||
(frontmatter) => {
|
||||
|
|
@ -138,7 +187,7 @@ export class NoteStatusService extends BaseNoteStatusService {
|
|||
|
||||
if (Array.isArray(noteStatusFrontmatter)) {
|
||||
const i = noteStatusFrontmatter.findIndex(
|
||||
(statusName: string) => statusName === status.name,
|
||||
(statusName: string) => statusName === targetIdentifier,
|
||||
);
|
||||
if (i !== -1) {
|
||||
noteStatusFrontmatter.splice(i, 1);
|
||||
|
|
@ -171,8 +220,13 @@ export class NoteStatusService extends BaseNoteStatusService {
|
|||
|
||||
async overrideStatuses(
|
||||
frontmatterTagName: string,
|
||||
statusIdentifiers: NoteStatus["name"][],
|
||||
statusIdentifiers: StatusIdentifier[],
|
||||
): Promise<boolean> {
|
||||
const formattedIdentifiers = statusIdentifiers.map((id) =>
|
||||
typeof id === "string"
|
||||
? id
|
||||
: BaseNoteStatusService.formatStatusIdentifier(id),
|
||||
);
|
||||
await BaseNoteStatusService.app.fileManager.processFrontMatter(
|
||||
this.file,
|
||||
(frontmatter) => {
|
||||
|
|
@ -181,9 +235,11 @@ export class NoteStatusService extends BaseNoteStatusService {
|
|||
Array.isArray(frontmatter[frontmatterTagName])
|
||||
) {
|
||||
frontmatter[frontmatterTagName].splice(0);
|
||||
frontmatter[frontmatterTagName].push(...statusIdentifiers);
|
||||
frontmatter[frontmatterTagName].push(
|
||||
...formattedIdentifiers,
|
||||
);
|
||||
}
|
||||
frontmatter[frontmatterTagName] = [...statusIdentifiers];
|
||||
frontmatter[frontmatterTagName] = [...formattedIdentifiers];
|
||||
},
|
||||
);
|
||||
eventBus.publish("frontmatter-manually-changed", { file: this.file });
|
||||
|
|
@ -192,16 +248,23 @@ export class NoteStatusService extends BaseNoteStatusService {
|
|||
|
||||
async addStatus(
|
||||
frontmatterTagName: string,
|
||||
statusIdentifier: NoteStatus["name"],
|
||||
statusIdentifier: StatusIdentifier,
|
||||
): Promise<boolean> {
|
||||
let added = false;
|
||||
const formattedIdentifier =
|
||||
typeof statusIdentifier === "string"
|
||||
? statusIdentifier
|
||||
: BaseNoteStatusService.formatStatusIdentifier(
|
||||
statusIdentifier,
|
||||
);
|
||||
|
||||
await BaseNoteStatusService.app.fileManager.processFrontMatter(
|
||||
this.file,
|
||||
(frontmatter) => {
|
||||
const noteStatusFrontmatter =
|
||||
(frontmatter?.[frontmatterTagName] as string[]) || [];
|
||||
if (!settingsService.settings.useMultipleStatuses) {
|
||||
frontmatter[frontmatterTagName] = [statusIdentifier];
|
||||
frontmatter[frontmatterTagName] = [formattedIdentifier];
|
||||
added = true;
|
||||
} else {
|
||||
// Ensure frontmatter property exists as an array
|
||||
|
|
@ -213,10 +276,13 @@ export class NoteStatusService extends BaseNoteStatusService {
|
|||
}
|
||||
|
||||
const i = noteStatusFrontmatter.findIndex(
|
||||
(statusName: string) => statusName === statusIdentifier,
|
||||
(statusName: string) =>
|
||||
statusName === formattedIdentifier,
|
||||
);
|
||||
if (i === -1) {
|
||||
frontmatter[frontmatterTagName].push(statusIdentifier);
|
||||
frontmatter[frontmatterTagName].push(
|
||||
formattedIdentifier,
|
||||
);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -290,6 +356,12 @@ export class MultipleNoteStatusService extends BaseNoteStatusService {
|
|||
status: NoteStatus,
|
||||
): Promise<boolean> {
|
||||
let removedFromAny = false;
|
||||
const targetIdentifier = status.templateId
|
||||
? BaseNoteStatusService.formatStatusIdentifier({
|
||||
templateId: status.templateId,
|
||||
name: status.name,
|
||||
})
|
||||
: status.name;
|
||||
|
||||
const promises = this.files.map(async (file) => {
|
||||
let removed = false;
|
||||
|
|
@ -302,13 +374,14 @@ export class MultipleNoteStatusService extends BaseNoteStatusService {
|
|||
|
||||
if (Array.isArray(noteStatusFrontmatter)) {
|
||||
const index = noteStatusFrontmatter.findIndex(
|
||||
(statusName: string) => statusName === status.name,
|
||||
(statusName: string) =>
|
||||
statusName === targetIdentifier,
|
||||
);
|
||||
if (index !== -1) {
|
||||
noteStatusFrontmatter.splice(index, 1);
|
||||
removed = true;
|
||||
}
|
||||
} else if (noteStatusFrontmatter === status.name) {
|
||||
} else if (noteStatusFrontmatter === targetIdentifier) {
|
||||
delete frontmatter[frontmatterTagName];
|
||||
removed = true;
|
||||
}
|
||||
|
|
@ -331,9 +404,15 @@ export class MultipleNoteStatusService extends BaseNoteStatusService {
|
|||
|
||||
async addStatus(
|
||||
frontmatterTagName: string,
|
||||
statusIdentifier: NoteStatus["name"],
|
||||
statusIdentifier: StatusIdentifier,
|
||||
): Promise<boolean> {
|
||||
let addedToAny = false;
|
||||
const formattedIdentifier =
|
||||
typeof statusIdentifier === "string"
|
||||
? statusIdentifier
|
||||
: BaseNoteStatusService.formatStatusIdentifier(
|
||||
statusIdentifier,
|
||||
);
|
||||
|
||||
const promises = this.files.map(async (file) => {
|
||||
let added = false;
|
||||
|
|
@ -344,18 +423,20 @@ export class MultipleNoteStatusService extends BaseNoteStatusService {
|
|||
frontmatter[frontmatterTagName];
|
||||
|
||||
if (!noteStatusFrontmatter) {
|
||||
frontmatter[frontmatterTagName] = [statusIdentifier];
|
||||
frontmatter[frontmatterTagName] = [formattedIdentifier];
|
||||
added = true;
|
||||
} else if (Array.isArray(noteStatusFrontmatter)) {
|
||||
if (!noteStatusFrontmatter.includes(statusIdentifier)) {
|
||||
noteStatusFrontmatter.push(statusIdentifier);
|
||||
if (
|
||||
!noteStatusFrontmatter.includes(formattedIdentifier)
|
||||
) {
|
||||
noteStatusFrontmatter.push(formattedIdentifier);
|
||||
added = true;
|
||||
}
|
||||
} else {
|
||||
if (noteStatusFrontmatter !== statusIdentifier) {
|
||||
if (noteStatusFrontmatter !== formattedIdentifier) {
|
||||
frontmatter[frontmatterTagName] = [
|
||||
noteStatusFrontmatter,
|
||||
statusIdentifier,
|
||||
formattedIdentifier,
|
||||
];
|
||||
added = true;
|
||||
}
|
||||
|
|
@ -381,6 +462,13 @@ export class MultipleNoteStatusService extends BaseNoteStatusService {
|
|||
frontmatterTagName: string,
|
||||
status: NoteStatus,
|
||||
): TFile[] {
|
||||
const targetIdentifier = status.templateId
|
||||
? BaseNoteStatusService.formatStatusIdentifier({
|
||||
templateId: status.templateId,
|
||||
name: status.name,
|
||||
})
|
||||
: status.name;
|
||||
|
||||
return this.files.filter((file) => {
|
||||
const cachedMetadata =
|
||||
BaseNoteStatusService.app.metadataCache.getFileCache(file);
|
||||
|
|
@ -391,9 +479,9 @@ export class MultipleNoteStatusService extends BaseNoteStatusService {
|
|||
if (!value) return false;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.includes(status.name);
|
||||
return value.includes(targetIdentifier);
|
||||
}
|
||||
return value === status.name;
|
||||
return value === targetIdentifier;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import SelectorService from "@/core/selectorService";
|
|||
import {
|
||||
MultipleNoteStatusService,
|
||||
NoteStatusService,
|
||||
BaseNoteStatusService,
|
||||
} from "@/core/noteStatusService";
|
||||
import eventBus from "@/core/eventBus";
|
||||
|
||||
|
|
@ -69,9 +70,16 @@ export class StatusModalIntegration extends Modal {
|
|||
frontmatterTagName,
|
||||
status,
|
||||
) => {
|
||||
const scopedIdentifier = status.templateId
|
||||
? BaseNoteStatusService.formatStatusIdentifier({
|
||||
templateId: status.templateId,
|
||||
name: status.name,
|
||||
})
|
||||
: status.name;
|
||||
|
||||
const added = await this.selectorService.noteStatusService.addStatus(
|
||||
frontmatterTagName,
|
||||
status.name,
|
||||
scopedIdentifier,
|
||||
);
|
||||
if (added) {
|
||||
new Notice(`Status ${status.name} added successfully to the note`);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,15 @@ export type NoteStatus = {
|
|||
icon: string;
|
||||
color?: string; // Optional color property
|
||||
description?: string; // Optional description property
|
||||
templateId?: string; // Optional template scope for namespacing
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type ScopedStatusName = {
|
||||
templateId?: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type StatusIdentifier = string | ScopedStatusName;
|
||||
|
||||
export type GroupedStatuses = Record<string, NoteStatus[]>;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ export const isStatusSelected = (
|
|||
status: NoteStatus,
|
||||
currentStatuses: NoteStatus[],
|
||||
): boolean => {
|
||||
return currentStatuses.some((s) => s.name === status.name);
|
||||
return currentStatuses.some(
|
||||
(s) => s.name === status.name && s.templateId === status.templateId,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -49,13 +51,16 @@ export const getStatusDisplayName = (
|
|||
};
|
||||
|
||||
/**
|
||||
* Compares two statuses for equality by name
|
||||
* Compares two statuses for equality by name and templateId
|
||||
*/
|
||||
export const isStatusEqual = (
|
||||
status1: NoteStatus,
|
||||
status2: NoteStatus,
|
||||
): boolean => {
|
||||
return status1.name === status2.name;
|
||||
return (
|
||||
status1.name === status2.name &&
|
||||
status1.templateId === status2.templateId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue