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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,6 +24,12 @@ const setupListeners = () => {
window.api.onNoteCreated(updateCacheCount)
window.api.onNoteUpdated(updateCacheCount)
window.api.onPluginChanged(async () => {
const api = await getNotesAPI()
await api.init()
notesChangeCount.value++
})
// Todo update cache
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([])
onMounted(async () => {
plugins.value = await window.api.listPlugins()
})
plugins.value = await window.api.listPlugins()
const setActivePlugin = async (pluginId) => {
await window.api.setActivePlugin(pluginId)
await refreshConfig()
}
return {

View File

@@ -110,6 +110,7 @@ export default class NotesAPI {
async init() {
await this._initSodium()
await this.adapter.init()
this.notesCache.clear()
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'
// Singleton pattern to make sure only one instance of NotesAPI exists
let notesAPI = null
let initPromise = null

View File

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

View File

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

View File

@@ -12,17 +12,6 @@
<div class="notes">
<note-row v-for="note in notes" :note="note" :key="note.id" />
</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>
</template>
@@ -38,9 +27,6 @@ const { categories, loadCategories, loadCategoryNotes, notesChangeCount } =
useNotes()
const { config } = useConfig()
const { plugins, setActivePlugin } = usePlugins()
const activePlugin = ref(config.value?.activeAdapter)
const notes = ref()
@@ -56,10 +42,6 @@ onMounted(async () => {
watch(notesChangeCount, async () => {
await refreshNotes()
})
watch(activePlugin, async (pluginId) => {
await setActivePlugin(pluginId)
await refreshNotes()
})
</script>
<style lang="scss">
@@ -67,18 +49,6 @@ main.directory {
padding-top: 18px;
padding-bottom: 30px;
input[type='radio'] {
display: block;
width: 10px;
height: 10px;
margin-right: 10px;
border: 1px solid white;
&:checked {
background-color: white;
}
}
.label {
text-transform: uppercase;
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>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import useNotes from '@/composables/useNotes'
import NoteRow from '@/components/NoteRow.vue'
import _debounce from 'lodash/debounce'
@@ -39,6 +39,10 @@ onMounted(async () => {
await new Promise((resolve) => setTimeout(resolve, 100))
searchInput.value?.focus()
})
onBeforeUnmount(() => {
query.value = ''
searchResults.value = []
})
const onSearch = async () => {
await search(query.value)