Skip to content

Commit 74a4ecc

Browse files
committed
Add Nebula Chat UI
1 parent d3139b2 commit 74a4ecc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2465
-220
lines changed

apps/dashboard/.env.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,7 @@ NEXT_PUBLIC_TURNSTILE_SITE_KEY=""
9494
TURNSTILE_SECRET_KEY=""
9595
REDIS_URL=""
9696

97-
ANALYTICS_SERVICE_URL=""
97+
ANALYTICS_SERVICE_URL=""
98+
99+
# Required for Nebula Chat
100+
NEXT_PUBLIC_NEBULA_URL=""

apps/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"color": "^4.2.3",
6161
"compare-versions": "^6.1.0",
6262
"date-fns": "4.1.0",
63+
"fetch-event-stream": "^0.1.5",
6364
"flat": "^6.0.1",
6465
"framer-motion": "11.11.17",
6566
"fuse.js": "7.0.0",

apps/dashboard/src/@/components/ui/code/code.client.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type CodeProps = {
1212
scrollableClassName?: string;
1313
keepPreviousDataOnCodeChange?: boolean;
1414
copyButtonClassName?: string;
15+
ignoreFormattingErrors?: boolean;
1516
};
1617

1718
export const CodeClient: React.FC<CodeProps> = ({
@@ -21,10 +22,14 @@ export const CodeClient: React.FC<CodeProps> = ({
2122
scrollableClassName,
2223
keepPreviousDataOnCodeChange = false,
2324
copyButtonClassName,
25+
ignoreFormattingErrors,
2426
}) => {
2527
const codeQuery = useQuery({
2628
queryKey: ["html", code],
27-
queryFn: () => getCodeHtml(code, lang),
29+
queryFn: () =>
30+
getCodeHtml(code, lang, {
31+
ignoreFormattingErrors: ignoreFormattingErrors,
32+
}),
2833
placeholderData: keepPreviousDataOnCodeChange
2934
? keepPreviousData
3035
: undefined,

apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,28 @@ function isPrettierSupportedLang(lang: BundledLanguage) {
1616
);
1717
}
1818

19-
export async function getCodeHtml(code: string, lang: BundledLanguage) {
19+
export async function getCodeHtml(
20+
code: string,
21+
lang: BundledLanguage,
22+
options?: {
23+
ignoreFormattingErrors?: boolean;
24+
},
25+
) {
2026
const formattedCode = isPrettierSupportedLang(lang)
2127
? await format(code, {
2228
parser: "babel-ts",
2329
plugins: [parserBabel, estree],
2430
printWidth: 60,
2531
}).catch((e) => {
26-
console.error(e);
27-
console.error("Failed to format code");
28-
console.log({
29-
code,
30-
lang,
31-
});
32+
if (!options?.ignoreFormattingErrors) {
33+
console.error(e);
34+
console.error("Failed to format code");
35+
console.log({
36+
code,
37+
lang,
38+
});
39+
}
40+
3241
return code;
3342
})
3443
: code;

apps/dashboard/src/@/components/ui/textarea.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,27 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
2222
Textarea.displayName = "Textarea";
2323

2424
export { Textarea };
25+
26+
export function AutoResizeTextarea(props: TextareaProps) {
27+
const textareaRef = React.useRef<HTMLTextAreaElement>(null);
28+
29+
// biome-ignore lint/correctness/useExhaustiveDependencies: value is needed in deps array
30+
React.useEffect(() => {
31+
const textarea = textareaRef.current;
32+
if (textarea) {
33+
textarea.style.height = "auto";
34+
textarea.style.height = `${textarea.scrollHeight}px`;
35+
}
36+
}, [props.value]);
37+
38+
return (
39+
<Textarea
40+
ref={textareaRef}
41+
{...props}
42+
style={{
43+
...props.style,
44+
overflowY: "hidden",
45+
}}
46+
/>
47+
);
48+
}

apps/dashboard/src/@/constants/env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@ export const BASE_URL = isProd
3939
: (process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL
4040
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`
4141
: "http://localhost:3000") || "https://thirdweb-dev.com";
42+
43+
export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL;

apps/dashboard/src/app/account/settings/getAccount.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export async function getRawAccount() {
3636
* If there's no account or account onboarding not complete, redirect to login page
3737
* @param pagePath - the path of the current page to redirect back to after login/onboarding
3838
*/
39-
export async function getValidAccount(pagePath: string) {
39+
export async function getValidAccount(pagePath?: string) {
4040
const account = await getRawAccount();
4141

4242
// enforce login & onboarding

apps/dashboard/src/app/login/LoginPage.tsx

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const wallets = [
4444

4545
export function LoginAndOnboardingPage(props: {
4646
account: Account | undefined;
47-
nextPath: string | undefined;
47+
redirectPath: string;
4848
}) {
4949
return (
5050
<div className="relative flex min-h-screen flex-col overflow-hidden bg-background">
@@ -81,7 +81,10 @@ export function LoginAndOnboardingPage(props: {
8181

8282
<main className="container z-10 flex grow flex-col items-center justify-center gap-6 py-12">
8383
<ClientOnly ssr={<LoadingCard />}>
84-
<PageContent nextPath={props.nextPath} account={props.account} />
84+
<PageContent
85+
redirectPath={props.redirectPath}
86+
account={props.account}
87+
/>
8588
</ClientOnly>
8689
</main>
8790

@@ -110,7 +113,7 @@ function LoadingCard() {
110113
}
111114

112115
function PageContent(props: {
113-
nextPath: string | undefined;
116+
redirectPath: string;
114117
account: Account | undefined;
115118
}) {
116119
const [screen, setScreen] = useState<
@@ -133,11 +136,7 @@ function PageContent(props: {
133136

134137
function onComplete() {
135138
setScreen({ id: "complete" });
136-
if (props.nextPath && isValidRedirectPath(props.nextPath)) {
137-
router.replace(props.nextPath);
138-
} else {
139-
router.replace("/team");
140-
}
139+
router.replace(props.redirectPath);
141140
}
142141

143142
if (connectionStatus === "connecting") {
@@ -154,7 +153,7 @@ function PageContent(props: {
154153
<LazyOnboardingUI
155154
account={screen.account}
156155
onComplete={onComplete}
157-
redirectPath={props.nextPath || "/team"}
156+
redirectPath={props.redirectPath}
158157
/>
159158
</Suspense>
160159
);
@@ -220,19 +219,6 @@ function CustomConnectEmbed(props: {
220219
);
221220
}
222221

223-
function isValidRedirectPath(encodedPath: string): boolean {
224-
try {
225-
// Decode the URI component
226-
const decodedPath = decodeURIComponent(encodedPath);
227-
// ensure the path always starts with a _single_ slash
228-
// double slash could be interpreted as `//example.com` which is not allowed
229-
return decodedPath.startsWith("/") && !decodedPath.startsWith("//");
230-
} catch {
231-
// If decoding fails, return false
232-
return false;
233-
}
234-
}
235-
236222
type AuroraProps = {
237223
size: { width: string; height: string };
238224
pos: { top: string; left: string };

apps/dashboard/src/app/login/auth-actions.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"use server";
2-
import "server-only";
32

43
import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie";
54
import { API_SERVER_URL } from "@/constants/env";
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function isValidEncodedRedirectPath(encodedPath: string): boolean {
2+
try {
3+
// Decode the URI component
4+
const decodedPath = decodeURIComponent(encodedPath);
5+
// ensure the path always starts with a _single_ slash
6+
// double slash could be interpreted as `//example.com` which is not allowed
7+
return decodedPath.startsWith("/") && !decodedPath.startsWith("//");
8+
} catch {
9+
// If decoding fails, return false
10+
return false;
11+
}
12+
}

0 commit comments

Comments
 (0)