1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-03 06:12:53 +02:00

feat: show loading status

This commit is contained in:
Arik Chakma
2025-05-23 00:48:16 +06:00
parent 2e248b1e6b
commit e1cdce70f0
3 changed files with 40 additions and 11 deletions

View File

@@ -220,7 +220,9 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
return;
}
const jsx = await renderMessage(content, renderer);
const jsx = await renderMessage(content, renderer, {
isLoading: true,
});
flushSync(() => {
setStreamedMessage(jsx);
@@ -233,7 +235,9 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
return;
}
const jsx = await renderMessage(content, renderer);
const jsx = await renderMessage(content, renderer, {
isLoading: false,
});
const newMessages: RoamdapAIChatHistoryType[] = [
...messages,
{

View File

@@ -69,10 +69,11 @@ type BulkUpdateResourceProgressResponse = {
type UserProgressActionListProps = {
roadmapId: string;
content: string;
isLoading?: boolean;
};
export function UserProgressActionList(props: UserProgressActionListProps) {
const { roadmapId, content } = props;
const { roadmapId, content, isLoading = false } = props;
const toast = useToast();
const updateUserProgress = parseUserProgress(content);
@@ -157,12 +158,19 @@ export function UserProgressActionList(props: UserProgressActionListProps) {
<div className="absolute inset-x-0 right-0 bottom-0.5 translate-y-1/2">
<div className="flex items-center justify-center gap-2">
<button
className="rounded-md bg-gray-100 px-2 py-1 text-[10px] leading-none font-medium"
className="rounded-md bg-gray-100 px-2 py-1 text-[10px] leading-none font-medium disabled:cursor-not-allowed disabled:opacity-70"
onClick={() => setShowAll(!showAll)}
disabled={isLoading}
>
{showAll
? '- Show Less'
: `+${progressItemWithText.length - itemCountToShow} more`}
{isLoading && <Loader2Icon className="size-2.5 animate-spin" />}
{!isLoading && (
<>
{showAll
? '- Show Less'
: `+${progressItemWithText.length - itemCountToShow} more`}
</>
)}
</button>
</div>
</div>

View File

@@ -10,16 +10,24 @@ type MessagePart = {
type MessagePartRendererProps = {
content: string;
isLoading?: boolean;
};
export type MessagePartRenderer = (
props: MessagePartRendererProps,
) => React.ReactNode | string;
export type MessagePartRendererOptions = {
isLoading?: boolean;
};
export async function parseMessageParts(
content: string,
renderer: Record<string, MessagePartRenderer>,
): Promise<MessagePart[]> {
options: MessagePartRendererOptions = {
isLoading: false,
},
) {
const parts: MessagePart[] = [];
const regex = /<([a-zA-Z0-9\-]+)>(.*?)<\/\1>/gs;
@@ -46,7 +54,10 @@ export async function parseMessageParts(
});
}
const output = renderer[tag]({ content: innerContent });
const output = renderer[tag]({
content: innerContent,
isLoading: options.isLoading,
});
parts.push({
id: nanoid(),
type: 'html',
@@ -81,7 +92,10 @@ export async function parseMessageParts(
}
const innerContent = content.slice(openingIndex + openingTag.length);
const output = renderer[tag]({ content: innerContent });
const output = renderer[tag]({
content: innerContent,
isLoading: options.isLoading,
});
parts.push({
id: nanoid(),
type: 'html',
@@ -109,8 +123,11 @@ export async function parseMessageParts(
export async function renderMessage(
content: string,
renderer: Record<string, MessagePartRenderer>,
options: MessagePartRendererOptions = {
isLoading: false,
},
) {
const parts = await parseMessageParts(content, renderer);
const parts = await parseMessageParts(content, renderer, options);
return (
<div className="max-w-[calc(100%-38px)]">