Skip to content

Commit 4dfef94

Browse files
authored
Merge pull request #40 from mrepol742/master
feat: bug fixes and improvements
2 parents 983eb89 + bf152e3 commit 4dfef94

11 files changed

Lines changed: 173 additions & 76 deletions

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=
77
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=
88

99
SENTRY_AUTH_TOKEN=
10+
SENTRY_ORG=
11+
SENTRY_PROJECT=
12+
SENTRY_DNS=

app/components/Chat.tsx

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export default function Chat({ user }: { user: User }) {
5050
const [allUsers, setAllUsers] = useState<ChatUser[]>([]);
5151
const channelRef = useRef<RealtimeChannel>(null);
5252
const textareaRef = useRef<HTMLTextAreaElement>(null);
53-
const [creatingConversation, setCreatingConversation] = useState(false);
5453
const bottomRef = useRef<HTMLDivElement | null>(null);
54+
const creatingRef = useRef(false);
5555

5656
useEffect(() => {
5757
if (textareaRef.current) {
@@ -124,6 +124,16 @@ export default function Chat({ user }: { user: User }) {
124124
.then(({ data }) => {
125125
if (!data || data.length === 0) return;
126126
const convo = data[0];
127+
// Double check the user is part of the conversation (should always be true)
128+
if (
129+
!convo.users.some(
130+
(u: { user_id: string }) => u.user_id === user.id,
131+
)
132+
)
133+
return;
134+
// Check if we already have this conversation in state
135+
if (conversations.some((c) => c.id === convo.id)) return;
136+
127137
setConversations((prev) => [
128138
...prev,
129139
{
@@ -141,7 +151,7 @@ export default function Chat({ user }: { user: User }) {
141151
return () => {
142152
channel.unsubscribe();
143153
};
144-
}, [user.id]);
154+
}, [user.id, conversations]);
145155

146156
useEffect(() => {
147157
if (!conversationId) return;
@@ -223,9 +233,9 @@ export default function Chat({ user }: { user: User }) {
223233
}, [showModal, user.id]);
224234

225235
const createConversation = async (otherUser: ChatUser) => {
226-
if (creatingConversation) return;
236+
if (creatingRef.current) return;
237+
creatingRef.current = true;
227238

228-
setCreatingConversation(true);
229239
const existing = conversations.find((conv) =>
230240
conv.users.some((u) => u.id === otherUser.user_id),
231241
);
@@ -274,8 +284,9 @@ export default function Chat({ user }: { user: User }) {
274284
],
275285
},
276286
]);
277-
setCreatingConversation(false);
287+
278288
setShowModal(false);
289+
creatingRef.current = false;
279290
};
280291

281292
const sendMessage = async () => {
@@ -296,22 +307,25 @@ export default function Chat({ user }: { user: User }) {
296307

297308
return (
298309
<div className="flex flex-col h-screen">
299-
<div className="p-4 flex gap-4 overflow-x-auto border-b border-neutral-700">
310+
<div className="px-3 pt-3 flex border-neutral-700">
300311
<button
301312
onClick={() => setShowModal(true)}
302313
className="flex flex-col items-center min-w-15"
303314
>
304-
<div className="w-12 h-12 rounded-full bg-indigo-500 flex items-center justify-center">
315+
<div className="w-10 h-10 rounded-full bg-indigo-500 flex items-center justify-center">
305316
<FontAwesomeIcon icon={faPlus} className="text-white" />
306317
</div>
307318
<span className="text-xs mt-1">New</span>
308319
</button>
309320

310-
<Conversations
311-
conversations={conversations}
312-
user={user}
313-
setConversationId={setConversationId}
314-
/>
321+
<div className="flex-1 flex gap-4 overflow-x-auto">
322+
<Conversations
323+
conversations={conversations}
324+
user={user}
325+
conversationId={conversationId}
326+
setConversationId={setConversationId}
327+
/>
328+
</div>
315329
</div>
316330

317331
{conversationId ? (

app/components/chat/Conversations.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,48 @@
1-
"use client";
21
import { User } from "@supabase/supabase-js";
32
import { Conversation } from "../Chat";
43

54
export default function Conversations({
65
conversations,
76
user,
7+
conversationId,
88
setConversationId,
99
}: {
1010
conversations: Conversation[];
1111
user: User;
12+
conversationId: string | null;
1213
setConversationId: (id: string) => void;
1314
}) {
1415
return (
1516
<>
1617
{conversations.map((conv, idx) => {
1718
const otherUser = conv.users.find((u) => u.id !== user.id);
19+
const isActive = conv.id === conversationId; // check active
20+
1821
return (
1922
<div
2023
key={idx}
2124
onClick={() => setConversationId(conv.id)}
2225
className="flex flex-col items-center min-w-15 cursor-pointer"
2326
>
24-
<div className="flex justify-center items-center w-12 h-12 rounded-full bg-neutral-600">
25-
{otherUser?.email[0].toUpperCase()}
27+
<div
28+
className={`
29+
flex justify-center items-center w-10 h-10 rounded-full border border-white/10
30+
${isActive ? "bg-indigo-500 text-white" : "bg-white/5 text-gray-300"}
31+
`}
32+
>
33+
{otherUser?.email[0]?.toUpperCase()}
2634
</div>
27-
<span className="text-xs mt-1">
28-
{otherUser?.email.split("@")[0]}
35+
<span
36+
className={`text-xs mt-1 ${isActive ? "text-white" : "text-gray-300"}`}
37+
>
38+
{(() => {
39+
const name = otherUser?.email?.split("@")[0] || "";
40+
return name.length > 10 ? name.slice(0, 8) + "..." : name;
41+
})()}
2942
</span>
3043
</div>
3144
);
3245
})}
3346
</>
3447
);
3548
}
36-

app/components/chat/Messages.tsx

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
"use client";
2+
13
import { User } from "@supabase/supabase-js";
24
import ReactMarkdown from "react-markdown";
35
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
46
import { atomDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
57
import { Conversation, Message } from "../Chat";
68
import { timeAgo } from "@/app/utils/time";
9+
import { useEffect, useState } from "react";
710

811
export default function Messages({
912
messages,
@@ -16,9 +19,42 @@ export default function Messages({
1619
conversations: Conversation[];
1720
bottomRef: React.RefObject<HTMLDivElement | null>;
1821
}) {
22+
const [showScrollBtn, setShowScrollBtn] = useState(false);
23+
24+
useEffect(() => {
25+
const container = document.getElementById("chat-container");
26+
27+
if (!container) return;
28+
29+
const handleScroll = () => {
30+
const isNearBottom =
31+
container.scrollHeight - container.scrollTop - container.clientHeight <
32+
100;
33+
34+
setShowScrollBtn(!isNearBottom);
35+
};
36+
37+
container.addEventListener("scroll", handleScroll);
38+
return () => container.removeEventListener("scroll", handleScroll);
39+
}, []);
40+
1941
return (
2042
<>
21-
<div className="flex-1 overflow-y-auto p-4 space-y-3">
43+
<div className="flex-1 overflow-y-auto p-4 space-y-3" id="chat-container">
44+
{showScrollBtn && (
45+
<button
46+
onClick={() =>
47+
bottomRef.current?.scrollIntoView({ behavior: "smooth" })
48+
}
49+
className="fixed right-4 top-1/2 -translate-y-1/2 z-50
50+
bg-white/10 hover:bg-white/20
51+
backdrop-blur-md border border-white/10
52+
text-white rounded-full p-3 shadow-lg transition"
53+
>
54+
55+
</button>
56+
)}
57+
2258
{messages.length === 0 && (
2359
<div className="text-gray-500 text-sm italic text-center mt-10">
2460
No messages yet. Start the conversation!
@@ -41,7 +77,7 @@ export default function Messages({
4177
)}
4278
<div>
4379
<div
44-
className={`px-4 py-2 rounded-2xl max-w-xs ${
80+
className={`px-4 py-2 rounded-2xl max-w-xs overflow-hidden ${
4581
msg.sender_id === user.id ? "bg-indigo-500" : "bg-neutral-700"
4682
}`}
4783
>
@@ -53,11 +89,12 @@ export default function Messages({
5389

5490
return (
5591
<>
56-
{ }
57-
92+
{}
93+
5894
<SyntaxHighlighter
59-
60-
style={atomDark as { [key: string]: React.CSSProperties }}
95+
style={
96+
atomDark as { [key: string]: React.CSSProperties }
97+
}
6198
language={match ? match[1] : "text"}
6299
PreTag="pre"
63100
className="rounded-md text-sm"
@@ -74,7 +111,7 @@ export default function Messages({
74111
</ReactMarkdown>
75112
</div>
76113

77-
<div className="text-muted text-sm">
114+
<div className="text-muted text-xs mt-1">
78115
{timeAgo(msg.created_at)}
79116
</div>
80117
</div>

app/components/landing-page/TopLeaderbord.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function TopLeaderboard({
2424
Celebrating the most dedicated coders in our community.
2525
</p>
2626

27-
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
27+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
2828
{top_members.map(
2929
(
3030
member: { email: string; total_seconds: number },

instrumentation-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import * as Sentry from "@sentry/nextjs";
66

77
Sentry.init({
8-
dsn: "https://d1a39ca51d4a6f8ce8f14535febe5d6b@o4508073369862144.ingest.de.sentry.io/4511078474711120",
8+
dsn: process.env.SENTRY_DNS,
99

1010
// Enable sending user PII (Personally Identifiable Information)
1111
// https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii

next.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ export default withSentryConfig(nextConfig, {
1919
// For all available options, see:
2020
// https://www.npmjs.com/package/@sentry/webpack-plugin#options
2121

22-
org: "melvin-jones-repol",
22+
org: process.env.SENTRY_ORG,
2323

24-
project: "devpulse",
24+
project: process.env.SENTRY_PROJECT,
2525

2626
// Only print logs for uploading source maps in CI
2727
silent: !process.env.CI,

0 commit comments

Comments
 (0)