fix: preserve original template IDs during migration and installation for legacy compatibility

This commit is contained in:
Aleix Soler 2026-03-18 19:02:33 +01:00
parent 712ab7bc6b
commit 782e9f14a8
4 changed files with 318 additions and 244 deletions

View file

@ -69,7 +69,15 @@ export const TemplateSettings: React.FC<TemplateSettingsProps> = ({
);
return;
}
const newId = generateTemplateId(template.name, settings.templates);
// Try to use the original ID if it's unique, otherwise generate a new one
const isIdUnique = !settings.templates.some(
(t) => t.id === template.id,
);
const newId = isIdUnique
? template.id
: generateTemplateId(template.name, settings.templates);
const installedTemplate: StatusTemplate = {
...template,
id: newId,
@ -105,11 +113,16 @@ export const TemplateSettings: React.FC<TemplateSettingsProps> = ({
);
onChange("templates", updatedTemplates);
} else {
// Add new template - generate unique ID
const uniqueId = generateTemplateId(
template.name,
settings.templates,
);
// Add new template
// Try to use the original ID if it's unique, otherwise generate a new one
const isIdUnique =
template.id &&
!settings.templates.some((t) => t.id === template.id);
const uniqueId = isIdUnique
? template.id
: generateTemplateId(template.name, settings.templates);
finalTemplate = { ...template, id: uniqueId };
// Update statuses with the final template ID

View file

@ -14,41 +14,7 @@ export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = {
showStatusBar: true,
autoHideStatusBar: false,
enableStatusOverviewPopup: true,
templates: [
{
id: "default-starter",
name: "Starter Template",
description:
"A simplified set of essential workflow statuses to get you started.",
authorGithub: "soler1212",
statuses: [
{
name: "todo",
icon: "📌",
color: "#F44336",
templateId: "default-starter",
},
{
name: "inProgress",
icon: "⚙️",
color: "#2196F3",
templateId: "default-starter",
},
{
name: "review",
icon: "👀",
color: "#9C27B0",
templateId: "default-starter",
},
{
name: "done",
icon: "✓",
color: "#4CAF50",
templateId: "default-starter",
},
],
},
],
templates: [],
customStatuses: [],
showStatusIconsInExplorer: true,
hideUnknownStatusInExplorer: true, // Default to hide unknown status
@ -60,7 +26,7 @@ export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = {
enableExperimentalFeatures: true,
enableStatusDashboard: true,
enableGroupedStatusView: true,
enabledTemplates: ["default-starter"],
enabledTemplates: [],
useCustomStatusesOnly: false,
useMultipleStatuses: true,
singleStatusStorageMode: "list",

View file

@ -6,275 +6,273 @@ import { StatusTemplate } from "types/pluginSettings";
export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
{
id: "academic",
name: "Academic research",
description: "Status workflow for academic research and writing",
authorGithub: "soler1212",
statuses: [
"id": "academic",
"name": "Academic research",
"description": "Status workflow for academic research and writing",
"authorGithub": "soler1212",
"statuses": [
{
name: "research",
icon: "🔍",
color: "#2196F3",
templateId: "academic",
"name": "research",
"icon": "🔍",
"color": "#2196F3",
"templateId": "academic"
},
{
name: "outline",
icon: "📑",
color: "#9E9E9E",
templateId: "academic",
"name": "outline",
"icon": "📑",
"color": "#9E9E9E",
"templateId": "academic"
},
{
name: "draft",
icon: "✏️",
color: "#FFC107",
templateId: "academic",
"name": "draft",
"icon": "✏️",
"color": "#FFC107",
"templateId": "academic"
},
{
name: "review",
icon: "🔬",
color: "#9C27B0",
templateId: "academic",
"name": "review",
"icon": "🔬",
"color": "#9C27B0",
"templateId": "academic"
},
{
name: "revision",
icon: "📝",
color: "#FF5722",
templateId: "academic",
"name": "revision",
"icon": "📝",
"color": "#FF5722",
"templateId": "academic"
},
{
name: "final",
icon: "📚",
color: "#4CAF50",
templateId: "academic",
"name": "final",
"icon": "📚",
"color": "#4CAF50",
"templateId": "academic"
},
{
name: "published",
icon: "🎓",
color: "#795548",
templateId: "academic",
},
],
"name": "published",
"icon": "🎓",
"color": "#795548",
"templateId": "academic"
}
]
},
{
id: "colorful",
name: "Colorful workflow",
description:
"A colorful set of workflow statuses with descriptive icons",
authorGithub: "soler1212",
statuses: [
"id": "colorful",
"name": "Colorful workflow",
"description": "A colorful set of workflow statuses with descriptive icons",
"authorGithub": "soler1212",
"statuses": [
{
name: "idea",
icon: "💡",
color: "#FFEB3B",
templateId: "colorful",
"name": "idea",
"icon": "💡",
"color": "#FFEB3B",
"templateId": "colorful"
},
{
name: "draft",
icon: "📝",
color: "#E0E0E0",
templateId: "colorful",
"name": "draft",
"icon": "📝",
"color": "#E0E0E0",
"templateId": "colorful"
},
{
name: "inProgress",
icon: "🔧",
color: "#FFC107",
templateId: "colorful",
"name": "inProgress",
"icon": "🔧",
"color": "#FFC107",
"templateId": "colorful"
},
{
name: "editing",
icon: "🖊️",
color: "#2196F3",
templateId: "colorful",
"name": "editing",
"icon": "🖊️",
"color": "#2196F3",
"templateId": "colorful"
},
{
name: "pending",
icon: "⏳",
color: "#9C27B0",
templateId: "colorful",
"name": "pending",
"icon": "⏳",
"color": "#9C27B0",
"templateId": "colorful"
},
{
name: "onHold",
icon: "⏸",
color: "#9E9E9E",
templateId: "colorful",
"name": "onHold",
"icon": "⏸",
"color": "#9E9E9E",
"templateId": "colorful"
},
{
name: "needsUpdate",
icon: "🔄",
color: "#FF5722",
templateId: "colorful",
"name": "needsUpdate",
"icon": "🔄",
"color": "#FF5722",
"templateId": "colorful"
},
{
name: "completed",
icon: "✅",
color: "#4CAF50",
templateId: "colorful",
"name": "completed",
"icon": "✅",
"color": "#4CAF50",
"templateId": "colorful"
},
{
name: "archived",
icon: "📦",
color: "#795548",
templateId: "colorful",
},
],
"name": "archived",
"icon": "📦",
"color": "#795548",
"templateId": "colorful"
}
]
},
{
id: "creative-writing",
name: "Creative Writing",
description: "Workflow for novelists and creative writers",
authorGithub: "soler1212",
statuses: [
"id": "creative-writing",
"name": "Creative Writing",
"description": "Workflow for novelists and creative writers",
"authorGithub": "soler1212",
"statuses": [
{
name: "idea",
icon: "🧠",
color: "#FFD700",
templateId: "creative-writing",
"name": "idea",
"icon": "🧠",
"color": "#FFD700",
"templateId": "creative-writing"
},
{
name: "outline",
icon: "🗺️",
color: "#87CEEB",
templateId: "creative-writing",
"name": "outline",
"icon": "🗺️",
"color": "#87CEEB",
"templateId": "creative-writing"
},
{
name: "first-draft",
icon: "✍️",
color: "#FFA07A",
templateId: "creative-writing",
"name": "first-draft",
"icon": "✍️",
"color": "#FFA07A",
"templateId": "creative-writing"
},
{
name: "revision",
icon: "🔍",
color: "#DA70D6",
templateId: "creative-writing",
"name": "revision",
"icon": "🔍",
"color": "#DA70D6",
"templateId": "creative-writing"
},
{
name: "final-polish",
icon: "✨",
color: "#32CD32",
templateId: "creative-writing",
},
],
"name": "final-polish",
"icon": "✨",
"color": "#32CD32",
"templateId": "creative-writing"
}
]
},
{
id: "minimal",
name: "Minimal workflow",
description: "A simplified set of essential workflow statuses",
authorGithub: "soler1212",
statuses: [
"id": "default-starter",
"name": "Starter Template",
"description": "A simplified set of essential workflow statuses to get you started.",
"authorGithub": "soler1212",
"statuses": [
{
name: "todo",
icon: "📌",
color: "#F44336",
templateId: "minimal",
"name": "todo",
"icon": "📌",
"color": "#F44336",
"templateId": "default-starter"
},
{
name: "inProgress",
icon: "⚙️",
color: "#2196F3",
templateId: "minimal",
"name": "inProgress",
"icon": "⚙️",
"color": "#2196F3",
"templateId": "default-starter"
},
{
name: "review",
icon: "👀",
color: "#9C27B0",
templateId: "minimal",
"name": "review",
"icon": "👀",
"color": "#9C27B0",
"templateId": "default-starter"
},
{
name: "done",
icon: "✓",
color: "#4CAF50",
templateId: "minimal",
},
],
"name": "done",
"icon": "✓",
"color": "#4CAF50",
"templateId": "default-starter"
}
]
},
{
id: "project",
name: "Project management",
description: "Status workflow for project management and tracking",
authorGithub: "soler1212",
statuses: [
"id": "minimal",
"name": "Minimal workflow",
"description": "A simplified set of essential workflow statuses",
"authorGithub": "soler1212",
"statuses": [
{
name: "planning",
icon: "🗓️",
color: "#9E9E9E",
templateId: "project",
"name": "todo",
"icon": "📌",
"color": "#F44336",
"templateId": "minimal"
},
{
name: "backlog",
icon: "📋",
color: "#E0E0E0",
templateId: "project",
"name": "inProgress",
"icon": "⚙️",
"color": "#2196F3",
"templateId": "minimal"
},
{
name: "ready",
icon: "🚦",
color: "#8BC34A",
templateId: "project",
"name": "review",
"icon": "👀",
"color": "#9C27B0",
"templateId": "minimal"
},
{
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",
},
],
"name": "done",
"icon": "✓",
"color": "#4CAF50",
"templateId": "minimal"
}
]
},
{
id: "default-starter",
name: "Starter Template",
description:
"A simplified set of essential workflow statuses to get you started.",
authorGithub: "soler1212",
statuses: [
"id": "project",
"name": "Project management",
"description": "Status workflow for project management and tracking",
"authorGithub": "soler1212",
"statuses": [
{
name: "todo",
icon: "📌",
color: "#F44336",
templateId: "default-starter",
"name": "planning",
"icon": "🗓️",
"color": "#9E9E9E",
"templateId": "project"
},
{
name: "inProgress",
icon: "⚙️",
color: "#2196F3",
templateId: "default-starter",
"name": "backlog",
"icon": "📋",
"color": "#E0E0E0",
"templateId": "project"
},
{
name: "review",
icon: "👀",
color: "#9C27B0",
templateId: "default-starter",
"name": "ready",
"icon": "🚦",
"color": "#8BC34A",
"templateId": "project"
},
{
name: "done",
icon: "✓",
color: "#4CAF50",
templateId: "default-starter",
"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"
}
]
}
];

View file

@ -1,5 +1,6 @@
import { DEFAULT_PLUGIN_SETTINGS } from "@/constants/defaultSettings";
import { PluginSettings, SyncGroup } from "@/types/pluginSettings";
import { PREDEFINED_TEMPLATES } from "@/constants/predefinedTemplates";
import { Plugin, TFile, normalizePath } from "obsidian";
import eventBus from "./eventBus";
@ -64,7 +65,8 @@ class SettingsService {
async initialize(plugin: Plugin): Promise<void> {
this.plugin = plugin;
await this.loadSettings();
const loadedData = await this.loadSettings();
this.migrateLegacySettings(loadedData);
if (this.settings.enableExternalStatusSync) {
await this.loadFromExternalFile();
@ -104,7 +106,102 @@ class SettingsService {
const loadedData = await this.plugin.loadData();
this.settings = this.mergeSettings(DEFAULT_PLUGIN_SETTINGS, loadedData);
this.deduplicateTemplates();
return this.settings;
return loadedData;
}
/**
* Migrates legacy settings and ensures new users have a starter template.
*/
private migrateLegacySettings(loadedData: Partial<PluginSettings> | null) {
const hasTemplates =
this.settings.templates && this.settings.templates.length > 0;
// 1. If it's a truly new user (no data at all), give them the starter template
if (!loadedData) {
this.injectStarterTemplate();
return;
}
// 2. If it's an existing user but has no templates, they were using the old defaults
if (!hasTemplates) {
const legacyIds = [
"colorful",
"minimal",
"academic",
"project",
"creative-writing",
];
const enabledLegacy = (this.settings.enabledTemplates || []).filter(
(id) => legacyIds.includes(id),
);
if (enabledLegacy.length > 0) {
// Restore the ones they had enabled from the marketplace list
const toRestore = PREDEFINED_TEMPLATES.filter((t) =>
enabledLegacy.includes(t.id),
).map((t) => ({
...t,
id: t.id, // Keep original ID for legacy compatibility
statuses: t.statuses.map((s) => ({
...s,
templateId: t.id,
})),
}));
this.settings.templates = toRestore;
// Update enabled list
this.settings.enabledTemplates = toRestore.map((t) => t.id);
this.saveSettings().catch(console.error);
} else {
// If nothing was enabled, just give them the starter template
this.injectStarterTemplate();
}
}
}
/**
* Injects the default starter template.
*/
private injectStarterTemplate() {
const starterTemplate = {
id: "starter",
name: "Starter Template",
description:
"A simplified set of essential workflow statuses to get you started.",
authorGithub: "soler1212",
statuses: [
{
name: "todo",
icon: "📌",
color: "#F44336",
templateId: "starter",
},
{
name: "inProgress",
icon: "⚙️",
color: "#2196F3",
templateId: "starter",
},
{
name: "review",
icon: "👀",
color: "#9C27B0",
templateId: "starter",
},
{
name: "done",
icon: "✓",
color: "#4CAF50",
templateId: "starter",
},
],
};
this.settings.templates = [starterTemplate];
if (!this.settings.enabledTemplates.includes(starterTemplate.id)) {
this.settings.enabledTemplates.push(starterTemplate.id);
}
this.saveSettings().catch(console.error);
}
/**