Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: firebase integration #11

Merged
merged 9 commits into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
VITE_FIREBASE_API_KEY=firebase_api_key
VITE_FIREBASE_AUTH_DOMAIN=firebase_auth_domain
VITE_FIREBASE_PROJECT_ID=firebase_project_id
VITE_FIREBASE_STORAGE_BUCKET=firebase_storage_bucket
VITE_FIREBASE_MESSAGING_SENDER=firebase_messaging_sender
VITE_FIREBASE_APP_ID=firebase_app_id
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ dist-ssr
coverage
*.local

.env

/cypress/videos/
/cypress/screenshots/

Expand Down
1,585 changes: 1,568 additions & 17 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@vueuse/core": "^10.4.1",
"bulma": "^0.9.4",
"firebase": "^10.4.0",
"pinia": "^2.1.6",
"vue": "^3.3.4",
"vue-router": "^4.2.4",
Expand Down
5 changes: 5 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<script setup>
import { useNotesStore } from '@/store'
import { RouterView } from 'vue-router'
import { NavBar } from '@/components'
import { onMounted } from 'vue'

const notesStore = useNotesStore()
onMounted(notesStore.getAllNotes)
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Layout/NavBar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<nav class="navbar is-success" role="navigation" aria-label="main navigation">
<nav class="navbar is-info" role="navigation" aria-label="main navigation">
<div class="container is-max-desktop px-2">
<div class="navbar-brand">
<RouterLink to="/" class="navbar-item is-size-4 is-family-monospace is-clickable">
Expand Down
54 changes: 35 additions & 19 deletions src/components/Notes/NoteCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,29 @@
{{ note.content }}
</p>
</div>
<div class="has-text-right has-text-grey-light mt-2">
<small> {{ noteContentLengthText }}</small>
</div>

<div class="has-text-right has-text-grey-light">
<small>
Created at:
<time :datetime="note.createdAt"> {{ note.createdAt }} </time>
</small>
</div>

<div v-if="note.updatedAt" class="has-text-right has-text-grey-light">
<small>
Updated at:
<time :datetime="note.updatedAt"> {{ note.updatedAt }} </time>
</small>
<div class="note-details">
<div>
<div class="has-text-grey-light">
<small>
Created at:
<time :datetime="note.createdAt">
{{ createdAt.value }}
</time>
</small>
</div>

<div v-if="note.updatedAt" class="has-text-grey-light">
<small>
Updated at:
<time :datetime="note.updatedAt"> {{ updatedAt.value }} </time>
</small>
</div>
</div>

<div class="has-text-grey-light">
<small> {{ noteContentLengthText }}</small>
</div>
</div>
</div>

Expand Down Expand Up @@ -59,8 +66,8 @@
<script setup>
import { defineProps, computed, ref, defineEmits, reactive } from 'vue'
import { ModalDeleteNote } from '@/components'
import { useNotesStore } from '@/stores/notes'
import { useToast } from 'vue-toastification'
import { useDateFormat } from '@vueuse/core'
import { useNotesStore } from '@/store'
import { useRouter } from 'vue-router'

const router = useRouter()
Expand Down Expand Up @@ -90,7 +97,6 @@ const enableEditingNote = () => {
return () => clearTimeout(timeout)
}

const toast = useToast()
const storeNotes = useNotesStore()
const { deleteNoteHandler } = storeNotes

Expand All @@ -105,7 +111,6 @@ const deleteNote = () => {

const timeout = setTimeout(() => {
deleteNoteHandler(props.note.id)
toast.success('Note deleted successfully!')
}, 500)

return () => clearTimeout(timeout)
Expand All @@ -115,6 +120,11 @@ const noteContentLengthText = computed(() => {
const length = props.note.content.length
return `${length} ${length === 1 ? 'character' : 'characters'}`
})

const formatDate = (date) => useDateFormat(date, 'YYYY-MM-DD - HH:mm:ss')

const createdAt = computed(() => formatDate(props.note.createdAt))
const updatedAt = computed(() => formatDate(props.note.updatedAt))
</script>

<style>
Expand All @@ -129,4 +139,10 @@ const noteContentLengthText = computed(() => {
.action-button {
border-radius: 0 !important;
}

.note-details {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
</style>
2 changes: 1 addition & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const app = createApp(App)

const toastOptions = {
pauseOnHover: false,
timeout: 3000,
timeout: 2500,
toastClassName: 'toast',
maxToasts: 3
}
Expand Down
15 changes: 15 additions & 0 deletions src/services/firebase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getFirestore } from 'firebase/firestore'
import { initializeApp } from 'firebase/app'

const firebaseConfig = {
apiKey: import.meta.env.VITE_VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER,
appId: import.meta.env.VITE_FIREBASE_APP_ID
}

const app = initializeApp(firebaseConfig)

export const firebaseDb = getFirestore(app)
1 change: 1 addition & 0 deletions src/services/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './firebase'
1 change: 1 addition & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './notes'
108 changes: 108 additions & 0 deletions src/store/notes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { useToast } from 'vue-toastification'
import { firebaseDb } from '@/services'
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import {
collection,
onSnapshot,
deleteDoc,
updateDoc,
orderBy,
addDoc,
query,
doc
} from 'firebase/firestore'

export const useNotesStore = defineStore('notes', () => {
const notesCollection = collection(firebaseDb, 'notes')
const toast = useToast()

const notesLoading = ref(false)
const notes = ref([])

const getNoteContent = computed(() => {
return (id) => {
const note = notes.value?.find((note) => note.id === id)
return note ? note.content : null
}
})

const totalNotesCount = computed(() => notes.value.length)

const totalCharactersCount = computed(() => {
return notes.value.reduce((acc, note) => acc + note.content.length, 0)
})

const getAllNotes = async () => {
try {
notesLoading.value = true

const q = query(notesCollection, orderBy('createdAt', 'desc'))
onSnapshot(q, (querySnapshot) => {
const allNotes = []

querySnapshot.forEach((doc) => {
allNotes.push({
id: doc.id,
...doc.data()
})
})

notes.value = allNotes
notesLoading.value = false
})
} catch (error) {
toast.error('Notes could not be fetched!')
}
}

const noteAddHandler = async (newNote, cb = () => {}) => {
try {
await addDoc(notesCollection, {
createdAt: new Date().toISOString(),
content: newNote.trim(),
updatedAt: null
})

toast.success('Note added successfully!')
cb()
} catch (error) {
toast.error('Note could not be added!')
}
}

const editNoteHandler = async (id, newContent) => {
try {
await updateDoc(doc(notesCollection, id), {
content: newContent,
updatedAt: new Date().toISOString()
})

toast.success('Note updated successfully!')
} catch (error) {
console.log(error)
toast.error('Note could not be updated!')
}
}

const deleteNoteHandler = async (id) => {
try {
await deleteDoc(doc(notesCollection, id))
toast.success('Note deleted successfully!')
} catch (error) {
toast.error('Note could not be deleted!')
}
}

return {
totalCharactersCount,
deleteNoteHandler,
totalNotesCount,
editNoteHandler,
getNoteContent,
noteAddHandler,
notesLoading,
getAllNotes,
notes
}
})
51 changes: 0 additions & 51 deletions src/stores/notes.js

This file was deleted.

14 changes: 6 additions & 8 deletions src/views/ViewEditNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,20 @@

<script setup>
import { useRoute, useRouter } from 'vue-router'
import { useNotesStore } from '@/stores/notes'
import { useToast } from 'vue-toastification'
import { AddEditNote } from '@/components'
import { useNotesStore } from '@/store'
import { onMounted, ref } from 'vue'

const noteStore = useNotesStore()
const isCanceling = ref(false)
const noteContent = ref('')
const router = useRouter()
const route = useRoute()
const toast = useToast()

const { getNoteContent, editNoteHandler } = noteStore

const editNote = () => {
editNoteHandler(+route.params.id, noteContent.value)
toast.success('Note edited successfully!')
editNoteHandler(route.params.id, noteContent.value)
router.push('/')
}

Expand All @@ -62,9 +59,10 @@ const cancelHandler = () => {
}

onMounted(() => {
const note = getNoteContent(+route.params.id)
if (note) {
noteContent.value = note
const content = getNoteContent(route.params.id)

if (content) {
noteContent.value = content
} else {
router.push('/')
}
Expand Down
Loading
Loading