Skip to content

Commit

Permalink
feat: add shelf crud
Browse files Browse the repository at this point in the history
  • Loading branch information
srg-kostyrko committed Oct 14, 2024
1 parent 338ac72 commit 4c02940
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 34 deletions.
56 changes: 56 additions & 0 deletions src/components/modals/CreateShelf.modal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script setup lang="ts">
import ObsidianSetting from "../obsidian/ObsidianSetting.vue";
import ObsidianTextInput from "../obsidian/ObsidianTextInput.vue";
import ObsidianButton from "../obsidian/ObsidianButton.vue";
import { useForm } from "vee-validate";
import { toTypedSchema } from "@vee-validate/valibot";
import * as v from "valibot";
import FormErrors from "../FormErrors.vue";
import { pluginSettings$ } from "@/stores/settings.store";
const emit = defineEmits<{
(event: "create", name: string): void;
(event: "close"): void;
}>();
function isNameNotUnique(value: string) {
return !(value in pluginSettings$.value.shelves);
}
const { defineField, errorBag, handleSubmit } = useForm({
initialValues: {
name: "",
},
validationSchema: toTypedSchema(
v.object({
name: v.pipe(
v.string(),
v.nonEmpty("Shelf name is required"),
v.check(isNameNotUnique, "Shelf name should be unique"),
),
}),
),
});
const [name, nameAttrs] = defineField("name");
const onSubmit = handleSubmit((values) => {
emit("create", values.name);
emit("close");
});
</script>

<template>
<form @submit.prevent="onSubmit">
<ObsidianSetting name="Journal name">
<template #description>
<FormErrors :errors="errorBag.name" />
</template>
<ObsidianTextInput v-model="name" placeholder="ex. Work" v-bind="nameAttrs" />
</ObsidianSetting>
<ObsidianSetting>
<ObsidianButton @click="emit('close')">Close</ObsidianButton>
<ObsidianButton cta type="submit" @click="onSubmit">Add</ObsidianButton>
</ObsidianSetting>
</form>
</template>
39 changes: 39 additions & 0 deletions src/components/modals/RemoveShelf.modal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import ObsidianSetting from "../obsidian/ObsidianSetting.vue";
import ObsidianDropdown from "../obsidian/ObsidianDropdown.vue";
import ObsidianButton from "../obsidian/ObsidianButton.vue";
import { pluginSettings$ } from "@/stores/settings.store";
const { shelfName } = defineProps<{
shelfName: string;
}>();
const emit = defineEmits<{
(event: "close"): void;
(event: "remove", destination: string): void;
}>();
const destinationShelf = ref("");
const otherShelves = computed(() => {
return Object.values(pluginSettings$.value.shelves).filter((shelf) => shelf.name !== shelfName);
});
function confirm() {
emit("remove", destinationShelf.value);
emit("close");
}
</script>

<template>
<ObsidianSetting v-if="otherShelves.length > 0" name="Move journals to">
<ObsidianDropdown v-model="destinationShelf">
<option value="">None</option>
<option v-for="shelf in otherShelves" :key="shelf.name" :value="shelf.name">{{ shelf.name }}</option>
</ObsidianDropdown>
</ObsidianSetting>
<p v-else>Journals will be moved out</p>
<ObsidianSetting>
<ObsidianButton @click="$emit('close')">Cancel</ObsidianButton>
<ObsidianButton cta @click="confirm">Remove</ObsidianButton>
</ObsidianSetting>
</template>
1 change: 1 addition & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {

export const defaultPluginSettings: PluginSettings = {
version: 2,
useShelves: false,
showReloadHint: false,
journals: {},
shelves: {},
Expand Down
36 changes: 36 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,22 @@ export default class JournalPlugin extends Plugin {
pluginSettings$.value.journals[newName] = pluginSettings$.value.journals[name];
pluginSettings$.value.journals[newName].name = newName;
delete pluginSettings$.value.journals[name];
for (const shelf of pluginSettings$.value.journals[newName].shelves) {
pluginSettings$.value.shelves[shelf].journals = pluginSettings$.value.shelves[shelf].journals.map(
(journalName) => (journalName === name ? newName : journalName),
);
}
this.#journals.delete(name);
this.#journals.set(newName, new Journal(newName));
}

removeJournal(name: string): void {
this.#journals.delete(name);
for (const shelf of pluginSettings$.value.journals[name].shelves) {
pluginSettings$.value.shelves[shelf].journals = pluginSettings$.value.shelves[shelf].journals.filter(
(journalName) => journalName !== name,
);
}
delete pluginSettings$.value.journals[name];
}

Expand All @@ -72,6 +82,32 @@ export default class JournalPlugin extends Plugin {
}
}

createShelf(name: string): void {
pluginSettings$.value.shelves[name] = {
name,
journals: [],
};
}

renameShelf(name: string, newName: string): void {
pluginSettings$.value.shelves[newName] = pluginSettings$.value.shelves[name];
delete pluginSettings$.value.shelves[name];
for (const journal of pluginSettings$.value.shelves[newName].journals) {
pluginSettings$.value.journals[journal].shelves = pluginSettings$.value.journals[journal].shelves.map((shelf) =>
shelf === name ? newName : shelf,
);
}
}

removeShelf(name: string, destinationShelf?: string): void {
delete pluginSettings$.value.shelves[name];
for (const journal of pluginSettings$.value.shelves[name].journals) {
pluginSettings$.value.journals[journal].shelves = destinationShelf
? pluginSettings$.value.journals[journal].shelves.map((shelf) => (shelf === name ? destinationShelf : shelf))
: pluginSettings$.value.journals[journal].shelves.filter((shelf) => shelf !== name);
}
}

async onload(): Promise<void> {
app$.value = this.app;
plugin$.value = this;
Expand Down
42 changes: 9 additions & 33 deletions src/settings/JournalSettingsDashboard.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
<script setup lang="ts">
import { moment } from "obsidian";
import { computed } from "vue";
import { calendarSettings$, calendarViewSettings$, journals$ } from "../stores/settings.store";
import { calendarSettings$, calendarViewSettings$, pluginSettings$ } from "../stores/settings.store";
import ObsidianSetting from "../components/obsidian/ObsidianSetting.vue";
import ObsidianDropdown from "../components/obsidian/ObsidianDropdown.vue";
import ObsidianNumberInput from "../components/obsidian/ObsidianNumberInput.vue";
import ObsidianIconButton from "../components/obsidian/ObsidianIconButton.vue";
import JournalSettingsList from "./JournalSettingsList.vue";
import CreateJournal from "../components/modals/CreateJournal.modal.vue";
import RemoveJournal from "../components/modals/RemoveJournal.modal.vue";
import { VueModal } from "../components/modals/vue-modal";
import type { JournalSettings, NotesProcessing } from "../types/settings.types";
import { plugin$ } from "../stores/obsidian.store";
import ObsidianToggle from "@/components/obsidian/ObsidianToggle.vue";
import { updateLocale } from "../calendar";
import JournalSettingsWithoutShelves from "./JournalSettingsWithoutShelves.vue";
import JournalSettingsWithShelves from "./JournalSettingsWithShelves.vue";
const emit = defineEmits<(event: "edit", name: string) => void>();
const emit = defineEmits<(event: "edit" | "orgamize", name: string) => void>();
const fow = moment().localeData().firstDayOfWeek();
const fowText = moment().localeData().weekdays()[fow];
Expand All @@ -35,27 +31,6 @@ function changeFirstWeekOfYear(value: number): void {
updateLocale(calendarSettings$.value.firstDayOfWeek, value);
calendarSettings$.value.firstWeekOfYear = value;
}
function create(): void {
new VueModal("Add Journal", CreateJournal, {
onCreate(name: string, writing: JournalSettings["write"]) {
plugin$.value.createJournal(name, writing);
},
}).open();
}
function edit(name: string): void {
emit("edit", name);
}
function remove(name: string): void {
const journal = journals$.value[name];
if (!journal) return;
new VueModal(`Remove ${journal.name} journal`, RemoveJournal, {
onRemove(_noteProcessing: NotesProcessing) {
// TODO Process notes on remove
plugin$.value.removeJournal(name);
},
}).open();
}
</script>

<template>
Expand All @@ -79,11 +54,12 @@ function remove(name: string): void {
<ObsidianNumberInput :model-value="calendarSettings$.firstWeekOfYear" @update:model-value="changeFirstWeekOfYear" />
</ObsidianSetting>

<ObsidianSetting name="Journals" heading>
<ObsidianIconButton :icon="'plus'" cta tooltip="Create new journal" @click="create" />
<ObsidianSetting name="Use shelves?">
<ObsidianToggle v-model="pluginSettings$.useShelves" />
</ObsidianSetting>

<JournalSettingsList @edit="edit" @remove="remove" />
<JournalSettingsWithShelves v-if="pluginSettings$.useShelves" @organize="emit('orgamize', $event)" />
<JournalSettingsWithoutShelves v-else @edit="emit('edit', $event)" />

<ObsidianSetting name="Calendar view" heading />
<ObsidianSetting name="Add to">
Expand Down
63 changes: 63 additions & 0 deletions src/settings/JournalSettingsWithShelves.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script setup lang="ts">
import ObsidianSetting from "@/components/obsidian/ObsidianSetting.vue";
import ObsidianIconButton from "@/components/obsidian/ObsidianIconButton.vue";
import { VueModal } from "@/components/modals/vue-modal";
import CreateShelf from "@/components/modals/CreateShelf.modal.vue";
import RemoveShelf from "@/components/modals/RemoveShelf.modal.vue";
import { plugin$ } from "@/stores/obsidian.store";
import { pluginSettings$ } from "@/stores/settings.store";
import { computed } from "vue";
defineEmits<(event: "organize", shelfName: string) => void>();
const shelvesList = computed(() =>
Object.values(pluginSettings$.value.shelves).toSorted((a, b) => a.name.localeCompare(b.name)),
);
const journalsWithoutShelf = computed(() => {
const journals = Object.values(pluginSettings$.value.journals);
return journals.filter((journal) => journal.shelves.length === 0);
});
function createShelf(): void {
new VueModal("Add Journal Shelf", CreateShelf, {
onCreate(name: string) {
plugin$.value.createShelf(name);
},
}).open();
}
function removeShelf(shelfName: string): void {
const shelf = pluginSettings$.value.shelves[shelfName];
if (!shelf) return;
new VueModal(`Remove ${shelf.name} shelf`, RemoveShelf, {
shelfName,
onRemove(destinationShelf: string) {
plugin$.value.removeShelf(shelfName, destinationShelf);
},
}).open();
}
</script>

<template>
<ObsidianSetting name="Journal shelves" heading>
<ObsidianIconButton :icon="'plus'" cta tooltip="Create new shelf" @click="createShelf" />
</ObsidianSetting>
<p v-if="shelvesList.length === 0">No shelves configured yet.</p>
<template v-else>
<ObsidianSetting v-for="shelf of shelvesList" :key="shelf.name">
<template #name>
<b>
{{ shelf.name }} </b
><br />
{{ shelf.journals.length }} journals
</template>
<ObsidianIconButton icon="library" :tooltip="'Organize ' + shelf.name" @click="$emit('organize', shelf.name)" />
<ObsidianIconButton icon="trash-2" :tooltip="'Delete ' + shelf.name" @click="removeShelf(shelf.name)" />
</ObsidianSetting>
</template>
<template v-if="journalsWithoutShelf.length > 0">
<ObsidianSetting name="Journals without shelf" heading />
</template>
</template>

<style scoped></style>
44 changes: 44 additions & 0 deletions src/settings/JournalSettingsWithoutShelves.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script setup lang="ts">
import ObsidianSetting from "@/components/obsidian/ObsidianSetting.vue";
import JournalSettingsList from "./JournalSettingsList.vue";
import ObsidianIconButton from "@/components/obsidian/ObsidianIconButton.vue";
import { VueModal } from "@/components/modals/vue-modal";
import CreateJournal from "@/components/modals/CreateJournal.modal.vue";
import RemoveJournal from "@/components/modals/RemoveJournal.modal.vue";
import { plugin$ } from "@/stores/obsidian.store";
import type { JournalSettings, NotesProcessing } from "@/types/settings.types";
import { journals$ } from "@/stores/settings.store";
const emit = defineEmits<(event: "edit", name: string) => void>();
function create(): void {
new VueModal("Add Journal", CreateJournal, {
onCreate(name: string, writing: JournalSettings["write"]) {
plugin$.value.createJournal(name, writing);
},
}).open();
}
function edit(name: string): void {
emit("edit", name);
}
function remove(name: string): void {
const journal = journals$.value[name];
if (!journal) return;
new VueModal(`Remove ${journal.name} journal`, RemoveJournal, {
onRemove(_noteProcessing: NotesProcessing) {
// TODO Process notes on remove
plugin$.value.removeJournal(name);
},
}).open();
}
</script>

<template>
<ObsidianSetting name="Journals" heading>
<ObsidianIconButton :icon="'plus'" cta tooltip="Create new journal" @click="create" />
</ObsidianSetting>

<JournalSettingsList @edit="edit" @remove="remove" />
</template>

<style scoped></style>
4 changes: 4 additions & 0 deletions src/types/plugin.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ export interface JournalPlugin extends Plugin {
createJournal(name: string, write: JournalSettings["write"]): void;
renameJournal(name: string, newName: string): void;
removeJournal(name: string): void;

createShelf(name: string): void;
renameShelf(name: string, newName: string): void;
removeShelf(name: string, destinationShelf?: string): void;
}
3 changes: 2 additions & 1 deletion src/types/settings.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type OpenMode = "active" | "tab" | "split" | "window";
export interface PluginSettings {
version: number;

useShelves: boolean;
showReloadHint: boolean;

journals: Record<string, JournalSettings>;
Expand All @@ -26,8 +27,8 @@ export interface PluginSettings {
}

export interface ShelfSettings {
id: string;
name: string;
journals: string[];
}

export interface WriteDaily {
Expand Down

0 comments on commit 4c02940

Please sign in to comment.