Files
contexted-v3/src/composables/useNotes.ts
2023-05-22 09:08:06 +02:00

213 lines
6.4 KiB
TypeScript

import { defaultNotes } from '@/utils/defaultNotes'
import { viewModes, activeViewMode } from '@/composables/useViewMode'
import { useTitle } from '@vueuse/core'
import { mdToHtml } from '@/utils/markdown'
import { getAllMatches } from '@/utils/helpers'
import { initialized, user } from '@/composables/useFirebase'
import shortid from 'shortid'
const notesSources = computed(() => ({
local: true,
firebase: initialized.value && user.value
}))
type notesSourceValues = keyof typeof notesSources.value | null
const activeNotesSource = ref<notesSourceValues>(null)
watchEffect(() => {
const getSource = (): notesSourceValues => {
if (!initialized.value) return null
return user.value ? 'firebase' : 'local'
}
activeNotesSource.value = getSource()
})
const baseNotes = ref<{ [noteId: string]: BaseNote }>({})
watch(
baseNotes,
() => {
console.log(`Sync base notes with ${activeNotesSource.value}`, baseNotes.value)
if (!activeNotesSource.value) return
if (activeNotesSource.value === 'local') {
console.log('sync with local')
localStorage.setItem('notes', JSON.stringify(baseNotes.value))
} else if (activeNotesSource.value === 'firebase') {
console.log('sync with firebase')
}
},
{ deep: true }
)
export const notes = computed<Note[]>(() => {
return Object.entries(baseNotes.value)
.map(([, note]) => ({
...note,
wordCount: note.content.split(' ').filter((word) => word.length > 0).length
}))
.sort((a, b) => b.modified - a.modified) as Note[]
})
watch(notes, () => {
if (notes.value.length > 0 && !activeNote.value && activeViewMode.value.name === 'Note')
setActiveNote(rootNote.value?.id)
})
const activeNoteId = ref<string>()
export const activeNote = computed(() => notes.value.find((note) => note.id === activeNoteId.value))
watch(activeNote, () => {
if (activeNote.value) {
useTitle(`${activeNote.value.title} | Contexted`)
}
})
export const setActiveNote = (noteId: string | undefined) => {
if (noteId) {
activeNoteId.value = noteId
activeViewMode.value = viewModes.find((mode) => mode.name === 'Note') || viewModes[0]
}
}
export const rootNote = computed<Note | undefined>(() => {
const rootNote = notes.value.find((note: Note) => note.isRoot)
return rootNote
})
export const setRootNote = (noteId: string) => {
if (rootNote.value) {
const updatedRootNote = { ...baseNotes.value[rootNote.value.id], isRoot: false }
updateNote(updatedRootNote.id, updatedRootNote)
}
const note = { ...baseNotes.value[noteId], isRoot: true }
updateNote(noteId, note)
setActiveNote(noteId)
}
export const setDefaultNotes = (defaultNotes: BaseNote[]) => {
defaultNotes.forEach((defaultNote) => {
baseNotes.value[defaultNote.id] = defaultNote
})
}
export const getNoteById = (noteId: string) => {
return notes.value.find((note) => note.id === noteId)
}
export const getNoteByTitle = (title: string) => {
return notes.value.find((note) => note.title === title)
}
export const findNotesByByTitle = (title: string) => {
const titleLowerCase = title.toLowerCase()
return notes.value.filter((note) => note.title.toLowerCase().includes(titleLowerCase))
}
export const findNotes = (query: string): Note[] => {
const removeMdFromText = (mdText: string): string => {
const div = document.createElement('div')
div.innerHTML = mdToHtml(mdText)
const textWithoutMd = div.textContent || div.innerText || ''
return textWithoutMd
}
return notes.value.filter((note) => {
const matchTitle = note.title.toLowerCase().includes(query.toLowerCase())
const matchContent = removeMdFromText(note.content).toLowerCase().includes(query.toLowerCase())
return matchTitle || matchContent
})
}
export const updateNote = (noteId: string, note: BaseNote) => {
const updatedNote: BaseNote = {
...note,
modified: new Date().getTime()
}
baseNotes.value[noteId] = updatedNote
}
export const addNote = (title: string, content: string, goToNote: boolean = false) => {
const id = shortid.generate()
const newNote: BaseNote = {
id,
title,
content,
created: new Date().getTime(),
modified: new Date().getTime()
}
baseNotes.value[id] = newNote
if (goToNote) setActiveNote(id)
return newNote
}
export const deleteNote = (noteId: string) => {
delete baseNotes.value[noteId]
}
const getNoteLinksByNoteId = (noteId: string): string[] => {
const note = baseNotes.value[noteId]
const regex = /\[\[(.*?)\]\]/g
const links = getAllMatches(regex, note.content || '')
.map((to) => notes.value.find((note) => note.title === to[1])?.id || '')
.filter((noteId) => Object.keys(baseNotes.value).includes(noteId))
return [...links]
}
export const notesRelations = computed(() => {
const noteIds = Object.keys(baseNotes.value)
const relations = noteIds
.filter((id) => id !== undefined)
.map((id) => {
const to = getNoteLinksByNoteId(id)
return { id, to }
})
.map((noteRelations, _, notesRelations): NoteRelations => {
const from = [...notesRelations]
.map((noteRelation) =>
noteRelation.to.filter((toId) => toId === noteRelations.id).map(() => noteRelation.id)
)
.reduce((arr, elem) => arr.concat(elem), [])
.filter((value, index, self) => self.indexOf(value) === index)
return {
id: noteRelations.id,
to: noteRelations.to,
from
}
})
.reduce((notes, { id, to, from }) => {
notes[id] = { to, from }
return notes
}, {} as NotesRelations)
return relations
})
export function getNoteReferences(note: Note) {
const relations = notesRelations.value[note.id]
return relations
? (relations.from || [])
.map((noteId) => {
return notes.value.find((note) => note.id === noteId)
})
.filter((note): note is Note => note !== undefined)
: []
}
watch(
activeNotesSource,
() => {
if (!activeNotesSource.value) return
baseNotes.value = {}
if (activeNotesSource.value === 'local') {
try {
const localNotes = JSON.parse(localStorage.getItem('notes') || '{}')
baseNotes.value = localNotes
} catch (error) {
console.log(error)
}
} else if (activeNotesSource.value === 'firebase') {
console.log('get notes from firebase')
}
if (Object.entries(baseNotes.value).length === 0) {
setDefaultNotes(defaultNotes)
}
setActiveNote(rootNote.value?.id)
},
{ immediate: true }
)