Skip to content

Commit

Permalink
feat: add upvote system
Browse files Browse the repository at this point in the history
Implemented firebase
  • Loading branch information
rupali-codes authored Apr 30, 2024
2 parents 74cf00d + 4fa07e7 commit 8cddab6
Show file tree
Hide file tree
Showing 9 changed files with 2,765 additions and 19 deletions.
54 changes: 54 additions & 0 deletions app/api/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import admin from 'firebase-admin';
import { promises as fs } from 'fs';
import path from 'path';
import { NextResponse } from 'next/server';
import { customInitApp } from 'lib/firebase-admin-config';

customInitApp();

const folders = [
'BlockChain',
'DSA',
'artificial_intelligence',
'backend',
'cloud_computing_platforms',
'competitive_programming',
'cybersecurity',
'data_structures',
'devops',
'frontend',
'internet_of_things',
'languages',
'open_source',
'other',
'placement_prep',
'resources',
'technical-writing',
'youtube'
];

export async function GET() {
try {
const firestore = admin.firestore();
const mainDirectoryPath = path.join(__dirname, '../../../../database');
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];
const folderPath = path.join(mainDirectoryPath, folder);

const files = await fs.readdir(folderPath);
for (let j = 0; j < files.length; j++) {
const file = files[j];
const filePath = path.join(folderPath, file);

const fileContent = await fs.readFile(filePath, 'utf-8');
const jsonData = JSON.parse(fileContent);
console.log(jsonData);
firestore.collection('resources').doc(folder).set({ resources: jsonData });
}
}
return NextResponse.json({ message: 'Imported to Firestore' });
} catch (error) {
console.error('Error occurred: \n', error);
return NextResponse.json({ message: 'Error occurred' });
}
}
110 changes: 108 additions & 2 deletions components/Cards/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ import { AiOutlineRead } from 'react-icons/ai'
import { HiOutlineExternalLink } from 'react-icons/hi'
import { CopyToClipboard } from 'components/CopyToClipboard/CopyToClipboard'
import type { IData } from 'types'
import {
collection,
doc,
where,
query,
getDocs,
setDoc,
getDoc,
} from 'firebase/firestore'
import { db } from '../../lib/firebase-config'
import { Timestamp } from 'firebase/firestore'
import Image from 'next/image'

import Bookmark from 'components/Bookmark/Bookmark'

interface CardProps {
Expand All @@ -16,6 +29,92 @@ export const Card: FC<CardProps> = ({ data, onClick }) => {
const descriptionRef = useRef<HTMLParagraphElement>(null)
const [isOverflow, setIsOverflow] = useState(false)
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/
const id = data.url.replace(/[^\w\s]/gi, '')

const [upvoteCount, setUpvoteCount] = useState(0)
const [isUpvoted, setIsUpvoted] = useState(false)
const timestamp = Timestamp.fromDate(new Date())
const date = timestamp.toDate()
const user = {
name: 'Vidip',
uid: 1234,
}
const docRef = doc(db, 'resources', id)
const save = async () => {
await setDoc(
docRef,
{
name: name,
},
{ merge: true }
)
}

const addUserToAssetBookmark = async () => {
try {
const subcollectionRef = collection(db, 'resources')
const assetQuery = query(subcollectionRef, where('name', '==', data.name))
const assetQuerySnapshot = await getDocs(assetQuery)
console.log('Asset Query: ', assetQuery)
console.log('Asset Query Snapshot: ', assetQuerySnapshot)
if (assetQuerySnapshot.empty) {
console.log('Asset not found')
return
}

const assetDocSnapshot = assetQuerySnapshot.docs[0]
const assetDocRef = doc(db, 'resources', data.name)
const assetData = assetDocSnapshot.data()
console.log('Asset Data: ', assetData)

const upvotes = assetData.upvotes || {}
const userUid = user.uid

if (upvotes[userUid]) {
// User has already upvoted, so remove their upvote
delete upvotes[userUid]
} else {
// User has not upvoted, so add their upvote
upvotes[userUid] = true
}
await setDoc(assetDocRef, {
name: name,
description: description,
url: url,
upvotedBy: user,
created: date, // Keep existing data
upvotes: upvotes,
})

const updatedAssetDoc = await getDoc(assetDocRef)
if (!updatedAssetDoc.exists()) {
console.log('Asset document not found')
return
}
const updatedUpvotes = updatedAssetDoc.data().upvotes || {}
const upvoteCount = Object.keys(updatedUpvotes).length
setUpvoteCount(upvoteCount)
console.log(upvoteCount)
} catch (error) {
console.error('Error adding user to asset upvotes:', error)
}
}

const toggleUpvote = () => {
setIsUpvoted((p) => !p)
}

const handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
e.preventDefault()
toggleUpvote()
save()
await addUserToAssetBookmark()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Img = ({ url }: any) => {
return <Image src={`${url}`} alt={'altimage'} width={40} height={40} />
}

useEffect(() => {
if (descriptionRef.current) {
Expand All @@ -25,7 +124,6 @@ export const Card: FC<CardProps> = ({ data, onClick }) => {
)
}
}, [])

return (
<article className="z-10 h-full w-full rounded-3xl dark:bg-slate-800 dark:border dark:border-theme-primary/8 dark:text-text-primary dark:shadow-sm bg-theme-primary-light border border border-theme-secondary/25">
<div className="card-body">
Expand Down Expand Up @@ -56,8 +154,16 @@ export const Card: FC<CardProps> = ({ data, onClick }) => {
</span>
)}
</div>
<div className="flex">
<p className="text-3xl">{upvoteCount}</p>
<button onClick={handleClick}>
<Img
url={isUpvoted ? '/upvoteFilled.png' : '/upvote.png'}
toggleUpvote={toggleUpvote}
/>
</button>
</div>
<footer className="grid grid-cols-2 gap-x-4 md:grid-cols-1 lg:grid-cols-2">
<CopyToClipboard url={url} />
<a
onClick={(e) => e.stopPropagation()}
href={url}
Expand Down
14 changes: 14 additions & 0 deletions lib/firebase-admin-config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import admin from 'firebase-admin'
import { initializeApp, cert, getApps } from 'firebase-admin/app'
import { serviceAccount } from '../service-account'

const firebaseAdminConfig = {
credential: cert(serviceAccount as admin.ServiceAccount),
databaseURL: 'https://linkshub-3c1b9-default-rtdb.firebaseio.com',
}

export function customInitApp() {
if (!getApps().length) {
initializeApp(firebaseAdminConfig)
}
}
20 changes: 20 additions & 0 deletions lib/firebase-config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getAuth, GithubAuthProvider } from 'firebase/auth'
import { initializeApp, getApps } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'

const firebaseConfig = {
apiKey: process.env.NEXT_FIREBASE_API_KEY,
authDomain: process.env.NEXT_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_FIREBASE_APP_ID,
}

const app =
getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0]
const db = getFirestore(app)
const auth = getAuth(app)
const provider = new GithubAuthProvider()
// console.log(app);
export { app, db, auth, provider, firebaseConfig }
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"eslint": "8.37.0",
"eslint-config-next": "13.4.3",
"eslint-config-prettier": "^8.8.0",
"firebase": "^10.5.0",
"firebase-admin": "^12.0.0",
"firebase-functions": "^4.7.0",
"firestore-export-import": "^1.4.0",
"git": "^0.1.5",
"next": "13.4.3",
"next-themes": "^0.2.1",
Expand Down
Loading

0 comments on commit 8cddab6

Please sign in to comment.