Skip to content

Commit 8b90327

Browse files
committed
Refactor support component and integrate Sentry feedback button
1 parent f3c2a1f commit 8b90327

File tree

2 files changed

+62
-77
lines changed

2 files changed

+62
-77
lines changed
Lines changed: 18 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,43 @@
11
'use client';
22

3-
import * as Sentry from '@sentry/nextjs';
43
import { EventEmitter } from 'events';
5-
import { useEffect, useState, useRef } from 'react';
4+
import { useEffect, useState } from 'react';
65
import { useVariables } from '@gitroom/react/helpers/variable.context';
76
import { useT } from '@gitroom/react/translation/get.transation.service.client';
87
export const supportEmitter = new EventEmitter();
9-
function AttachToFeedbackButton() {
10-
const [feedback, setFeedback] = useState<any>();
8+
export const Support = () => {
9+
const [show, setShow] = useState(true);
10+
const { discordUrl } = useVariables();
1111
const t = useT();
12-
// Read `getFeedback` on the client only, to avoid hydration errors during server rendering
12+
1313
useEffect(() => {
14-
// Sentry.getFeedback is only available when Sentry is initialized on the client
15-
try {
16-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
17-
const fb = (Sentry as any).getFeedback?.();
18-
setFeedback(fb);
19-
} catch (e) {
20-
setFeedback(undefined);
21-
}
14+
supportEmitter.on('change', setShow);
15+
return () => {
16+
supportEmitter.off('state', setShow);
17+
};
2218
}, []);
23-
const buttonRef = useRef<HTMLButtonElement | null>(null);
24-
useEffect(() => {
25-
if (feedback && buttonRef.current) {
26-
const unsubscribe = feedback.attachTo(buttonRef.current);
27-
return unsubscribe;
28-
}
29-
return () => {};
30-
}, [feedback]);
19+
if (!discordUrl || !show) return null;
3120
return (
32-
<button
33-
type="button"
34-
ref={buttonRef}
35-
className="bg-customColor39 w-[194px] h-[58px] text-[16px] text-customColor40 rounded-[30px] !rounded-br-[0] cursor-pointer flex justify-center items-center gap-[10px]"
21+
<div
22+
className="bg-customColor39 w-[194px] h-[58px] fixed end-[20px] bottom-[20px] z-[500] text-[16px] text-customColor40 rounded-[30px] !rounded-br-[0] cursor-pointer flex justify-center items-center gap-[10px]"
23+
onClick={() => window.open(discordUrl)}
3624
>
3725
<div>
3826
<svg
39-
width="24"
40-
height="24"
41-
viewBox="0 0 24 24"
27+
width="32"
28+
height="33"
29+
viewBox="0 0 32 33"
4230
fill="none"
4331
xmlns="http://www.w3.org/2000/svg"
4432
className="mb-[4px]"
4533
>
4634
<path
47-
d="M12 2C6.48 2 2 5.58 2 10C2 12.39 3.22 14.57 5.11 15.99C5.04 16.66 4.9 17.55 4.8 18.01C4.68 18.57 5.06 19 5.63 19C8.61 19 9.99 17.17 10.6 16.31C11.36 16.58 12.17 16.73 13 16.73C18.52 16.73 23 13.15 23 8.73C23 4.31 18.52 2 12 2Z"
35+
d="M26.1303 11.347C24.3138 9.93899 22.134 9.23502 19.8331 9.11768L19.4697 9.4697C21.5284 9.93899 23.345 10.8776 25.0404 12.1683C22.9817 11.1123 20.6807 10.4084 18.2587 10.1737C17.5321 10.0563 16.9266 10.0563 16.2 10.0563C15.4734 10.0563 14.8679 10.0563 14.1413 10.1737C11.7193 10.4084 9.41833 11.1123 7.35963 12.1683C9.05501 10.8776 10.8716 9.93899 12.9303 9.4697L12.5669 9.11768C10.266 9.23502 8.08621 9.93899 6.26972 11.347C4.21101 15.1017 3.1211 19.3257 3 23.6669C4.81649 25.5443 7.35963 26.7177 10.0239 26.7177C10.0239 26.7177 10.8716 25.779 11.477 24.9576C9.90277 24.6057 8.44954 23.7843 7.48074 22.4937C8.32843 22.963 9.17611 23.4323 10.0239 23.7843C11.1138 24.2537 12.2037 24.4883 13.2936 24.723C14.2624 24.8403 15.2312 24.9576 16.2 24.9576C17.1688 24.9576 18.1376 24.8403 19.1064 24.723C20.1963 24.4883 21.2862 24.2537 22.3761 23.7843C23.2239 23.4323 24.0716 22.963 24.9193 22.4937C23.9505 23.7843 22.4972 24.6057 20.923 24.9576C21.5284 25.779 22.3761 26.7177 22.3761 26.7177C25.0404 26.7177 27.5835 25.5443 29.4 23.6669C29.2789 19.3257 28.189 15.1017 26.1303 11.347ZM12.2037 21.555C10.9927 21.555 9.90278 20.499 9.90278 19.2084C9.90278 17.9177 10.9927 16.8617 12.2037 16.8617C13.4147 16.8617 14.5046 17.9177 14.5046 19.2084C14.5046 20.499 13.4147 21.555 12.2037 21.555ZM20.1963 21.555C18.9853 21.555 17.8954 20.499 17.8954 19.2084C17.8954 17.9177 18.9853 16.8617 20.1963 16.8617C21.4073 16.8617 22.4972 17.9177 22.4972 19.2084C22.4972 20.499 21.4073 21.555 20.1963 21.555Z"
4836
fill="currentColor"
4937
/>
5038
</svg>
5139
</div>
52-
<div>{t('feedback', 'Send feedback')}</div>
53-
</button>
54-
);
55-
}
56-
57-
export const Support = () => {
58-
const [show, setShow] = useState(true);
59-
const { discordUrl, sentryDsn } = useVariables();
60-
const t = useT();
61-
62-
useEffect(() => {
63-
supportEmitter.on('change', setShow);
64-
return () => {
65-
supportEmitter.off('change', setShow);
66-
};
67-
}, []);
68-
69-
if (!discordUrl || !show) return null;
70-
71-
return (
72-
<div className="flex flex-col items-end gap-2 fixed end-[20px] bottom-[20px] z-[500]">
73-
{sentryDsn ? (
74-
<AttachToFeedbackButton />
75-
) : null}
76-
77-
<div
78-
className="bg-customColor39 w-[194px] h-[58px] text-[16px] text-customColor40 rounded-[30px] !rounded-br-[0] cursor-pointer flex justify-center items-center gap-[10px]"
79-
onClick={() => window.open(discordUrl)}
80-
>
81-
<div>
82-
<svg
83-
width="32"
84-
height="33"
85-
viewBox="0 0 32 33"
86-
fill="none"
87-
xmlns="http://www.w3.org/2000/svg"
88-
className="mb-[4px]"
89-
>
90-
<path
91-
d="M26.1303 11.347C24.3138 9.93899 22.134 9.23502 19.8331 9.11768L19.4697 9.4697C21.5284 9.93899 23.345 10.8776 25.0404 12.1683C22.9817 11.1123 20.6807 10.4084 18.2587 10.1737C17.5321 10.0563 16.9266 10.0563 16.2 10.0563C15.4734 10.0563 14.8679 10.0563 14.1413 10.1737C11.7193 10.4084 9.41833 11.1123 7.35963 12.1683C9.05501 10.8776 10.8716 9.93899 12.9303 9.4697L12.5669 9.11768C10.266 9.23502 8.08621 9.93899 6.26972 11.347C4.21101 15.1017 3.1211 19.3257 3 23.6669C4.81649 25.5443 7.35963 26.7177 10.0239 26.7177C10.0239 26.7177 10.8716 25.779 11.477 24.9576C9.90277 24.6057 8.44954 23.7843 7.48074 22.4937C8.32843 22.963 9.17611 23.4323 10.0239 23.7843C11.1138 24.2537 12.2037 24.4883 13.2936 24.723C14.2624 24.8403 15.2312 24.9576 16.2 24.9576C17.1688 24.9576 18.1376 24.8403 19.1064 24.723C20.1963 24.4883 21.2862 24.2537 22.3761 23.7843C23.2239 23.4323 24.0716 22.963 24.9193 22.4937C23.9505 23.7843 22.4972 24.6057 20.923 24.9576C21.5284 25.779 22.3761 26.7177 22.3761 26.7177C25.0404 26.7177 27.5835 25.5443 29.4 23.6669C29.2789 19.3257 28.189 15.1017 26.1303 11.347ZM12.2037 21.555C10.9927 21.555 9.90278 20.499 9.90278 19.2084C9.90278 17.9177 10.9927 16.8617 12.2037 16.8617C13.4147 16.8617 14.5046 17.9177 14.5046 19.2084C14.5046 20.499 13.4147 21.555 12.2037 21.555ZM20.1963 21.555C18.9853 21.555 17.8954 20.499 17.8954 19.2084C17.8954 17.9177 18.9853 16.8617 20.1963 16.8617C21.4073 16.8617 22.4972 17.9177 22.4972 19.2084C22.4972 20.499 21.4073 21.555 20.1963 21.555Z"
92-
fill="currentColor"
93-
/>
94-
</svg>
95-
</div>
96-
<div>{t('discord_support', 'Discord Support')}</div>
97-
</div>
40+
<div>{t('discord_support', 'Discord Support')}</div>
9841
</div>
9942
);
10043
};

apps/frontend/src/components/new-layout/layout.component.tsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
'use client';
1+
'use client';
22

3-
import React, { ReactNode, useCallback } from 'react';
3+
import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
4+
import * as Sentry from '@sentry/nextjs';
45
import { Logo } from '@gitroom/frontend/components/new-layout/logo';
56
import { Plus_Jakarta_Sans } from 'next/font/google';
67
const ModeComponent = dynamic(
@@ -50,6 +51,45 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => {
5051
const fetch = useFetch();
5152

5253
const { backendUrl, billingEnabled, isGeneral } = useVariables();
54+
55+
// Feedback icon component attaches Sentry feedback to a top-bar icon when DSN is present
56+
function AttachToFeedbackIcon({ sentryDsn }: { sentryDsn?: string }) {
57+
const [feedback, setFeedback] = useState<any>();
58+
const buttonRef = useRef<HTMLButtonElement | null>(null);
59+
60+
useEffect(() => {
61+
if (!sentryDsn) return;
62+
try {
63+
const fb = (Sentry as any).getFeedback?.();
64+
setFeedback(fb);
65+
} catch (e) {
66+
setFeedback(undefined);
67+
}
68+
}, [sentryDsn]);
69+
70+
useEffect(() => {
71+
if (feedback && buttonRef.current) {
72+
const unsubscribe = feedback.attachTo(buttonRef.current);
73+
return unsubscribe;
74+
}
75+
return () => {};
76+
}, [feedback]);
77+
78+
if (!sentryDsn) return null;
79+
80+
return (
81+
<button
82+
ref={buttonRef}
83+
type="button"
84+
aria-label="Feedback"
85+
className="hover:text-newTextColor"
86+
>
87+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
88+
<path d="M27 10H23V6C23 5.46957 22.7893 4.96086 22.4142 4.58579C22.0391 4.21071 21.5304 4 21 4H5C4.46957 4 3.96086 4.21071 3.58579 4.58579C3.21071 4.96086 3 5.46957 3 6V22C3.00059 22.1881 3.05423 22.3723 3.15478 22.5313C3.25532 22.6903 3.39868 22.8177 3.56839 22.8989C3.7381 22.9801 3.92728 23.0118 4.11418 22.9903C4.30108 22.9689 4.47814 22.8951 4.625 22.7775L9 19.25V23C9 23.5304 9.21071 24.0391 9.58579 24.4142C9.96086 24.7893 10.4696 25 11 25H22.6987L27.375 28.7775C27.5519 28.9206 27.7724 28.9991 28 29C28.2652 29 28.5196 28.8946 28.7071 28.7071C28.8946 28.5196 29 28.2652 29 28V12C29 11.4696 28.7893 10.9609 28.4142 10.5858C28.0391 10.2107 27.5304 10 27 10ZM8.31875 17.2225L5 19.9062V6H21V17H8.9475C8.71863 17 8.4967 17.0786 8.31875 17.2225ZM27 25.9062L23.6812 23.2225C23.5043 23.0794 23.2838 23.0009 23.0562 23H11V19H21C21.5304 19 22.0391 18.7893 22.4142 18.4142C22.7893 18.0391 23 17.5304 23 17V12H27V25.9062Z" fill="#343330"/>
89+
</svg>
90+
</button>
91+
);
92+
}
5393
const searchParams = useSearchParams();
5494
const load = useCallback(async (path: string) => {
5595
return await (await fetch(path)).json();
@@ -118,6 +158,8 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => {
118158
<LanguageComponent />
119159
<ChromeExtensionComponent />
120160
<div className="w-[1px] h-[20px] bg-blockSeparator" />
161+
{/* Feedback icon (icon-only) - only show when Sentry DSN is present */}
162+
<AttachToFeedbackIcon sentryDsn={(useVariables() as any).sentryDsn} />
121163
<NotificationComponent />
122164
</div>
123165
</div>

0 commit comments

Comments
 (0)