Files
palace/src/store.ts
2025-07-19 00:36:16 +02:00

191 lines
5.2 KiB
TypeScript

import { atom } from 'nanostores'
import { db } from './db'
type Note = {
id: string
x: number
y: number
content: string
}
type Space = {
id: string
notes: Note[]
}
export const currentSpaceId = atom<string>('home')
export const currentSpace = atom<Space | null>(null)
export const navigationHistory = atom<string[]>(['home'])
export const editingNoteId = atom<string | null>(null)
export const selectedNoteIds = atom<string[]>([])
export const existingSpaceIds = atom<string[]>([])
export const refreshExistingSpaces = async () => {
const spaces = await db.getAllSpaceIds()
existingSpaceIds.set(spaces)
}
export const loadSpace = async (spaceId: string) => {
// Check if current space is empty and delete it (except 'home')
const currentSpace_value = currentSpace.get()
const currentSpaceId_value = currentSpaceId.get()
if (currentSpace_value && currentSpaceId_value !== 'home' && currentSpace_value.notes.length === 0) {
await db.deleteSpace(currentSpaceId_value)
}
currentSpace.set(null)
let space = await db.getSpace(spaceId)
if (!space) {
space = await db.createSpace(spaceId)
}
currentSpace.set(space)
currentSpaceId.set(spaceId)
window.history.pushState({}, '', `/#${spaceId}`)
// Refresh list of existing spaces
await refreshExistingSpaces()
return space
}
export const navigateToSpace = async (spaceId: string) => {
const history = navigationHistory.get()
if (history[history.length - 1] !== spaceId) {
navigationHistory.set([...history, spaceId])
}
await loadSpace(spaceId)
}
export const goBack = async () => {
const history = navigationHistory.get()
if (history.length > 1) {
const newHistory = history.slice(0, -1)
navigationHistory.set(newHistory)
const previousSpaceId = newHistory[newHistory.length - 1]
await loadSpace(previousSpaceId)
}
}
export const createNote = async (x: number, y: number) => {
const space = currentSpace.get()
if (!space) return null
const note: Note = {
id: `note-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
x,
y,
content: ''
}
await db.addNoteToSpace(space.id, note)
const updatedSpace = await db.getSpace(space.id)
currentSpace.set(updatedSpace!)
return note.id
}
export const updateNote = async (noteId: string, updates: Partial<Note>) => {
const space = currentSpace.get()
if (!space) return
await db.updateNoteInSpace(space.id, noteId, updates)
const updatedSpace = await db.getSpace(space.id)
currentSpace.set(updatedSpace!)
}
export const deleteNote = async (noteId: string) => {
const space = currentSpace.get()
if (!space) return
await db.deleteNoteFromSpace(space.id, noteId)
const updatedSpace = await db.getSpace(space.id)
currentSpace.set(updatedSpace!)
}
export const getSpaceFromUrl = () => {
const hash = window.location.hash.slice(1)
return hash || 'home'
}
// Selection management
export const selectNote = (noteId: string, multiSelect: boolean = false) => {
const currentSelection = selectedNoteIds.get()
if (multiSelect) {
if (!currentSelection.includes(noteId)) {
// Add to selection only
const newSelection = [...currentSelection, noteId]
selectedNoteIds.set(newSelection)
}
// If already selected and multiSelect, do nothing (don't remove)
} else {
// Single selection
selectedNoteIds.set([noteId])
}
}
export const selectNotesInRect = (minX: number, minY: number, maxX: number, maxY: number) => {
const space = currentSpace.get()
if (!space) return
const selectedIds = space.notes
.filter(note => {
// Check if note overlaps with rectangle (considering note has some width/height)
const noteRight = note.x + 50 // Approximate note width
const noteBottom = note.y + 20 // Approximate note height
const inRect = !(note.x > maxX || noteRight < minX || note.y > maxY || noteBottom < minY)
return inRect
})
.map(note => note.id)
selectedNoteIds.set(selectedIds)
}
export const clearSelection = () => {
selectedNoteIds.set([])
}
export const deleteSelectedNotes = async () => {
const selectedIds = selectedNoteIds.get()
const space = currentSpace.get()
if (!space || selectedIds.length === 0) return
for (const noteId of selectedIds) {
await db.deleteNoteFromSpace(space.id, noteId)
}
const updatedSpace = await db.getSpace(space.id)
currentSpace.set(updatedSpace!)
clearSelection()
}
export const moveSelectedNotesLocal = (deltaX: number, deltaY: number) => {
const selectedIds = selectedNoteIds.get()
const space = currentSpace.get()
if (!space || selectedIds.length === 0) return
// Update positions locally without DB calls
const updatedNotes = space.notes.map(note => {
if (selectedIds.includes(note.id)) {
return { ...note, x: note.x + deltaX, y: note.y + deltaY }
}
return note
})
currentSpace.set({ ...space, notes: updatedNotes })
}
export const saveSelectedNotesToDB = async () => {
const selectedIds = selectedNoteIds.get()
const space = currentSpace.get()
if (!space || selectedIds.length === 0) return
// Save to DB
for (const noteId of selectedIds) {
const note = space.notes.find(n => n.id === noteId)
if (note) {
await db.updateNoteInSpace(space.id, noteId, { x: note.x, y: note.y })
}
}
}