Skip to content

Commit 0d478a9

Browse files
committed
[NEB-123] Nebula: Allow settings context with search params
1 parent e29f749 commit 0d478a9

File tree

12 files changed

+174
-33
lines changed

12 files changed

+174
-33
lines changed

.npmrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
public-hoist-pattern[]=*import-in-the-middle*
2-
public-hoist-pattern[]=*require-in-the-middle*
2+
public-hoist-pattern[]=*require-in-the-middle*
3+
public-hoist-pattern[]=*pino-pretty*

apps/dashboard/src/app/nebula-app/(app)/chat/[session_id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default async function Page(props: {
3434
session={session}
3535
type="new-chat"
3636
account={account}
37-
initialPrompt={undefined}
37+
initialParams={undefined}
3838
/>
3939
);
4040
}

apps/dashboard/src/app/nebula-app/(app)/chat/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default async function Page() {
1717
session={undefined}
1818
type="new-chat"
1919
account={account}
20-
initialPrompt={undefined}
20+
initialParams={undefined}
2121
/>
2222
);
2323
}

apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ export function ChatBar(props: {
1010
sendMessage: (message: string) => void;
1111
isChatStreaming: boolean;
1212
abortChatStream: () => void;
13+
prefillMessage: string | undefined;
1314
}) {
14-
const [message, setMessage] = useState("");
15+
const [message, setMessage] = useState(props.prefillMessage || "");
1516

1617
return (
1718
<div className="rounded-2xl border border-border bg-card p-2">

apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ export function ChatPageContent(props: {
2929
authToken: string;
3030
type: "landing" | "new-chat";
3131
account: Account;
32-
initialPrompt: string | undefined;
32+
initialParams:
33+
| {
34+
q: string | undefined;
35+
chainIds: number[];
36+
wallet: string | undefined;
37+
}
38+
| undefined;
3339
}) {
3440
const address = useActiveAccount()?.address;
3541
const client = useThirdwebClient(props.authToken);
@@ -85,8 +91,12 @@ export function ChatPageContent(props: {
8591
>(() => {
8692
const contextRes = props.session?.context;
8793
const value: NebulaContext = {
88-
chainIds: contextRes?.chain_ids || null,
89-
walletAddress: contextRes?.wallet_address || null,
94+
chainIds:
95+
contextRes?.chain_ids ||
96+
props.initialParams?.chainIds.map((x) => x.toString()) ||
97+
[],
98+
walletAddress:
99+
contextRes?.wallet_address || props.initialParams?.wallet || null,
90100
};
91101

92102
return value;
@@ -118,8 +128,9 @@ export function ChatPageContent(props: {
118128
walletAddress: null,
119129
};
120130

121-
// Only set wallet address from connected wallet
122-
updatedContextFilters.walletAddress = address || null;
131+
if (!updatedContextFilters.walletAddress && address) {
132+
updatedContextFilters.walletAddress = address;
133+
}
123134

124135
// if we have last used chains in storage, continue using them
125136
try {
@@ -370,14 +381,14 @@ export function ChatPageContent(props: {
370381
// eslint-disable-next-line no-restricted-syntax
371382
useEffect(() => {
372383
if (
373-
props.initialPrompt &&
384+
props.initialParams?.q &&
374385
messages.length === 0 &&
375386
!hasDoneAutoPrompt.current
376387
) {
377388
hasDoneAutoPrompt.current = true;
378-
handleSendMessage(props.initialPrompt);
389+
handleSendMessage(props.initialParams.q);
379390
}
380-
}, [props.initialPrompt, messages.length, handleSendMessage]);
391+
}, [props.initialParams?.q, messages.length, handleSendMessage]);
381392

382393
const showEmptyState = !userHasSubmittedMessage && messages.length === 0;
383394

@@ -412,7 +423,10 @@ export function ChatPageContent(props: {
412423
<div className="relative flex grow flex-col overflow-hidden rounded-lg pb-6">
413424
{showEmptyState ? (
414425
<div className="fade-in-0 container flex max-w-[800px] grow animate-in flex-col justify-center">
415-
<EmptyStateChatPageContent sendMessage={handleSendMessage} />
426+
<EmptyStateChatPageContent
427+
sendMessage={handleSendMessage}
428+
prefillMessage={props.initialParams?.q}
429+
/>
416430
</div>
417431
) : (
418432
<div className="fade-in-0 relative z-[0] flex max-h-full flex-1 animate-in flex-col overflow-hidden">
@@ -430,6 +444,7 @@ export function ChatPageContent(props: {
430444

431445
<div className="container max-w-[800px]">
432446
<ChatBar
447+
prefillMessage={undefined}
433448
sendMessage={handleSendMessage}
434449
isChatStreaming={isChatStreaming}
435450
abortChatStream={() => {

apps/dashboard/src/app/nebula-app/(app)/components/Chatbar.stories.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function Story() {
2727
abortChatStream={() => {}}
2828
isChatStreaming={false}
2929
sendMessage={() => {}}
30+
prefillMessage={undefined}
3031
/>
3132
</BadgeContainer>
3233

@@ -35,6 +36,16 @@ function Story() {
3536
abortChatStream={() => {}}
3637
isChatStreaming={true}
3738
sendMessage={() => {}}
39+
prefillMessage={undefined}
40+
/>
41+
</BadgeContainer>
42+
43+
<BadgeContainer label="Prefilled Message">
44+
<ChatBar
45+
abortChatStream={() => {}}
46+
isChatStreaming={false}
47+
sendMessage={() => {}}
48+
prefillMessage="This is a prefilled message"
3849
/>
3950
</BadgeContainer>
4051
</div>

apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.stories.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,26 @@ export default meta;
1515
type Story = StoryObj<typeof meta>;
1616

1717
export const Default: Story = {
18-
args: {},
18+
args: {
19+
prefillMessage: undefined,
20+
},
21+
};
22+
23+
export const PrefilledMessage: Story = {
24+
args: {
25+
prefillMessage: "This is a prefilled message",
26+
},
1927
};
2028

21-
function Story() {
29+
function Story(props: {
30+
prefillMessage: string | undefined;
31+
}) {
2232
return (
2333
<div className="container flex max-w-[800px] grow flex-col justify-center overflow-hidden">
24-
<EmptyStateChatPageContent sendMessage={() => {}} />
34+
<EmptyStateChatPageContent
35+
sendMessage={() => {}}
36+
prefillMessage={props.prefillMessage}
37+
/>
2538
</div>
2639
);
2740
}

apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ChatBar } from "./ChatBar";
88

99
export function EmptyStateChatPageContent(props: {
1010
sendMessage: (message: string) => void;
11+
prefillMessage: string | undefined;
1112
}) {
1213
return (
1314
<div className="py-10 lg:py-16">
@@ -33,6 +34,7 @@ export function EmptyStateChatPageContent(props: {
3334
abortChatStream={() => {
3435
// the page will switch so, no need to handle abort here
3536
}}
37+
prefillMessage={props.prefillMessage}
3638
/>
3739
<div className="h-5" />
3840
<div className="flex flex-wrap justify-center gap-2.5">
Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,74 @@
1+
import { unstable_cache } from "next/cache";
2+
import { isAddress } from "thirdweb";
3+
import { fetchChain } from "../../../utils/fetchChain";
14
import { getValidAccount } from "../../account/settings/getAccount";
25
import { getAuthToken } from "../../api/lib/getAuthToken";
36
import { loginRedirect } from "../../login/loginRedirect";
47
import { ChatPageContent } from "./components/ChatPageContent";
58

69
export default async function Page(props: {
710
searchParams: Promise<{
8-
prompt?: string;
11+
q?: string | string[];
12+
chain?: string | string[];
13+
wallet?: string | string[];
914
}>;
1015
}) {
11-
const [searchParams, authToken] = await Promise.all([
12-
props.searchParams,
16+
const searchParams = await props.searchParams;
17+
18+
const [chainIds, authToken, account] = await Promise.all([
19+
getChainIds(searchParams.chain),
1320
getAuthToken(),
21+
getValidAccount(),
1422
]);
1523

1624
if (!authToken) {
1725
loginRedirect();
1826
}
1927

20-
const account = await getValidAccount();
21-
2228
return (
2329
<ChatPageContent
2430
authToken={authToken}
2531
session={undefined}
2632
type="landing"
2733
account={account}
28-
initialPrompt={searchParams.prompt}
34+
initialParams={{
35+
q: typeof searchParams.q === "string" ? searchParams.q : undefined,
36+
chainIds: chainIds,
37+
wallet:
38+
typeof searchParams.wallet === "string" &&
39+
isAddress(searchParams.wallet)
40+
? searchParams.wallet
41+
: undefined,
42+
}}
2943
/>
3044
);
3145
}
46+
47+
const getChainIds = unstable_cache(
48+
async (_chainNames: string[] | string | undefined) => {
49+
if (!_chainNames) {
50+
return [];
51+
}
52+
53+
const chainIds: number[] = [];
54+
55+
const chainNames =
56+
typeof _chainNames === "string" ? [_chainNames] : _chainNames;
57+
58+
const chainResults = await Promise.allSettled(
59+
chainNames.map((x) => fetchChain(x)),
60+
);
61+
62+
for (const chainResult of chainResults) {
63+
if (chainResult.status === "fulfilled" && chainResult.value) {
64+
chainIds.push(chainResult.value.chainId);
65+
}
66+
}
67+
68+
return chainIds;
69+
},
70+
["nebula_getChainIds"],
71+
{
72+
revalidate: 60 * 60 * 24, // 24 hours
73+
},
74+
);

apps/dashboard/src/app/nebula-app/login/NebulaLoginPage.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,37 @@ import { LoginAndOnboardingPageContent } from "../../login/LoginPage";
1111

1212
export function NebulaLoginPage(props: {
1313
account: Account | undefined;
14+
params: {
15+
chain: string | string[] | undefined;
16+
q: string | undefined;
17+
wallet: string | undefined;
18+
};
1419
}) {
15-
const [message, setMessage] = useState<string | undefined>(undefined);
20+
const [message, setMessage] = useState<string | undefined>(props.params.q);
1621
const [showPage, setShowPage] = useState<"connect" | "welcome">(
1722
props.account ? "connect" : "welcome",
1823
);
24+
25+
const redirectPathObj = {
26+
chain: props.params.chain,
27+
q: message, // don't use props.params.q, because message may be updated by user
28+
wallet: props.params.wallet,
29+
};
30+
31+
const redirectPathParams = Object.entries(redirectPathObj)
32+
.map(([key, value]) => {
33+
if (!value) {
34+
return "";
35+
}
36+
37+
if (Array.isArray(value)) {
38+
return value.map((v) => `${key}=${encodeURIComponent(v)}`).join("&");
39+
}
40+
41+
return `${key}=${encodeURIComponent(value)}`;
42+
})
43+
.join("&");
44+
1945
return (
2046
<div className="relative flex min-h-dvh flex-col overflow-hidden bg-background">
2147
{/* nav */}
@@ -55,15 +81,14 @@ export function NebulaLoginPage(props: {
5581
<LoginAndOnboardingPageContent
5682
loginWithInAppWallet={false}
5783
account={props.account}
58-
redirectPath={
59-
message ? `/?prompt=${encodeURIComponent(message)}` : "/"
60-
}
84+
redirectPath={`/?${redirectPathParams}`}
6185
/>
6286
)}
6387

6488
{showPage === "welcome" && (
6589
<div className="container relative flex max-w-[800px] grow flex-col justify-center overflow-hidden rounded-lg pb-6">
6690
<EmptyStateChatPageContent
91+
prefillMessage={message}
6792
sendMessage={(msg) => {
6893
setMessage(msg);
6994
setShowPage("connect");

0 commit comments

Comments
 (0)