Skip to content

Commit bc71a5c

Browse files
authored
Merge pull request #37 from parlandin/develop
feat: add buy my coffe button and several small changes and fixes
2 parents 4c52359 + 8f91e75 commit bc71a5c

File tree

30 files changed

+19345
-25725
lines changed

30 files changed

+19345
-25725
lines changed

package-lock.json

Lines changed: 17912 additions & 25699 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
"typecheck": "tsc --noEmit"
1717
},
1818
"dependencies": {
19-
"@giscus/react": "^3.0.0",
19+
"@fingerprintjs/fingerprintjs": "^3.4.2",
2020
"@mdx-js/react": "^2.3.0",
2121
"axios": "^1.7.2",
2222
"babel-plugin-styled-components": "^2.1.4",
2323
"framer-motion": "^11.2.10",
24-
"gatsby": "^5.13.3",
24+
"gatsby": "^5.14.1",
2525
"gatsby-omni-font-loader": "^2.0.2",
2626
"gatsby-plugin-alias-imports": "^1.0.5",
2727
"gatsby-plugin-catch-links": "^5.13.1",
@@ -43,14 +43,18 @@
4343
"react": "^18.2.0",
4444
"react-dom": "^18.2.0",
4545
"react-helmet": "^6.1.0",
46+
"react-markdown": "^10.0.0",
4647
"react-paginate": "^8.2.0",
47-
"react-vertical-timeline-component": "^3.6.0",
4848
"reading-time": "^1.5.0",
49+
"rehype-raw": "^7.0.0",
50+
"rehype-sanitize": "^6.0.0",
51+
"remark-gfm": "^4.0.1",
4952
"slugify": "^1.6.6",
5053
"styled-components": "^6.1.9"
5154
},
5255
"devDependencies": {
5356
"@types/node": "^20.11.19",
57+
"@types/prismjs": "^1.26.5",
5458
"@types/react": "^18.2.55",
5559
"@types/react-dom": "^18.2.19",
5660
"@types/react-helmet": "^6.1.11",

src/api/axios.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import axios from "axios";
22

3-
const url = "https://parlan-blog-backend.vercel.app";
3+
const defaultPath = "http://localhost:3000";
4+
5+
const url = process.env.GATSBY_BACKEND_URL || defaultPath;
46

57
const instance = axios.create({
68
baseURL: url,
9+
withCredentials: true,
710
});
811

912
export default instance;

src/api/useGetWordOfTheDayData.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useState, useMemo } from "react";
22
import axios from "../api/axios";
3+
import useFingerprint from "@hooks/useFingerPrint";
34

45
interface WordOfTheDayInterface {
56
date: string;
@@ -20,14 +21,18 @@ const useGetHomeData = () => {
2021
credits: "",
2122
});
2223
const [loading, setLoading] = useState(true);
24+
const fingerprint = useFingerprint();
2325

2426
const getWordOfTheDay = async (signal: AbortSignal) => {
25-
26-
2727
try {
2828
const { data } = await axios.get<WordOfTheDayInterface>(
2929
"/get-word/json/word",
30-
{ signal }
30+
{
31+
signal,
32+
headers: {
33+
"x-fingerprint": fingerprint,
34+
},
35+
}
3136
);
3237

3338
setWordOfTheDay(data);
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import BuyCoffeeIcon from "@Icons/BuyCoffee";
2+
import React from "react";
3+
import * as S from "./styles";
4+
import { StaticImage } from "gatsby-plugin-image";
5+
import useNoBodyScroll from "@src/hooks/useNoBodyScroll";
6+
7+
interface CoffeeBModalProps {
8+
show: boolean;
9+
onClose: () => void;
10+
}
11+
12+
const CoffeeButton = () => {
13+
const [showModal, setShowModal] = React.useState(false);
14+
15+
const handleModal = () => {
16+
setShowModal((prev) => !prev);
17+
};
18+
19+
const closedModal = () => {
20+
setShowModal(false);
21+
};
22+
23+
return (
24+
<>
25+
<CoffeeModal show={showModal} onClose={closedModal} />
26+
<S.Container>
27+
<S.Button onClick={handleModal} title="botão ajudar o blog com um café">
28+
<BuyCoffeeIcon />
29+
Que tal um café?
30+
</S.Button>
31+
</S.Container>
32+
</>
33+
);
34+
};
35+
36+
const CoffeeModal: React.FC<CoffeeBModalProps> = ({ show, onClose }) => {
37+
useNoBodyScroll(show);
38+
return (
39+
<>
40+
{show && (
41+
<>
42+
<S.Overlay onClick={onClose} title="fechar modal" />
43+
44+
<S.Modal>
45+
<S.QrCode>
46+
<StaticImage
47+
src="../../images/bmc_qr.png"
48+
alt="Café"
49+
placeholder="blurred"
50+
layout="constrained"
51+
width={400}
52+
height={400}
53+
/>
54+
</S.QrCode>
55+
56+
<S.Text>
57+
leia o <span>qrcode</span> ou clique no <span>link abaixo</span>
58+
</S.Text>
59+
60+
<S.LinkContainer
61+
href="https://www.buymeacoffee.com/parlandim"
62+
target="_blank"
63+
rel="noreferrer"
64+
>
65+
https://www.buymeacoffee.com/parlandim
66+
</S.LinkContainer>
67+
68+
<S.Text>Obrigado pelo apoio username !</S.Text>
69+
</S.Modal>
70+
</>
71+
)}
72+
</>
73+
);
74+
};
75+
76+
export default CoffeeButton;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import styled from "styled-components";
2+
3+
export const Container = styled.section`
4+
display: flex;
5+
flex-direction: column;
6+
align-items: center;
7+
justify-content: center;
8+
width: 100%;
9+
height: 100%;
10+
background-color: ${({ theme }) => theme.colors.background};
11+
margin: 50px 0;
12+
`;
13+
14+
export const Button = styled.button`
15+
display: flex;
16+
flex-direction: column;
17+
cursor: pointer;
18+
align-items: center;
19+
border: none;
20+
justify-content: center;
21+
font-size: 0.8rem;
22+
color: ${({ theme }) => theme.colors.text};
23+
padding-bottom: 3px;
24+
25+
border-bottom: 2px solid ${({ theme }) => theme.colors.primary};
26+
27+
&:hover {
28+
opacity: 0.9;
29+
border-bottom: 2px solid ${({ theme }) => theme.colors.text};
30+
}
31+
32+
& svg {
33+
width: 26px;
34+
margin-bottom: 5px;
35+
}
36+
`;
37+
38+
export const Overlay = styled.div`
39+
position: fixed;
40+
inset: 0;
41+
width: 100%;
42+
height: 100%;
43+
background-color: rgba(0, 0, 0, 0.7);
44+
z-index: 15;
45+
cursor: pointer;
46+
`;
47+
48+
export const Modal = styled.div`
49+
display: flex;
50+
flex-direction: column;
51+
align-items: center;
52+
justify-content: center;
53+
background-color: ${({ theme }) => theme.colors.background};
54+
width: fit-content;
55+
max-width: 500px;
56+
margin: 20px;
57+
padding: 20px;
58+
border-radius: 8px;
59+
position: fixed;
60+
top: 50%;
61+
left: 50%;
62+
transform: translate(-50%, -50%);
63+
z-index: 16;
64+
65+
@media (max-width: 400px) {
66+
margin: 8px;
67+
}
68+
`;
69+
70+
export const QrCode = styled.div`
71+
width: 100%;
72+
height: 100%;
73+
border-radius: 8px;
74+
display: flex;
75+
justify-content: center;
76+
align-items: center;
77+
78+
img {
79+
border-radius: 8px;
80+
width: 100%;
81+
height: 100%;
82+
}
83+
`;
84+
85+
export const Text = styled.p`
86+
margin: 20px 0 10px;
87+
max-width: 400px;
88+
text-align: center;
89+
width: 100%;
90+
91+
span {
92+
color: ${({ theme }) => theme.colors.primary};
93+
font-weight: bold;
94+
}
95+
`;
96+
97+
export const LinkContainer = styled.a`
98+
display: block;
99+
padding: 5px;
100+
margin: 15px 0 10px;
101+
border-radius: 8px;
102+
max-width: 400px;
103+
width: 400px;
104+
box-sizing: border-box;
105+
color: ${({ theme }) => theme.colors.secondary};
106+
text-decoration: underline;
107+
overflow: hidden;
108+
text-overflow: ellipsis;
109+
white-space: nowrap;
110+
111+
@media (max-width: 510px) {
112+
width: 300px;
113+
}
114+
115+
@media (max-width: 420px) {
116+
width: 200px;
117+
}
118+
119+
@media (max-width: 337px) {
120+
width: 100px;
121+
font-size: 0.8rem;
122+
}
123+
`;

src/components/Comment/index.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import React, { useCallback } from "react";
2+
import * as S from "./styles";
3+
import ViewMarkdown from "@components/ViewMarkdown";
4+
import Heart from "@components/Icons/Heart";
5+
import NewComment from "@components/CommentCreate";
6+
7+
export interface ICommentData {
8+
id: number;
9+
user: {
10+
name: string;
11+
avatar: string;
12+
};
13+
comment: string;
14+
likes: number;
15+
reply?: ICommentData[];
16+
}
17+
18+
export interface IComment {
19+
listComment: ICommentData;
20+
children?: React.ReactNode;
21+
currentUser: string;
22+
isReplying?: boolean;
23+
handleReply?: (id: number) => void;
24+
replyComment?: {
25+
id: number;
26+
isReplying: boolean;
27+
};
28+
}
29+
30+
const Comment: React.FC<IComment> = ({
31+
currentUser = "",
32+
children,
33+
listComment,
34+
isReplying = false,
35+
handleReply = () => {},
36+
replyComment,
37+
}) => {
38+
const userMention = useCallback(
39+
(content: string) => {
40+
const newContent = content.replace(
41+
`@${currentUser}`,
42+
`<span className="mention">@${currentUser}</span>`
43+
);
44+
45+
const reply = newContent.replace(/@(\w+)/g, (match, username) => {
46+
if (username === currentUser) return match;
47+
return `<span className="reply">@${username}</span>`;
48+
});
49+
50+
return reply;
51+
52+
/* return newContent; */
53+
},
54+
[currentUser]
55+
);
56+
57+
return (
58+
<>
59+
<S.CommentContainer
60+
key={listComment.id}
61+
className={isReplying ? "reply" : ""}
62+
>
63+
<S.Avatar
64+
src={listComment.user.avatar}
65+
alt={`${listComment.user.avatar} avatar`}
66+
className={isReplying ? "reply" : ""}
67+
/>
68+
69+
<S.CommentInfos>
70+
<S.CommentTitle>
71+
<S.UserName>{listComment.user.name}</S.UserName>
72+
73+
<S.Separator />
74+
<S.Date>22/01/2024</S.Date>
75+
</S.CommentTitle>
76+
77+
<S.Comment>
78+
<ViewMarkdown content={userMention(listComment.comment)} />
79+
</S.Comment>
80+
81+
<S.ExtraInfo>
82+
<S.LikeButton>
83+
<Heart />
84+
<span>{listComment.likes}</span>
85+
</S.LikeButton>
86+
87+
<S.ReplyButton onClick={() => handleReply(listComment.id)}>
88+
{replyComment?.isReplying && replyComment.id === listComment.id
89+
? "Cancelar resposta"
90+
: "Responder"}
91+
</S.ReplyButton>
92+
</S.ExtraInfo>
93+
94+
{listComment.reply && listComment.reply.length > 0 && (
95+
<S.ReplyCount>{listComment.reply?.length} respostas</S.ReplyCount>
96+
)}
97+
98+
{replyComment?.isReplying && replyComment.id === listComment.id && (
99+
<NewComment
100+
isReplying={true}
101+
replyId={listComment.id}
102+
replyUser={listComment.user.name}
103+
isReplyOverReply={isReplying}
104+
/>
105+
)}
106+
<S.ReplyContainer>{children}</S.ReplyContainer>
107+
</S.CommentInfos>
108+
</S.CommentContainer>
109+
</>
110+
);
111+
};
112+
113+
export default Comment;

0 commit comments

Comments
 (0)