sync with local storage

This commit is contained in:
2023-05-22 00:19:46 +02:00
parent 87c3ff52ef
commit 16c92ed33f
14 changed files with 95 additions and 67 deletions

1
components.d.ts vendored
View File

@@ -28,6 +28,7 @@ declare module '@vue/runtime-core' {
SkeletonNote: typeof import('./src/components/Skeleton/SkeletonNote.vue')['default']
SkeletonSidebarItem: typeof import('./src/components/Skeleton/SkeletonSidebarItem.vue')['default']
SkeletonTopBar: typeof import('./src/components/Skeleton/SkeletonTopBar.vue')['default']
Spinner: typeof import('./src/components/Spinner.vue')['default']
TopBar: typeof import('./src/components/TopBar.vue')['default']
}
}

View File

@@ -6,23 +6,15 @@ import { initialized } from '@/composables/useFirebase'
import firebase from 'firebase/compat/app'
import * as firebaseui from 'firebaseui'
const sideBarCollapsed = ref(windowIsMobile())
const sideBarCollapsed = ref<boolean>(windowIsMobile())
const Note = defineAsyncComponent(() => import('@/components/ViewModes/Note.vue'))
const ListView = defineAsyncComponent(() => import('@/components/ViewModes/ListView.vue'))
const Mindmap = defineAsyncComponent(() => import('@/components/ViewModes/Mindmap.vue'))
// const Note = defineAsyncComponent(() => import('@/components/ViewModes/Note.vue'))
// const ListView = defineAsyncComponent(() => import('@/components/ViewModes/ListView.vue'))
// const Mindmap = defineAsyncComponent(() => import('@/components/ViewModes/Mindmap.vue'))
const firebaseAuthUI =
firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebase.auth())
provide('firebaseAuthUI', firebaseAuthUI)
// const setMinHeight = () => {
// const app = document.querySelector('#app')
// app?.setAttribute('style', `min-height: ${window.innerHeight}px`)
// }
// const handleResize = useDebounceFn(() => setMinHeight(), 100, { maxWait: 100 })
// window.addEventListener('resize', handleResize)
// setMinHeight()
</script>
<template>
@@ -54,7 +46,6 @@ provide('firebaseAuthUI', firebaseAuthUI)
<Mindmap v-else-if="activeViewMode.name === 'Mindmap'" />
</template>
<SkeletonNote v-else />
<!-- <SkeletonNote /> -->
</main>
</div>
</template>

View File

@@ -38,7 +38,7 @@ const editorConfig = {
ParagraphPlugin,
ListPlugin,
AutoformatPlugin,
ContextedPlugin,
ContextedPlugin
],
toolbar: {
items: [
@@ -51,10 +51,10 @@ const editorConfig = {
'redo',
'heading',
'bulletedList',
'numberedList',
],
'numberedList'
]
},
placeholder: 'Click here to start typing...',
placeholder: 'Click here to start typing...'
}
const editorElement = ref<HTMLInputElement | null>(null)
@@ -70,10 +70,10 @@ const handleClick = ({ data }: { data: any }) => {
}
const autocompleteRef = ref<InstanceType<typeof Autocomplete> | null>(null)
const showAutocomplete = ref(false)
const autocompleteStyle = ref({})
const autocompleteText = ref('')
const autocompleteReverse = ref(false)
const showAutocomplete = ref<boolean>(false)
const autocompleteStyle = ref<{ [key: string]: any }>({})
const autocompleteText = ref<string>('')
const autocompleteReverse = ref<boolean>(false)
const handleAutocomplete = async (event: AutocompleteEvent) => {
const position = event.position
@@ -84,7 +84,7 @@ const handleAutocomplete = async (event: AutocompleteEvent) => {
)
autocompleteStyle.value = {
top: `${position.top - rect.top + lineHeight}px`,
left: `${position.left - rect.left}px`,
left: `${position.left - rect.left}px`
}
}
autocompleteText.value = event.autocompleteText || ''
@@ -99,12 +99,10 @@ const handleAutocomplete = async (event: AutocompleteEvent) => {
editorRect &&
autocompleteRect.bottom > editorRect.bottom
) {
const autocompleteHeight = parseFloat(
window.getComputedStyle(autocompleteElem).height
)
const autocompleteHeight = parseFloat(window.getComputedStyle(autocompleteElem).height)
autocompleteStyle.value = {
...autocompleteStyle.value,
top: `${position.top - editorRect.top - autocompleteHeight}px`,
top: `${position.top - editorRect.top - autocompleteHeight}px`
}
autocompleteReverse.value = true
} else {

View File

@@ -5,7 +5,7 @@ const emit = defineEmits<{
active: [active: boolean]
}>()
const active = ref(false)
const active = ref<boolean>(false)
watch(active, () => {
if (!active.value) {
query.value = ''
@@ -14,7 +14,7 @@ watch(active, () => {
emit('active', active.value)
})
const query = ref('')
const query = ref<string>('')
const results = computed<Note[]>(() => {
return query.value ? findNotes(query.value) : notes.value
})

View File

@@ -0,0 +1 @@
<template>loading...</template>

View File

@@ -11,7 +11,7 @@ const emit = defineEmits<{
toggleSideBar: []
}>()
const searchActive = ref(false)
const searchActive = ref<boolean>(false)
const signOut = async (close: () => Promise<boolean>) => {
await firebaseSignOut()
@@ -19,7 +19,7 @@ const signOut = async (close: () => Promise<boolean>) => {
}
const authUI: any = inject('firebaseAuthUI')
const authModalInitialStateOpen = ref(authUI.isPendingRedirect())
const authModalInitialStateOpen = ref<boolean>(authUI.isPendingRedirect())
</script>
<template>
<div

View File

@@ -10,7 +10,7 @@ const notesWithReferences = computed(() => {
})
const selectedNotes = ref<{ [key: string]: Boolean }>({})
const countSelectedNotes = computed(
const countSelectedNotes = computed<number>(
() => Object.entries(selectedNotes.value).filter(([, selected]) => Boolean(selected)).length
)
@@ -18,7 +18,7 @@ const toggleRow = (note: Note) => {
if (!note.isRoot) selectedNotes.value[note.id] = !selectedNotes.value[note.id]
}
const filter = ref('')
const filter = ref<string>('')
const deleteSelectedNotes = (closeModal: () => void) => {
closeModal()

View File

@@ -17,7 +17,7 @@ const emit = defineEmits<{
update: [note: Note]
}>()
const noteTitle = ref(props.note.title)
const noteTitle = ref<string>(props.note.title)
watch(noteTitle, () => {
const updatedNote: Note = { ...props.note, title: noteTitle.value }
emit('update', updatedNote)

View File

@@ -29,6 +29,6 @@ export const initializeFirebase = () => {
})
}
export const initialized = computed(() => user.value !== undefined)
export const initialized = computed<boolean>(() => user.value !== undefined)
export const signOut = () => firebase.auth().signOut()

View File

@@ -1,13 +1,34 @@
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 baseNotes = reactive<{ [noteId: string]: BaseNote }>({})
const notesSource = computed<'firebase' | 'local' | null>(() => {
if (!initialized.value) return null
return user.value ? 'firebase' : 'local'
})
const baseNotes = ref<{ [noteId: string]: BaseNote }>({})
watch(
baseNotes,
() => {
console.log(`Sync base notes with ${notesSource.value}`, baseNotes.value)
if (!notesSource.value) return
if (notesSource.value === 'local') {
console.log('sync with local')
localStorage.setItem('notes', JSON.stringify(baseNotes.value))
} else if (notesSource.value === 'firebase') {
console.log('sync with firebase')
}
},
{ deep: true }
)
export const notes = computed<Note[]>(() => {
return Object.entries(baseNotes)
return Object.entries(baseNotes.value)
.map(([, note]) => ({
...note,
wordCount: note.content.split(' ').filter((word) => word.length > 0).length
@@ -40,17 +61,17 @@ export const rootNote = computed<Note | undefined>(() => {
export const setRootNote = (noteId: string) => {
if (rootNote.value) {
const updatedRootNote = { ...baseNotes[rootNote.value.id], isRoot: false }
const updatedRootNote = { ...baseNotes.value[rootNote.value.id], isRoot: false }
updateNote(updatedRootNote.id, updatedRootNote)
}
const note = { ...baseNotes[noteId], isRoot: true }
const note = { ...baseNotes.value[noteId], isRoot: true }
updateNote(noteId, note)
setActiveNote(noteId)
}
export const setDefaultNotes = (defaultNotes: BaseNote[]) => {
defaultNotes.forEach((defaultNote) => {
baseNotes[defaultNote.id] = defaultNote
baseNotes.value[defaultNote.id] = defaultNote
})
}
@@ -86,7 +107,7 @@ export const updateNote = (noteId: string, note: BaseNote) => {
...note,
modified: new Date().getTime()
}
baseNotes[noteId] = updatedNote
baseNotes.value[noteId] = updatedNote
}
export const addNote = (title: string, content: string, goToNote: boolean = false) => {
@@ -98,38 +119,26 @@ export const addNote = (title: string, content: string, goToNote: boolean = fals
created: new Date().getTime(),
modified: new Date().getTime()
}
baseNotes[id] = newNote
baseNotes.value[id] = newNote
if (goToNote) setActiveNote(id)
return newNote
}
export const deleteNote = (noteId: string) => {
delete baseNotes[noteId]
delete baseNotes.value[noteId]
}
const getNoteLinksByNoteId = (noteId: string): string[] => {
const note = baseNotes[noteId]
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).includes(noteId))
.filter((noteId) => Object.keys(baseNotes.value).includes(noteId))
return [...links]
}
interface NoteRelations {
id: string
to: string[]
from: string[]
}
interface NotesRelations {
[noteId: string]: {
to: string[]
from: string[]
}
}
export const notesRelations = computed(() => {
const noteIds = Object.keys(baseNotes)
const noteIds = Object.keys(baseNotes.value)
const relations = noteIds
.filter((id) => id !== undefined)
.map((id) => {
@@ -166,3 +175,22 @@ export function getNoteReferences(note: Note) {
.filter((note): note is Note => note !== undefined)
: []
}
watch(
notesSource,
() => {
if (!notesSource.value) return
baseNotes.value = {}
if (notesSource.value === 'local') {
const localNotes = JSON.parse(localStorage.getItem('notes') || '{}')
baseNotes.value = localNotes
} else if (notesSource.value === 'firebase') {
console.log('get notes from firebase')
}
if (Object.entries(baseNotes.value).length === 0) {
setDefaultNotes(defaultNotes)
}
setActiveNote(rootNote.value?.id)
},
{ immediate: true }
)

View File

@@ -3,4 +3,4 @@ export const viewModes: ViewMode[] = [
{ name: 'List', icon: 'fas fa-list fa-fw' },
{ name: 'Mindmap', icon: 'fas fa-project-diagram fa-fw' },
]
export const activeViewMode = ref(viewModes[0])
export const activeViewMode = ref<ViewMode>(viewModes[0])

View File

@@ -2,17 +2,15 @@ import { createApp } from 'vue'
import '@/style.scss'
import '@fortawesome/fontawesome-free/css/all.min.css'
import App from './App.vue'
import { setDefaultNotes } from '@/composables/useNotes'
import { defaultNotes } from '@/utils/defaultNotes'
import { usePreferredDark, useFavicon } from '@vueuse/core'
import { initializeFirebase } from '@/composables/useFirebase'
initializeFirebase()
const isDark = usePreferredDark()
const favicon = computed(() => (isDark.value ? '/contexted-white.ico' : '/contexted-black.ico'))
const favicon = computed<string>(() =>
isDark.value ? '/contexted-white.ico' : '/contexted-black.ico'
)
useFavicon(favicon)
setDefaultNotes(defaultNotes)
createApp(App).mount('#app')

View File

@@ -5,14 +5,12 @@
@import '@fontsource/source-sans-pro/300';
html {
height: fill-available;
height: -webkit-fill-available;
height: stretch;
}
body {
min-height: 100vh;
min-height: fill-available;
min-height: -webkit-fill-available;
min-height: stretch;
font-family: 'Source Sans Pro', sans-serif;
overflow-y: scroll;
@apply flex flex-col bg-gray-100;

13
src/types.d.ts vendored
View File

@@ -23,5 +23,18 @@ declare global {
domElement?: HTMLElement
show: boolean
}
interface NoteRelations {
id: string
to: string[]
from: string[]
}
interface NotesRelations {
[noteId: string]: {
to: string[]
from: string[]
}
}
}
export {}