Preferences WIP

This commit is contained in:
nicwands
2026-03-11 13:05:28 -04:00
parent a1b339f668
commit 99e6761e92
16 changed files with 150 additions and 57 deletions

View File

@@ -143,6 +143,11 @@ app.whenReady().then(async () => {
ipcMain.on("open-note-window", (_, noteId) => { ipcMain.on("open-note-window", (_, noteId) => {
createNoteWindow(noteId); createNoteWindow(noteId);
}); });
const broadcastNoteChange = (event, data) => {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send(event, data);
});
};
const registry = new PluginRegistry(); const registry = new PluginRegistry();
registry.register(filesystemPlugin); registry.register(filesystemPlugin);
registry.register(supabasePlugin); registry.register(supabasePlugin);
@@ -162,6 +167,8 @@ app.whenReady().then(async () => {
} }
return await adapter[method](...args); return await adapter[method](...args);
}); });
broadcastNoteChange("plugin-changed", pluginId);
console.log("activePlugin: ", pluginId);
return true; return true;
}; };
await setActivePlugin(initialConfig.activeAdapter); await setActivePlugin(initialConfig.activeAdapter);
@@ -177,11 +184,6 @@ app.whenReady().then(async () => {
ipcMain.handle("setActivePlugin", async (_, pluginId) => { ipcMain.handle("setActivePlugin", async (_, pluginId) => {
return await setActivePlugin(pluginId); return await setActivePlugin(pluginId);
}); });
const broadcastNoteChange = (event, data) => {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send(event, data);
});
};
ipcMain.on("note-changed", (_, event, data) => { ipcMain.on("note-changed", (_, event, data) => {
broadcastNoteChange(event, data); broadcastNoteChange(event, data);
}); });

View File

@@ -16,6 +16,9 @@ const api = {
onNoteDeleted: (callback) => { onNoteDeleted: (callback) => {
ipcRenderer.on("note-deleted", (_, data) => callback(data)); ipcRenderer.on("note-deleted", (_, data) => callback(data));
}, },
onPluginChanged: (callback) => {
ipcRenderer.on("plugin-changed", (_, data) => callback(data));
},
notifyNoteChanged: (event, data) => { notifyNoteChanged: (event, data) => {
ipcRenderer.send("note-changed", event, data); ipcRenderer.send("note-changed", event, data);
} }

View File

@@ -70,6 +70,13 @@ app.whenReady().then(async () => {
createNoteWindow(noteId) createNoteWindow(noteId)
}) })
// Broadcast note changes to all windows
const broadcastNoteChange = (event, data) => {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send(event, data)
})
}
// Create plugin registry // Create plugin registry
const registry = new PluginRegistry() const registry = new PluginRegistry()
@@ -102,6 +109,8 @@ app.whenReady().then(async () => {
return await adapter[method](...args) return await adapter[method](...args)
}) })
broadcastNoteChange('plugin-changed', pluginId)
return true return true
} }
@@ -124,13 +133,6 @@ app.whenReady().then(async () => {
return await setActivePlugin(pluginId) return await setActivePlugin(pluginId)
}) })
// Broadcast note changes to all windows
const broadcastNoteChange = (event, data) => {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send(event, data)
})
}
// Handle note change events from renderer // Handle note change events from renderer
ipcMain.on('note-changed', (_, event, data) => { ipcMain.on('note-changed', (_, event, data) => {
broadcastNoteChange(event, data) broadcastNoteChange(event, data)

View File

@@ -19,6 +19,9 @@ const api = {
onNoteDeleted: (callback) => { onNoteDeleted: (callback) => {
ipcRenderer.on('note-deleted', (_, data) => callback(data)) ipcRenderer.on('note-deleted', (_, data) => callback(data))
}, },
onPluginChanged: (callback) => {
ipcRenderer.on('plugin-changed', (_, data) => callback(data))
},
notifyNoteChanged: (event, data) => { notifyNoteChanged: (event, data) => {
ipcRenderer.send('note-changed', event, data) ipcRenderer.send('note-changed', event, data)
}, },

View File

@@ -2,7 +2,9 @@
<div :class="classes" :style="styles"> <div :class="classes" :style="styles">
<Nav /> <Nav />
<router-view :key="$route.fullPath" /> <Suspense>
<router-view :key="$route.fullPath" />
</Suspense>
<Menu /> <Menu />

View File

@@ -5,15 +5,18 @@
<div class="menu-wrap layout-block"> <div class="menu-wrap layout-block">
<new-note class="menu-item" @noteOpened="closeMenu" /> <new-note class="menu-item" @noteOpened="closeMenu" />
<router-link class="menu-item" to="/category" <router-link class="menu-item" to="/category">
>+ New Capitulum</router-link + New Capitulum
> </router-link>
<theme-switcher class="menu-item" /> <theme-switcher class="menu-item" />
<router-link class="menu-item" to="/instructions" <router-link class="menu-item" to="/instructions">
>Instructio</router-link Instructio
> </router-link>
<button class="menu-item">Import</button> <button class="menu-item">Import</button>
<button class="menu-item">Export</button> <button class="menu-item">Export</button>
<router-link class="menu-item" to="/preferences">
Preferences
</router-link>
</div> </div>
</div> </div>
</transition> </transition>

View File

@@ -29,8 +29,14 @@ export default () => {
return configPromise return configPromise
} }
const refreshConfig = async () => {
config.value = await window.api.getConfig()
configResolve()
}
return { return {
config, config,
ensureConfig, ensureConfig,
refreshConfig,
} }
} }

View File

@@ -24,6 +24,12 @@ const setupListeners = () => {
window.api.onNoteCreated(updateCacheCount) window.api.onNoteCreated(updateCacheCount)
window.api.onNoteUpdated(updateCacheCount) window.api.onNoteUpdated(updateCacheCount)
window.api.onPluginChanged(async () => {
const api = await getNotesAPI()
await api.init()
notesChangeCount.value++
})
// Todo update cache // Todo update cache
window.api.onNoteDeleted(() => { window.api.onNoteDeleted(() => {

View File

@@ -1,14 +1,16 @@
import { ref, onMounted } from 'vue' import { ref } from 'vue'
import useConfig from './useConfig'
export default async () => {
const { refreshConfig } = useConfig()
export default () => {
const plugins = ref([]) const plugins = ref([])
onMounted(async () => { plugins.value = await window.api.listPlugins()
plugins.value = await window.api.listPlugins()
})
const setActivePlugin = async (pluginId) => { const setActivePlugin = async (pluginId) => {
await window.api.setActivePlugin(pluginId) await window.api.setActivePlugin(pluginId)
await refreshConfig()
} }
return { return {

View File

@@ -110,6 +110,7 @@ export default class NotesAPI {
async init() { async init() {
await this._initSodium() await this._initSodium()
await this.adapter.init() await this.adapter.init()
this.notesCache.clear()
const encryptedNotes = await this.adapter.getAll() const encryptedNotes = await this.adapter.getAll()

View File

@@ -3,7 +3,6 @@ import IpcAdapter from '@/libs/core/IpcAdapter.js'
import useConfig from '@/composables/useConfig.js' import useConfig from '@/composables/useConfig.js'
// Singleton pattern to make sure only one instance of NotesAPI exists // Singleton pattern to make sure only one instance of NotesAPI exists
let notesAPI = null let notesAPI = null
let initPromise = null let initPromise = null

View File

@@ -6,6 +6,7 @@ import CreateCategory from '@/views/CreateCategory.vue'
import Category from '@/views/Category.vue' import Category from '@/views/Category.vue'
import Instructions from '@/views/Instructions.vue' import Instructions from '@/views/Instructions.vue'
import Search from '@/views/Search.vue' import Search from '@/views/Search.vue'
import Preferences from '@/views/Preferences.vue'
const routes = [ const routes = [
{ path: '/', name: 'directory', component: Directory }, { path: '/', name: 'directory', component: Directory },
@@ -14,6 +15,7 @@ const routes = [
{ path: '/category/:id', name: 'category', component: Category }, { path: '/category/:id', name: 'category', component: Category },
{ path: '/instructions', name: 'instructions', component: Instructions }, { path: '/instructions', name: 'instructions', component: Instructions },
{ path: '/search', name: 'search', component: Search }, { path: '/search', name: 'search', component: Search },
{ path: '/preferences', name: 'preferences', component: Preferences },
] ]
export const router = createRouter({ export const router = createRouter({

View File

@@ -47,7 +47,9 @@ a,
button, button,
input, input,
pre, pre,
span { span,
label,
li {
@include p; @include p;
} }
.bold { .bold {

View File

@@ -12,17 +12,6 @@
<div class="notes"> <div class="notes">
<note-row v-for="note in notes" :note="note" :key="note.id" /> <note-row v-for="note in notes" :note="note" :key="note.id" />
</div> </div>
<div v-for="plugin in plugins" :key="plugin.id">
<input
v-model="activePlugin"
type="radio"
name="plugins"
:value="plugin.id"
:id="plugin.id"
/>
<label :for="plugin.id">{{ plugin.name }}</label>
</div>
</main> </main>
</template> </template>
@@ -38,9 +27,6 @@ const { categories, loadCategories, loadCategoryNotes, notesChangeCount } =
useNotes() useNotes()
const { config } = useConfig() const { config } = useConfig()
const { plugins, setActivePlugin } = usePlugins()
const activePlugin = ref(config.value?.activeAdapter)
const notes = ref() const notes = ref()
@@ -56,10 +42,6 @@ onMounted(async () => {
watch(notesChangeCount, async () => { watch(notesChangeCount, async () => {
await refreshNotes() await refreshNotes()
}) })
watch(activePlugin, async (pluginId) => {
await setActivePlugin(pluginId)
await refreshNotes()
})
</script> </script>
<style lang="scss"> <style lang="scss">
@@ -67,18 +49,6 @@ main.directory {
padding-top: 18px; padding-top: 18px;
padding-bottom: 30px; padding-bottom: 30px;
input[type='radio'] {
display: block;
width: 10px;
height: 10px;
margin-right: 10px;
border: 1px solid white;
&:checked {
background-color: white;
}
}
.label { .label {
text-transform: uppercase; text-transform: uppercase;
margin: 17px 0 24px; margin: 17px 0 24px;

View File

@@ -0,0 +1,86 @@
<template>
<main class="preferences layout-block">
<router-link to="/" class="back"><- Back</router-link>
<h1 class="mono">Storage Plugin</h1>
<div v-for="plugin in plugins" class="plugin" :key="plugin.id">
<input
v-model="activePluginId"
name="plugins"
type="radio"
:id="plugin.id"
:value="plugin.id"
/>
<label :for="plugin.id">
<p class="name bold">{{ plugin.name }}</p>
<p class="description">{{ plugin.description }}</p>
</label>
</div>
</main>
</template>
<script setup>
import usePlugins from '@/composables/usePlugins'
import useConfig from '@/composables/useConfig'
import { ref, onMounted, watch } from 'vue'
const { plugins, setActivePlugin } = await usePlugins()
const { config, ensureConfig } = useConfig()
const activePluginId = ref(plugins.value[0].id)
onMounted(async () => {
await ensureConfig()
activePluginId.value = config.value.activeAdapter
watch(activePluginId, async (id) => {
await setActivePlugin(id)
})
})
</script>
<style lang="scss">
.preferences {
padding-top: 8px;
.back {
opacity: 0.25;
display: block;
margin-top: 9px;
margin-bottom: 14px;
}
h1 {
margin-bottom: 15px;
}
.plugin {
display: flex;
margin-bottom: 16px;
}
input[type='radio'] {
display: block;
flex-shrink: 0;
width: 10px;
height: 10px;
margin-right: 10px;
border: 1px solid white;
cursor: pointer;
&:checked {
background-color: white;
}
}
label {
cursor: pointer;
.description {
color: var(--grey-100);
margin-top: 6px;
}
}
}
</style>

View File

@@ -25,7 +25,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted, onBeforeUnmount } from 'vue'
import useNotes from '@/composables/useNotes' import useNotes from '@/composables/useNotes'
import NoteRow from '@/components/NoteRow.vue' import NoteRow from '@/components/NoteRow.vue'
import _debounce from 'lodash/debounce' import _debounce from 'lodash/debounce'
@@ -39,6 +39,10 @@ onMounted(async () => {
await new Promise((resolve) => setTimeout(resolve, 100)) await new Promise((resolve) => setTimeout(resolve, 100))
searchInput.value?.focus() searchInput.value?.focus()
}) })
onBeforeUnmount(() => {
query.value = ''
searchResults.value = []
})
const onSearch = async () => { const onSearch = async () => {
await search(query.value) await search(query.value)