Skip to content

Commit

Permalink
Create a clock command
Browse files Browse the repository at this point in the history
  • Loading branch information
cwegrzyn committed May 27, 2024
1 parent fe60ad8 commit f783359
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 12 deletions.
141 changes: 141 additions & 0 deletions src/clocks/clock-create-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {
App,
Modal,
SearchComponent,
Setting,
TextComponent,
normalizePath,
} from "obsidian";
import { generateObsidianFilename } from "utils/filename";
import { FolderTextSuggest } from "utils/ui/settings/folder";
import { Clock } from "./clock";

export type ClockCreateResultType = {
segments: number;
name: string;
fileName: string;
targetFolder: string;
};

export class ClockCreateModal extends Modal {
public result: ClockCreateResultType = {
segments: 0,
name: "",
fileName: "",
targetFolder: "",
};

public accepted: boolean = false;

constructor(
app: App,
defaults: Partial<ClockCreateResultType> = {},
protected readonly onAccept: (arg: {
name: string;
targetFolder: string;
fileName: string;
clock: Clock;
}) => void,
protected readonly onCancel: () => void,
) {
super(app);
Object.assign(this.result, defaults);
}

onOpen(): void {
this.accepted = false;

const { contentEl } = this;
new Setting(contentEl).setName("New Clock").setHeading();

let fileNameText: TextComponent;

new Setting(contentEl).setName("Name").addText((text) =>
text.onChange((value) => {
this.result.name = value;
// TODO: could add smarter logic to only update if user hasn't made a specific value
fileNameText.setValue(generateObsidianFilename(value)).onChanged();
}),
);

new Setting(contentEl).setName("File name").addText(
(text) =>
(fileNameText = text.onChange((value) => {
this.result.fileName = value;
})),
);

let folderComponent!: SearchComponent;
const folderSetting = new Setting(contentEl)
.setName("Target folder")
.addSearch((search) => {
new FolderTextSuggest(this.app, search.inputEl);
folderComponent = search
.setPlaceholder("Choose a folder")
.setValue(this.result.targetFolder)
.onChange((newFolder) => {
this.result.targetFolder = newFolder;
const normalized = normalizePath(newFolder);
if (this.app.vault.getFolderByPath(normalized)) {
folderSetting.setDesc(
`Creating clock in existing folder '${normalized}'`,
);
} else {
folderSetting.setDesc(
`Creating clock in new folder '${normalized}`,
);
}
});
});

new Setting(contentEl).setName("Segments").addSlider((slider) =>
slider
.setLimits(2, 12, 1)
.setValue(6)
.setDynamicTooltip()
.onChange((segments) => {
this.result.segments = segments;
}),
);

folderComponent.onChanged();

new Setting(contentEl)
.addButton((btn) =>
btn
.setButtonText("Create")
.setCta()
.onClick(() => {
this.accept();
}),
)
.addButton((btn) =>
btn.setButtonText("Cancel").onClick(() => {
this.accepted = false;
this.close();
}),
);
}

accept(): void {
this.accepted = true;
this.close();
this.onAccept({
name: this.result.name,
fileName: this.result.fileName,
targetFolder: this.result.targetFolder,
clock: Clock.create({
progress: 0,
segments: this.result.segments,
active: true,
}).unwrap(),
});
}

onClose(): void {
this.contentEl.empty();
if (!this.accepted) {
this.onCancel();
}
}
}
21 changes: 20 additions & 1 deletion src/clocks/clock-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Index } from "indexer/index-impl";
import { CachedMetadata } from "obsidian";
import { normalizeKeys } from "utils/zodutils";
import { z } from "zod";
import { PLUGIN_KIND_FIELD } from "../constants";
import { BaseIndexer, IndexUpdate } from "../indexer/indexer";
import { Either, Left } from "../utils/either";
import { updater } from "../utils/update";
Expand Down Expand Up @@ -45,6 +46,22 @@ export class ClockFileAdapter {
return this.raw.name;
}

static newFromClock({
name,
clock,
}: {
name: string;
clock: Clock;
}): Either<z.ZodError, ClockFileAdapter> {
return this.create({
name,
segments: clock.segments,
progress: clock.progress,
tags: !clock.active ? ["complete"] : ["incomplete"],
[PLUGIN_KIND_FIELD]: KIND__CLOCK,
} satisfies z.input<typeof clockSchema>);
}

static create(data: unknown): Either<z.ZodError, ClockFileAdapter> {
const result = normalizedClockSchema.safeParse(data);
if (result.success) {
Expand Down Expand Up @@ -84,8 +101,10 @@ export class ClockFileAdapter {
}
}

export const KIND__CLOCK = "clock";

export class ClockIndexer extends BaseIndexer<ClockFileAdapter, z.ZodError> {
readonly id: string = "clock";
readonly id: string = KIND__CLOCK;

processFile(
path: string,
Expand Down
53 changes: 50 additions & 3 deletions src/clocks/commands.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import IronVaultPlugin from "index";
import { appendNodesToMoveOrMechanicsBlock } from "mechanics/editor";
import { createClockNode } from "mechanics/node-builders";
import { App, Editor, MarkdownView } from "obsidian";
import {
createClockCreationNode,
createClockNode,
} from "mechanics/node-builders";
import { App, Editor, MarkdownView, stringifyYaml } from "obsidian";
import { IronVaultPluginSettings } from "settings";
import { ClockFileAdapter, ClockIndex, clockUpdater } from "../clocks/clock-file";
import {
ClockFileAdapter,
ClockIndex,
clockUpdater,
} from "../clocks/clock-file";
import { selectClock } from "../clocks/select-clock";
import { BLOCK_TYPE__CLOCK } from "../constants";
import { vaultProcess } from "../utils/obsidian";
import { CustomSuggestModal } from "../utils/suggest";
import { Clock } from "./clock";
Expand Down Expand Up @@ -51,3 +59,42 @@ export async function advanceClock(
);
}

export async function createClock(
plugin: IronVaultPlugin,
editor: Editor,
): Promise<void> {
const clockInput: {
targetFolder: string;
fileName: string;
name: string;
clock: Clock;
} = await new Promise((onAccept, onReject) => {
new ClockCreateModal(
plugin.app,
{ targetFolder: plugin.settings.defaultClockFolder },
onAccept,
onReject,
).open();
});

const clock =
ClockFileAdapter.newFromClock(clockInput).expect("invalid clock");

let clockFolder = plugin.app.vault.getFolderByPath(clockInput.targetFolder);
if (!clockFolder) {
clockFolder = await plugin.app.vault.createFolder(clockInput.targetFolder);
}

// TODO: figure out the templating for this
const file = await plugin.app.fileManager.createNewFile(
clockFolder,
clockInput.fileName,
"md",
`---\n${stringifyYaml(clock.raw)}\n---\n\n\`\`\`${BLOCK_TYPE__CLOCK}\n\`\`\`\n\n`,
);

appendNodesToMoveOrMechanicsBlock(
editor,
createClockCreationNode(clockInput.name, file.path),
);
}
9 changes: 7 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/* How we refer to the plugin in block names, property names, etc. */
export const PLUGIN_SLUG: string = "iron-vault";
export const PLUGIN_SLUG = "iron-vault";

export function pluginPrefixed(name: string): string {
export function pluginPrefixed<N extends string>(
name: N,
): `${typeof PLUGIN_SLUG}-${N}` {
return `${PLUGIN_SLUG}-${name}`;
}

export const PLUGIN_KIND_FIELD = pluginPrefixed("kind");

export const BLOCK_TYPE__TRACK = pluginPrefixed("track");
export const BLOCK_TYPE__CLOCK = pluginPrefixed("clock");
25 changes: 19 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ViewPlugin, ViewUpdate } from "@codemirror/view";
import { determineCharacterActionContext } from "characters/action-context";
import { addAssetToCharacter } from "characters/commands";
import registerClockBlock from "clocks/clock-block";
import { advanceClock } from "clocks/commands";
import { advanceClock, createClock } from "clocks/commands";
import { generateEntityCommand } from "entity/command";
import { IndexManager } from "indexer/manager";
import { runMoveCommand } from "moves/action";
Expand Down Expand Up @@ -133,6 +133,10 @@ export default class IronVaultPlugin extends Plugin {
meterCommands.burnMomentum(this, editor),
});

/*
* PROGRESS TRACKS
*/

this.addCommand({
id: "progress-create",
name: "Progress Track: Create a Progress Track",
Expand All @@ -155,18 +159,27 @@ export default class IronVaultPlugin extends Plugin {
},
});

/*
* CLOCKS
*/

this.addCommand({
id: "clock-create",
name: "Clock: Create a clock",
editorCallback: (editor) => createClock(this, editor),
});

this.addCommand({
id: "clock-advance",
name: "Advance a Clock",
editorCallback: async (editor, ctx) => {
await advanceClock(
name: "Clock: Advance a Clock",
editorCallback: (editor, ctx) =>
advanceClock(
this.app,
this.settings,
editor,
ctx as MarkdownView,
this.clockIndex,
);
},
),
});

this.addCommand({
Expand Down
1 change: 1 addition & 0 deletions src/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class IronVaultPluginSettings {
oraclesFolder: string = "";

defaultProgressTrackFolder: string = "Progress";
defaultClockFolder: string = "Clocks";

momentumResetTemplate: string =
"> [!mechanics] {{character.name}} burned momentum: {{oldValue}} -> {{newValue}}\n\n";
Expand Down
11 changes: 11 additions & 0 deletions src/settings/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ export class IronVaultSettingTab extends PluginSettingTab {
);
});

new Setting(containerEl)
.setName("Default clock folder")
.setDesc("Create clocks in this folder by default.")
.addSearch((search) => {
new FolderTextSuggest(this.app, search.inputEl);
search
.setPlaceholder("Type the name of a folder")
.setValue(settings.defaultClockFolder)
.onChange((value) => this.updateSetting("defaultClockFolder", value));
});

new Setting(containerEl)
.setName("Use character system")
.setDesc(
Expand Down

0 comments on commit f783359

Please sign in to comment.