Skip to content

Commit

Permalink
Feat: Fetch initial prompts
Browse files Browse the repository at this point in the history
  • Loading branch information
yosuva-rajendran committed Jan 6, 2025
1 parent 7835bf2 commit b994234
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 36 deletions.
7 changes: 3 additions & 4 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import ScrollToTop from '@/components/page/home/featured/scroll-to-top';
import { getFeaturedData } from '@/services/featured.service';

export default async function Home() {
const { featuredData, discoverData, isLoggedIn, isError, userInfo, authToken, focusAreas } = await getPageData();
const { featuredData, discoverData, isLoggedIn, isError, userInfo, focusAreas } = await getPageData();

if (isError) {
return <Error />;
Expand All @@ -39,8 +39,8 @@ export default async function Home() {
<ScrollToTop pageName='Home' userInfo={userInfo}/>
</div>
</div>
<HuskyDialog isLoggedIn={isLoggedIn} authToken={authToken}/>
<HuskyDiscover isLoggedIn={isLoggedIn} authToken={authToken}/>
<HuskyDialog isLoggedIn={isLoggedIn} />
<HuskyDiscover isLoggedIn={isLoggedIn} />
</>
}

Expand Down Expand Up @@ -80,7 +80,6 @@ const getPageData = async () => {
isError,
userInfo,
isLoggedIn,
authToken,
focusAreas: {
teamFocusAreas ,
projectFocusAreas
Expand Down
63 changes: 55 additions & 8 deletions components/core/husky/husky-ai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ interface HuskyAiProps {
isLoggedIn: boolean;
blogId?: string;
onClose?: () => void;
authToken: string;
}

interface Chat {
Expand All @@ -42,7 +41,7 @@ const DEFAULT_TAB_ITEMS = [

// This component represents the Husky AI interface, allowing users to interact with the AI in chat or blog modes.

function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose, authToken }: HuskyAiProps) {
function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose }: HuskyAiProps) {
const [activeTab, setActiveTab] = useState<string>(DEFAULT_TAB_ITEMS[0].key);
const [chats, setChats] = useState<Chat[]>(initialChats);
const [isLoading, setLoadingStatus] = useState<boolean>(false);
Expand All @@ -56,6 +55,7 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
const chatCnRef = useRef<HTMLDivElement>(null);
const router = useRouter();
const { trackTabSelection, trackUserPrompt, trackAnswerCopy, trackFollowupQuestionClick, trackQuestionEdit, trackRegenerate, trackCopyUrl, trackFeedbackClick, trackAiResponse } = useHuskyAnalytics();
const [authToken, setAuthToken] = useState(null);

// Handles the selection of a tab in the UI
const onTabSelected = (item: string) => {
Expand All @@ -81,6 +81,24 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
trackAnswerCopy(answer);
};

// update auth token
const updateToken = async () => {
try {
if (!isLoggedIn) {
setAuthToken(null);
return;
}
const { isLoginRequired, newAuthToken } = await getUserCredentialsInfo();
if (isLoginRequired) {
setAuthToken(null);
return;
}
setAuthToken(newAuthToken); // Update token state
} catch (error) {
console.error('Failed to update token:', error);
}
};

// Fetches user credentials and handles login state
const getUserCredentials = async () => {
if (!isLoggedIn) {
Expand Down Expand Up @@ -133,7 +151,6 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
.optional(),
}),
onFinish: (data) => {
console.log(data);
},
onError: (error) => {
console.error(error);
Expand All @@ -151,20 +168,25 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
return chatUid;
};

const onInitialPromptClicked = (quesObj: any) => {
setChats([{ ...quesObj, isError: false }]);
}

// Handles the event when a prompt is clicked
const onPromptClicked = async (question: string) => {
try {
const { authToken, userInfo } = await getUserCredentials();
if (!authToken) {
return;
}
await updateToken();
const chatUid = checkAndSetPromptId();
setAskingQuestion(question);
setAnswerLoadingStatus(true);
setActiveTab(DEFAULT_TAB_ITEMS[0].key);
setChats((prev:any) => [...prev, {
question,
content: "",
answer: "",
followupQuestions: [],
sources: [],
actions: [],
Expand Down Expand Up @@ -222,10 +244,10 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
setLoginBoxStatus(true);
return;
}

await updateToken();
setChats((prev:any) => [...prev, {
question,
content: "",
answer: "",
followupQuestions: [],
sources: [],
actions: [],
Expand Down Expand Up @@ -302,13 +324,14 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
setLoginBoxStatus(true);
return;
}
await updateToken();
if (activeTab === 'supported-scope') {
setChats([]);
setActiveTab(DEFAULT_TAB_ITEMS[0].key);
}
setChats((prev:any) => [...prev, {
question:query,
content: "",
answer: "",
followupQuestions: [],
sources: [],
actions: [],
Expand Down Expand Up @@ -341,6 +364,21 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
};

useEffect(() => {
if(error) {
setChats((prev:any) => {
const newMessages = [...prev];

// Update the last item in the array
const lastIndex = newMessages.length - 1;
newMessages[lastIndex] = {
...newMessages[lastIndex],
answer: "",
isError: true
};
return newMessages;
})
}

if (object?.content && isLoadingObject) {
setAnswerLoadingStatus(false);
setChats((prev:any) => {
Expand Down Expand Up @@ -373,7 +411,7 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
});

}
}, [object, isLoadingObject]);
}, [object, isLoadingObject, error]);

// Handles the login click event
const onLoginClick = () => {
Expand All @@ -395,6 +433,11 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
}
}, [isAnswerLoading]);

useEffect(() => {
updateToken(); // Fetch token on initial render
}, []);


return (
<>
{mode === 'chat' && (
Expand All @@ -411,12 +454,14 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
onRegenerate={onRegenerate}
onQuestionEdit={onQuestionEdit}
onPromptClicked={onPromptClicked}
onInitialPromptClicked={onInitialPromptClicked}
isAnswerLoading={isAnswerLoading}
chats={chats}
blogId={blogId}
onFollowupClicked={onFollowupClicked}
mode="chat"
onCopyAnswer={onCopyAnswer}
isLoadingObject={isLoadingObject}
/>
{isAnswerLoading && <HuskyAnswerLoader question={askingQuestion} data-testid="chat-answer-loader" />}
</div>
Expand All @@ -434,13 +479,15 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose
onRegenerate={onHuskyInput}
onQuestionEdit={onQuestionEdit}
onPromptClicked={onPromptClicked}
onInitialPromptClicked={onInitialPromptClicked}
onShareClicked={onShareClicked}
isAnswerLoading={isAnswerLoading}
chats={chats}
blogId={blogId}
onFollowupClicked={onFollowupClicked}
mode="blog"
onCopyAnswer={onCopyAnswer}
isLoadingObject={isLoadingObject}
/>
{isAnswerLoading && <HuskyAnswerLoader question={askingQuestion} data-testid="blog-answer-loader" />}
</div>
Expand Down
5 changes: 3 additions & 2 deletions components/core/husky/husky-chat-suggestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ interface HuskyChatSuggestionsProps {
onFollowupClicked?: (question: string) => Promise<void>;
chatIndex?: number;
isAnswerLoading: boolean;
isLoadingObject: boolean;
}

function HuskyChatSuggestions({ followupQuestions = [], chatIndex = 0, onFollowupClicked, isAnswerLoading }: HuskyChatSuggestionsProps) {
function HuskyChatSuggestions({ followupQuestions = [], chatIndex = 0, onFollowupClicked, isAnswerLoading, isLoadingObject }: HuskyChatSuggestionsProps) {
// Handles the click event for a follow-up question.
// If an answer is loading, it prevents further actions.
const onQuestionClicked = (question: string) => {
if(isAnswerLoading) {
if(isAnswerLoading || isLoadingObject) {
return;
}
if (onFollowupClicked) {
Expand Down
8 changes: 5 additions & 3 deletions components/core/husky/husky-chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ interface HuskyChatProps {
onCopyAnswer: (answer: string) => Promise<void>;
blogId?: string;
isAnswerLoading: boolean;
isLoadingObject: boolean;
onInitialPromptClicked: (quesObj: any) => void;
}
function HuskyChat({ mode, chats, onFollowupClicked, isAnswerLoading, onQuestionEdit, onShareClicked, onPromptClicked, onCopyAnswer, onRegenerate, onFeedback, blogId }: HuskyChatProps) {
function HuskyChat({ mode, chats, onInitialPromptClicked, onFollowupClicked, isAnswerLoading, onQuestionEdit, onShareClicked, onPromptClicked, onCopyAnswer, onRegenerate, onFeedback, blogId, isLoadingObject }: HuskyChatProps) {
return (
<>
<div className="huskychat">
Expand All @@ -46,7 +48,7 @@ function HuskyChat({ mode, chats, onFollowupClicked, isAnswerLoading, onQuestion
mode={mode}
answer={chat?.answer}
/>}
{chat?.followupQuestions?.length > 0 && <HuskyChatSuggestions isAnswerLoading={isAnswerLoading} chatIndex={index} onFollowupClicked={onFollowupClicked} followupQuestions={chat?.followupQuestions} />}
{chat?.followupQuestions?.length > 0 && <HuskyChatSuggestions isLoadingObject={isLoadingObject} isAnswerLoading={isAnswerLoading} chatIndex={index} onFollowupClicked={onFollowupClicked} followupQuestions={chat?.followupQuestions} />}
{mode !== 'blog' && chat?.actions?.length > 0 && <HuskyChatActions actions={chat?.actions} />}
</>
)}
Expand All @@ -69,7 +71,7 @@ function HuskyChat({ mode, chats, onFollowupClicked, isAnswerLoading, onQuestion
</div>
))}
</div>}
{chats.length === 0 && !isAnswerLoading && <HuskyEmptyChat onPromptClicked={onPromptClicked} />}
{chats.length === 0 && !isAnswerLoading && <HuskyEmptyChat onInitialPromptClicked={onInitialPromptClicked} onPromptClicked={onPromptClicked} />}
</div>
<style jsx>
{`
Expand Down
32 changes: 19 additions & 13 deletions components/core/husky/husky-empty-chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@
// allowing users to submit prompts and view suggestions.

import { useHuskyAnalytics } from '@/analytics/husky.analytics';
import { useState, useRef } from 'react';
import { getChatQuestions } from '@/services/discovery.service';
import { useState, useRef, useEffect } from 'react';

interface HuskyEmptyChatProps {
onPromptClicked: (ques: string) => Promise<void>;
onInitialPromptClicked: (quesObj: any) => void;
}

function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) {
function HuskyEmptyChat({ onPromptClicked, onInitialPromptClicked }: HuskyEmptyChatProps) {
// Initial prompts displayed to the user
const initialPrompts = [
{ text: 'Summary of discussions from the LabWeek Field Building sessions?', icon: '/icons/send-black.svg' },
{ text: 'Recent updates from the Filecoin ecosystem?', icon: '/icons/send-black.svg' },
{ text: 'What initiatives or programs does Protocol Labs offer to foster innovation in decentralized technologies?', icon: '/icons/send-black.svg' },
];

const [initialPrompts, setInitialPrompts] = useState<any[]>([]);

// Function to check if the user is on a mobile device
const isMobileDevice = () => {
Expand All @@ -39,9 +38,9 @@ function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) {
};

// Handles the click event for exploration prompts
const onExplorationPromptClicked = async (ques: string) => {
trackExplorationPromptSelection(ques);
await onPromptClicked(ques);
const onExplorationPromptClicked = async (quesObj: any) => {
trackExplorationPromptSelection(quesObj.question);
onInitialPromptClicked(quesObj);
};

// Handles key down events in the textarea
Expand All @@ -55,6 +54,13 @@ function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) {
}
};


useEffect(() => {
getChatQuestions().then((res) => {
setInitialPrompts(res.data);
});
}, []);

return (
<>
<div className="hec" data-testid="husky-empty-chat">
Expand Down Expand Up @@ -101,10 +107,10 @@ function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) {
<span className="hec__content__box__prompts__title__text">Try asking or searching for</span>
</h4>
<div className="hec__content__box__prompts__list">
{initialPrompts.map((prompt, index) => (
<div className="hec__content__box__prompts__list__item" key={index} onClick={() => onExplorationPromptClicked(prompt.text)} data-testid={`prompt-${index}`}> {/* Added data-testid for each prompt */}
{initialPrompts?.slice(0, 5)?.map((prompt, index) => (
<div className="hec__content__box__prompts__list__item" key={index} onClick={() => onExplorationPromptClicked(prompt)} data-testid={`prompt-${index}`}> {/* Added data-testid for each prompt */}
<img alt="Prompt Icon" src={prompt.icon} className="hec__content__box__prompts__list__item__icon" />
<span className="hec__content__box__prompts__list__item__text">{prompt.text}</span>
<span className="hec__content__box__prompts__list__item__text">{prompt.question}</span>
</div>
))}
</div>
Expand Down
16 changes: 15 additions & 1 deletion components/core/husky/husky-input-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ function HuskyInputBox(props: any) {
};
}, [isAnswerLoading]);

useEffect(() => {
const handler = (e: any) => {
const question = e.detail;
if (inputRef.current) {
inputRef.current.innerText = question;
}
};

document.addEventListener('husky-ai-input', handler);
return () => {
document.removeEventListener('husky-ai-input', handler);
};
}, []);

return (
<>
<div className={`huskyinput`} data-testid="husky-input-box">
Expand Down Expand Up @@ -189,7 +203,7 @@ function HuskyInputBox(props: any) {
}
.huskyinput__itemcn__instruction__tag {
border: 1px solid #CBD5E1;
border: 1px solid #cbd5e1;
padding: 2px 4px;
border-radius: 4px;
}
Expand Down
2 changes: 1 addition & 1 deletion components/core/husky/husky-login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function HuskyLogin({ onLoginBoxClose, onLoginClick }: HuskyLoginProps) {
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
z-index: 3;
}
.login-popup__box {
background: white;
Expand Down
3 changes: 1 addition & 2 deletions components/page/home/husky-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useEffect, useRef, useState } from 'react';
function HuskyDialog(props:any) {
const dialogRef = useRef<HTMLDialogElement>(null);
const isLoggedIn = props?.isLoggedIn;
const authToken = props?.authToken;
const [isOpen, setIsOpen] = useState(false);

const onDialogClose = () => {
Expand Down Expand Up @@ -35,7 +34,7 @@ function HuskyDialog(props:any) {
<img onClick={onDialogClose} className="hd__head__close" src="/icons/close.svg" />
</div>
<div className="hd__content">
{isOpen && <HuskyAi onClose={onDialogClose} isLoggedIn={isLoggedIn} authToken={authToken} />}
{isOpen && <HuskyAi onClose={onDialogClose} isLoggedIn={isLoggedIn} />}
</div>
</dialog>
<style jsx>
Expand Down
Loading

0 comments on commit b994234

Please sign in to comment.