1
- import { useEffect , useState , useRef } from "react" ;
2
- import { useParams } from "react-router-dom" ;
3
- import axios , { AxiosError } from "axios" ;
4
- import { IPost } from "../types" ;
5
- import DOMPurify from "dompurify" ;
6
- import Loader from "../components/Loader" ;
1
+ import { useEffect , useState , useRef } from 'react' ;
2
+ import { useParams } from 'react-router-dom' ;
3
+ import axios , { AxiosError } from 'axios' ;
4
+ import { IPost } from '../types' ;
5
+ import DOMPurify from 'dompurify' ;
6
+ import { BiDislike , BiLike , BiSolidDislike , BiSolidLike } from "react-icons/bi" ;
7
+ import Loader from '../components/Loader'
7
8
8
9
const Post = ( ) => {
9
10
const { id } = useParams < { id : string } > ( ) ;
@@ -16,18 +17,21 @@ const Post = () => {
16
17
author : {
17
18
id : "" ,
18
19
username : "" ,
19
- email : "" ,
20
+ email : ""
20
21
} ,
21
- } ) ;
22
+ likes : 0 ,
23
+ dislikes : 0
24
+ } ) ;
22
25
const [ loading , setLoading ] = useState ( true ) ;
23
26
const [ error , setError ] = useState < string | null > ( null ) ;
24
27
const [ isPreview , setIsPreview ] = useState ( false ) ;
25
28
const ref = useRef < HTMLIFrameElement > ( null ) ;
26
- const [ height , setHeight ] = useState ( "0px" ) ;
29
+ const [ height , setHeight ] = useState ( '0px' ) ;
30
+ const [ userLiked , setUserLiked ] = useState ( false ) ;
31
+ const [ userDisliked , setUserDisliked ] = useState ( false ) ;
27
32
28
33
const onLoad = ( ) => {
29
- setHeight ( ref . current ?. contentWindow ?. document . body . scrollHeight + "px" ) ;
30
- console . log ( ref . current ?. contentWindow ?. document . body . scrollHeight ) ;
34
+ setHeight ( ref . current ?. contentWindow ?. document . body . scrollHeight + 'px' ) ;
31
35
} ;
32
36
33
37
useEffect ( ( ) => {
@@ -37,11 +41,8 @@ const Post = () => {
37
41
setPost ( response . data . post ) ;
38
42
setLoading ( false ) ;
39
43
} catch ( error ) {
40
- const axiosError = error as AxiosError < {
41
- error : string ;
42
- } > ;
43
-
44
- setError ( axiosError . response ?. data . error || "Failed to fetch the post" ) ;
44
+ const axiosError = error as AxiosError < { error : string } > ;
45
+ setError ( axiosError . response ?. data . error || 'Failed to fetch the post' ) ;
45
46
setLoading ( false ) ;
46
47
}
47
48
} ;
@@ -51,17 +52,91 @@ const Post = () => {
51
52
52
53
useEffect ( ( ) => {
53
54
onLoad ( ) ;
54
- } , [ isPreview , post . codeSnippet ] ) ;
55
+ } , [ isPreview , post ? .codeSnippet ] ) ;
55
56
56
57
const handleCopy = ( ) => {
57
- navigator . clipboard . writeText ( post . codeSnippet ) ;
58
- alert ( "Code snippet copied to clipboard" ) ;
58
+ if ( post ) {
59
+ navigator . clipboard . writeText ( post . codeSnippet ) ;
60
+ alert ( 'Code snippet copied to clipboard' ) ;
61
+ }
59
62
} ;
60
63
61
64
const togglePreview = ( ) => {
62
65
setIsPreview ( ! isPreview ) ;
63
66
} ;
64
67
68
+ useEffect ( ( ) => {
69
+ const fetchPost = async ( ) => {
70
+ try {
71
+ const response = await axios . get ( `/api/v1/posts/${ id } ` ) ;
72
+ setPost ( response . data . post ) ;
73
+ setLoading ( false ) ;
74
+ } catch ( error ) {
75
+ const axiosError = error as AxiosError < { error : string } > ;
76
+ setError ( axiosError . response ?. data . error || 'Failed to fetch the post' ) ;
77
+ setLoading ( false ) ;
78
+ }
79
+ } ;
80
+
81
+ fetchPost ( ) ;
82
+ } , [ id ] ) ;
83
+
84
+ useEffect ( ( ) => {
85
+ onLoad ( ) ;
86
+ } , [ isPreview , post ?. codeSnippet ] ) ;
87
+
88
+ useEffect ( ( ) => {
89
+ const userLikedStatus = localStorage . getItem ( `post-${ id } -liked` ) ;
90
+ const userDislikedStatus = localStorage . getItem ( `post-${ id } -disliked` ) ;
91
+ setUserLiked ( userLikedStatus === 'true' ) ;
92
+ setUserDisliked ( userDislikedStatus === 'true' ) ;
93
+ } , [ id ] ) ;
94
+
95
+ const handleLike = async ( ) => {
96
+ try {
97
+ const token = localStorage . getItem ( 'token' ) ;
98
+ if ( ! token ) {
99
+ alert ( 'You need to be logged in to like a post' ) ;
100
+ return ;
101
+ }
102
+ const response = await axios . post ( `/api/v1/posts/${ id } /like` , { } , {
103
+ headers : {
104
+ 'Authorization' : `Bearer ${ token } `
105
+ }
106
+ } ) ;
107
+ setPost ( prevPost => ( { ...prevPost , likes : response . data . likes , dislikes : response . data . dislikes } ) ) ;
108
+ setUserLiked ( true ) ;
109
+ setUserDisliked ( false ) ;
110
+ localStorage . setItem ( `post-${ id } -liked` , 'true' ) ;
111
+ localStorage . removeItem ( `post-${ id } -disliked` ) ;
112
+ } catch ( error ) {
113
+ alert ( 'like is done only once, no spam 😊' ) ;
114
+ }
115
+ } ;
116
+
117
+ const handleDislike = async ( ) => {
118
+ try {
119
+ const token = localStorage . getItem ( 'token' ) ;
120
+ if ( ! token ) {
121
+ alert ( 'You need to be logged in to dislike a post' ) ;
122
+ return ;
123
+ }
124
+ const response = await axios . post ( `/api/v1/posts/${ id } /dislike` , { } , {
125
+ headers : {
126
+ 'Authorization' : `Bearer ${ token } `
127
+ }
128
+ } ) ;
129
+ setPost ( prevPost => ( { ...prevPost , dislikes : response . data . dislikes , likes : response . data . likes } ) ) ;
130
+ setUserLiked ( false ) ;
131
+ setUserDisliked ( true ) ;
132
+ localStorage . setItem ( `post-${ id } -disliked` , 'true' ) ;
133
+ localStorage . removeItem ( `post-${ id } -liked` ) ;
134
+ } catch ( error ) {
135
+ alert ( 'Dislike is done only once, no spam 😊' ) ;
136
+ }
137
+ } ;
138
+
139
+
65
140
if ( loading ) {
66
141
return < Loader /> ;
67
142
}
@@ -95,12 +170,24 @@ const Post = () => {
95
170
< div className = "p-6 text-white max-w-screen-xl mx-auto" >
96
171
{ post && (
97
172
< >
98
- < button onClick = { ( ) => window . history . back ( ) } className = "mb-4 px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded" >
99
- < svg xmlns = "http://www.w3.org/2000/svg" className = "h-4 w-4 inline-block" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
100
- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M10 19l-7-7m0 0l7-7m-7 7h18" />
101
- </ svg >
173
+ < button onClick = { ( ) => window . history . back ( ) } className = "mb-4 px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded" >
174
+ < svg xmlns = "http://www.w3.org/2000/svg" className = "h-4 w-4 inline-block" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
175
+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M10 19l-7-7m0 0l7-7m-7 7h18" />
176
+ </ svg >
102
177
</ button >
103
- < h2 className = "text-2xl font-semibold mb-4" > { post . title } </ h2 >
178
+ < h2 className = "text-2xl font-semibold mr-4" > { post . title } </ h2 >
179
+ < button
180
+ onClick = { handleLike }
181
+ className = "px-4 py-2 my-3 rounded-md border-2 text-white text-sm mr-2"
182
+ >
183
+ { userLiked ? < BiSolidLike size = { 25 } /> : < BiLike size = { 25 } /> } { post . likes }
184
+ </ button >
185
+ < button
186
+ onClick = { handleDislike }
187
+ className = "px-4 py-2 rounded-md border-2 text-white text-sm"
188
+ >
189
+ { userDisliked ? < BiSolidDislike size = { 25 } /> : < BiDislike size = { 25 } /> } { post . dislikes }
190
+ </ button >
104
191
< p className = "mb-4" > { post . description } </ p >
105
192
< div className = "relative mb-4" >
106
193
{ isPreview ? (
0 commit comments