Skip to content

Commit

Permalink
Merge pull request #11 from Saba-Var/feat/firebase_integration
Browse files Browse the repository at this point in the history
feat: firebase integration
  • Loading branch information
Saba-Var authored Sep 24, 2023
2 parents c175f9c + 92b5a61 commit 1e54c69
Show file tree
Hide file tree
Showing 16 changed files with 1,771 additions and 113 deletions.
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

0 comments on commit 1e54c69

Please sign in to comment.