mirror of
https://github.com/onlyworlds/obsidian-plugin.git
synced 2026-07-22 11:00:31 +00:00
The wrong-world write class: per-world commands fell back to the settings key whenever a world's own World.md key was missing, silently writing to whatever world that key named. The settings key survives a vault-content clear (.obsidian/data.json), so a read-key download + stale settings key routed rename/save to the settings world. Shared resolveWorldKey() now uses the world's OWN key; settings is fallback ONLY when the world has none, and the fallback warns. Rename false-success: the toast fired on the local rename before the API PATCH ran, so a read-key (or any failed) push still said 'success'. Now the push runs first and the notice states plainly whether onlyworlds.com was updated or only the vault. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
130 lines
5.9 KiB
TypeScript
130 lines
5.9 KiB
TypeScript
import { App, Notice, requestUrl, normalizePath, TFile, TFolder } from 'obsidian';
|
|
import { WorldRenameModal } from 'Modals/WorldRenameModal';
|
|
import { resolveWorldKey } from '../vault/world-key';
|
|
import type OnlyWorldsPlugin from '../main';
|
|
|
|
|
|
|
|
export class RenameWorldCommand {
|
|
app: App;
|
|
manifest: any;
|
|
plugin: OnlyWorldsPlugin | null;
|
|
|
|
constructor(app: App, manifest: any, plugin?: OnlyWorldsPlugin) {
|
|
this.app = app;
|
|
this.manifest = manifest;
|
|
this.plugin = plugin ?? null;
|
|
}
|
|
|
|
execute() {
|
|
const modal = new WorldRenameModal(this.app, async (oldWorldName: string, newWorldName: string) => {
|
|
try {
|
|
// Ensures each step completes before starting the next one
|
|
await this.renameWorldFile(oldWorldName, newWorldName);
|
|
await this.renameWorldFolder(oldWorldName, newWorldName);
|
|
await this.checkSettingsFile(oldWorldName, newWorldName);
|
|
// Push to the API BEFORE claiming success — the notice must
|
|
// reflect what actually happened on onlyworlds.com, not just the
|
|
// local rename (the false-success class, 2026-07-12).
|
|
await this.pushRenameToApi(newWorldName);
|
|
} catch (error) {
|
|
console.error('Error in renaming process:', error);
|
|
new Notice('Failed to complete the renaming process.');
|
|
}
|
|
});
|
|
modal.open();
|
|
}
|
|
|
|
// PATCH the world name via the SDK (WorldResource.update → PATCH /world/),
|
|
// using THIS world's own key. A local-only rename is stated plainly, never
|
|
// dressed up as a synced success.
|
|
private async pushRenameToApi(newWorldName: string): Promise<void> {
|
|
if (!this.plugin) {
|
|
new Notice('World renamed in your vault (not synced: no plugin context).');
|
|
return;
|
|
}
|
|
|
|
// World.md key wins; settings is fallback ONLY when the world has no key
|
|
// of its own. Never silently write to whatever world the settings key
|
|
// names (the wrong-world class).
|
|
const resolved = await resolveWorldKey(this.app, newWorldName, this.plugin.settings.apiKey);
|
|
if (!resolved.apiKey) {
|
|
new Notice('World renamed in your vault. Not synced: no API key for this world.');
|
|
return;
|
|
}
|
|
|
|
const client = await this.plugin.buildClient(resolved.apiKey);
|
|
if (!client) {
|
|
new Notice('World renamed in your vault. Not synced: PIN not provided.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await client.worlds.update({ name: newWorldName });
|
|
new Notice(`World renamed on onlyworlds.com${resolved.ownWorld ? '' : ' (using your default key)'}.`);
|
|
} catch (error) {
|
|
console.error('Failed to push world rename to API:', error);
|
|
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
// Read-only key or any write failure: say so, do NOT claim success.
|
|
new Notice(`World renamed in your vault, but onlyworlds.com was not updated: ${msg}`, 12000);
|
|
}
|
|
}
|
|
|
|
async renameWorldFile(oldWorldName: string, newWorldName: string) {
|
|
const worldFilePath = normalizePath(`OnlyWorlds/Worlds/${oldWorldName}/World.md`);
|
|
const worldFile = this.app.vault.getAbstractFileByPath(worldFilePath);
|
|
|
|
if (worldFile instanceof TFile) {
|
|
const worldFileContent = await this.app.vault.read(worldFile);
|
|
const updatedContent = this.updateWorldNameInContent(worldFileContent, newWorldName);
|
|
await this.app.vault.modify(worldFile, updatedContent);
|
|
new Notice('World file name updated successfully.');
|
|
} else {
|
|
new Notice('World file does not exist.');
|
|
}
|
|
}
|
|
|
|
private updateWorldNameInContent(content: string, newName: string): string {
|
|
const lines = content.split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (lines[i].startsWith('- **Name:**')) {
|
|
lines[i] = `- **Name:** ${newName}`;
|
|
break;
|
|
}
|
|
}
|
|
return lines.join('\n');
|
|
}
|
|
|
|
async renameWorldFolder(oldWorldName: string, newWorldName: string) {
|
|
const oldPath = normalizePath(`OnlyWorlds/Worlds/${oldWorldName}`);
|
|
const newPath = normalizePath(`OnlyWorlds/Worlds/${newWorldName}`);
|
|
const oldFolder = this.app.vault.getAbstractFileByPath(oldPath) as TFolder | null;
|
|
|
|
if (oldFolder instanceof TFolder) {
|
|
// Correctly using renameFolder through the FileManager
|
|
await this.app.fileManager.renameFile(oldFolder, newPath);
|
|
new Notice(`World folder renamed successfully from '${oldWorldName}' to '${newWorldName}'.`);
|
|
} else {
|
|
new Notice('World folder does not exist.');
|
|
}
|
|
}
|
|
|
|
async checkSettingsFile(oldWorldName: string, newWorldName: string) {
|
|
const settingsFilePath = normalizePath('OnlyWorlds/Settings.md');
|
|
const settingsFile = this.app.vault.getAbstractFileByPath(settingsFilePath);
|
|
|
|
if (settingsFile instanceof TFile) {
|
|
const content = await this.app.vault.read(settingsFile);
|
|
const updatedContent = this.updateWorldNameInSettings(content, oldWorldName, newWorldName);
|
|
await this.app.vault.modify(settingsFile, updatedContent);
|
|
new Notice('Settings file updated successfully.');
|
|
} else {
|
|
new Notice('Settings file does not exist.');
|
|
}
|
|
}
|
|
|
|
updateWorldNameInSettings(content: string, oldWorldName: string, newWorldName: string): string {
|
|
const regex = new RegExp(`^- \\*\\*Primary World Name:\\*\\*\\s+${oldWorldName.trim()}$`, 'm');
|
|
return content.replace(regex, `- **Primary World Name:** ${newWorldName}`);
|
|
}
|
|
}
|