Finished with the tutorial

This commit is contained in:
Turingon 2024-08-06 20:01:23 +02:00
parent 409bc2c3cc
commit b871cd1a54
11 changed files with 264 additions and 127 deletions

View file

@ -2,7 +2,7 @@
const nextConfig = {
images: {
domains:
["lh3.googleusercontent.com"]
["lh3.googleusercontent.com", "firebasestorage.googleapis.com"]
}
}

BIN
public/external.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
public/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
public/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -9,8 +9,9 @@ export const GET = async (req, {params})=>{
try{
const post = await prisma.post.findUnique({
const post = await prisma.post.update({
where: {slug},
data: {views:{increment:1}},
include: {user: true},
});
return new NextResponse(JSON.stringify(post, {status: 200}));

View file

@ -1,34 +1,58 @@
import { getAuthSession } from "@/utils/auth";
import prisma from "@/utils/connect";
import { NextResponse } from "next/server";
export const GET = async (req)=>{
export const GET = async (req) => {
const { searchParams } = new URL(req.url);
const {searchParams} = new URL(req.url);
const page = searchParams.get("page");
const cat = searchParams.get("cat");
const POST_PER_PAGE = 2;
const page = searchParams.get("page");
const cat = searchParams.get("cat");
const query = {
take: POST_PER_PAGE,
skip: POST_PER_PAGE * (page - 1),
where: {
...(cat && {catSlug: cat}),
},
};
const POST_PER_PAGE = 2;
try{
const query = {
take: POST_PER_PAGE,
skip: POST_PER_PAGE * (page - 1),
where: {
...(cat && { catSlug: cat }),
},
};
try {
const [posts, count] = await prisma.$transaction([
prisma.post.findMany(query),
prisma.post.count({ where: query.where }),
]);
return new NextResponse(JSON.stringify({ posts, count }, { status: 200 }));
} catch (err) {
console.log(err);
return new NextResponse(
JSON.stringify({ message: "Something went wrong!" }, { status: 500 })
);
}
};
const [posts,count] = await prisma.$transaction(
[
prisma.post.findMany(query),
prisma.post.count({where:query.where}),
]
);
return new NextResponse(JSON.stringify({posts, count}, {status:200}));
// CREATE A POST
export const POST = async (req) => {
const session = await getAuthSession();
if (!session) {
return new NextResponse(
JSON.stringify({ message: "Not Authenticated!" }, { status: 401 })
);
}
} catch(err) {
console.log(err);
return new NextResponse(JSON.stringify({message: "Something went wrong!"}, {status:500}));
}
try {
const body = await req.json();
const post = await prisma.post.create({
data: { ...body, userEmail: session.user.email },
});
return new NextResponse(JSON.stringify(post, { status: 200 }));
} catch (err) {
console.log(err);
return new NextResponse(
JSON.stringify({ message: "Something went wrong!" }, { status: 500 })
);
}
};

View file

@ -5,7 +5,7 @@ import Comments from "@/components/comments/Comments";
const getData = async (slug) => {
const res = await fetch(
`http://localhost:3000/api/posts/${slug}`,
`http://localhost:3000/api/posts/${slug}?popular=true`,
{
cache: "no-store",
}

View file

@ -1,71 +1,154 @@
"use client";
import styles from "./writePage.module.css";
import Image from "next/image";
import { useState } from "react";
import ReactQuill from "react-quill";
import styles from "./writePage.module.css";
import { useEffect, useState } from "react";
import "react-quill/dist/quill.bubble.css";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useSession } from "next-auth/react";
import {
getStorage,
ref,
uploadBytesResumable,
getDownloadURL,
} from "firebase/storage";
import { app } from "@/utils/firebase";
import ReactQuill from "react-quill";
const WritePage = () => {
const { status } = useSession();
const router = useRouter();
const {status} = useSession();
const router = useRouter();
const [file, setFile] = useState(null);
const [open, setOpen] = useState(false);
const [value, setValue] = useState("");
const [open, setOpen] = useState(false);
const [file, setFile] = useState(null);
const [media, setMedia] = useState("");
const [value, setValue] = useState("");
const [title, setTitle] = useState("");
const [catSlug, setCatSlug] = useState("");
if (status === "loading") {
return <div className={styles.loading}>Loading...</div>
}
if (status === "authenticated") {
router.push("/");
useEffect(() => {
const storage = getStorage(app);
const upload = () => {
const name = new Date().getTime() + file.name;
const storageRef = ref(storage, name);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
}
},
(error) => {},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setMedia(downloadURL);
});
}
);
};
file && upload();
}, [file]);
if (status === "loading") {
return <div className={styles.loading}>Loading...</div>;
}
if (status === "unauthenticated") {
router.push("/");
}
const slugify = (str) =>
str
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "")
.replace(/[\s_-]+/g, "-")
.replace(/^-+|-+$/g, "");
const handleSubmit = async () => {
const res = await fetch("/api/posts", {
method: "POST",
body: JSON.stringify({
title,
desc: value,
img: media,
slug: slugify(title),
catSlug: catSlug || "style", //If not selected, choose the general category
}),
});
if (res.status === 200) {
const data = await res.json();
router.push(`/posts/${data.slug}`);
}
};
return (
<div className={styles.container}>
<input type="text" placeholder="Title" className={styles.input}/>
<div className={styles.editor}>
<button className={styles.button} onClick={()=> setOpen(!open)}>
<Image src="/plus.png" alt="add" width={16} height={16}/>
</button>
{open && (
<div className={styles.add}>
<input
type="file"
id="image"
onChange={e=>setFile(e.target.files[0])}
style={ { display: "none" }}
/>
<button className={styles.addButton}>
<label htmlFor="image">
<Image src="/image.png" alt="" width={16} height={16} />
</label>
</button>
<button className={styles.addButton}>
<Image src="/external.png" alt="" width={16} height={16} />
</button>
<button className={styles.addButton}>
<Image src="/video.png" alt="" width={16} height={16} />
</button>
</div>
)}
<ReactQuill
className={styles.textArea}
theme="bubble"
value={value}
onChange={setValue}
placeholder="Tell your story..."
/>
</div>
<button className={styles.publish}>Publish</button>
</div>
)
}
return (
<div className={styles.container}>
<input
type="text"
placeholder="Title"
className={styles.input}
onChange={(e) => setTitle(e.target.value)}
/>
<select className={styles.select} onChange={(e) => setCatSlug(e.target.value)}>
<option value="style">style</option>
<option value="fashion">fashion</option>
<option value="food">food</option>
<option value="culture">culture</option>
<option value="travel">travel</option>
<option value="coding">coding</option>
</select>
<div className={styles.editor}>
<button className={styles.button} onClick={() => setOpen(!open)}>
<Image src="/plus.png" alt="" width={16} height={16} />
</button>
{open && (
<div className={styles.add}>
<input
type="file"
id="image"
onChange={(e) => setFile(e.target.files[0])}
style={{ display: "none" }}
/>
<button className={styles.addButton}>
<label htmlFor="image">
<Image src="/image.png" alt="" width={16} height={16} />
</label>
</button>
<button className={styles.addButton}>
<Image src="/external.png" alt="" width={16} height={16} />
</button>
<button className={styles.addButton}>
<Image src="/video.png" alt="" width={16} height={16} />
</button>
</div>
)}
<ReactQuill
className={styles.textArea}
theme="bubble"
value={value}
onChange={setValue}
placeholder="Tell your story..."
/>
</div>
<button className={styles.publish} onClick={handleSubmit}>
Publish
</button>
</div>
);
};
export default WritePage;

View file

@ -1,63 +1,75 @@
.container {
.container{
position: relative;
display: flex;
flex-direction: column;
}
.select{
margin-bottom: 50px;
padding: 10px 20px;
margin-left: 50px;
width: max-content;
}
.editor {
display: flex;
gap: 20px;
height: 700px;
position: relative;
display: flex;
gap: 20px;
height: 700px;
position: relative;
}
.button, .addButton{
width: 36px;
height: 36px;
border-radius: 50%;
background-color: transparent;
border: 1px solid var(--textColor);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.button,
.addButton {
width: 36px;
height: 36px;
border-radius: 50%;
background-color: transparent;
border: 1px solid var(--textColor);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.addButton{
border-color: #1a8917;
.addButton {
border-color: #1a8917;
}
.add{
display: flex;
gap: 20px;
background-color: var(--bg);
position: absolute;
z-index: 999;
width: 100%;
left: 50px;
.add {
display: flex;
gap: 20px;
background-color: var(--bg);
position: absolute;
z-index: 999;
width: 100%;
left: 50px;
}
.input{
padding: 50px;
font-size: 64px;
border: none;
outline: none;
background-color: transparent;
padding: 50px;
font-size: 64px;
border: none;
outline: none;
background-color: transparent;
color: var(--textColor);
}
.input::placeholder{
color: #b3b3b1;
color: #b3b3b1;
}
.textArea{
width: 100%;
.textArea {
width: 100%;
}
.publish{
position: absolutee;
top: 30px;
right: 20px;
padding: 10px 20px;
border: none;
background-color: #1a8917;
color: white;
cursor: pointer;
border-radius: 20px;
position: absolute;
top: 0px;
right: 0px;
padding: 10px 20px;
border: none;
background-color: #1a8917;
color: white;
cursor: pointer;
border-radius: 20px;
}

17
src/utils/firebase.js Normal file
View file

@ -0,0 +1,17 @@
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: process.env.FIREBASE,
authDomain: "blog-62dc6.firebaseapp.com",
projectId: "blog-62dc6",
storageBucket: "blog-62dc6.appspot.com",
messagingSenderId: "261518728912",
appId: "1:261518728912:web:c02c5f459f32f23eb948d5"
};
// Initialize Firebase
export const app = initializeApp(firebaseConfig);