Skip to content
Merged

Dev #46

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,6 @@ Before every feature:

- Keep 'leading-6' in all components, and fix if you find any un-matching ones.

- If you see any svgs in the attached design look for it in the '/assets' folder. If we don't have it, try to create them yourself like you created the google icon in the login screens.
- If you see any svgs in the attached design look for them in the '/assets' folder. If we don't have it, try to create them yourself like you created the Google icon in the login screens.
Comment thread
aryansoni-dev marked this conversation as resolved.

- Keep the text sizes like : headings, subheadings, body text, etc consistent on each screen.
4 changes: 2 additions & 2 deletions app/(onboarding)/onboarding-screen-2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,10 @@ export default function OnboardingScreenTwo() {
}}
>
<Image
source={images.insightsTabMockup}
source={images.onboardingAiChatCard}
contentFit="cover"
contentPosition="top center"
accessibilityLabel="DearDiary insights screen preview"
accessibilityLabel="DearDiary AI chat preview"
style={{ height: "100%", width: "100%" }}
/>
</View>
Expand Down
27 changes: 13 additions & 14 deletions app/insights/report/[periodType].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { useSafeAreaInsets } from "react-native-safe-area-context";

import { AnimatedIconButton } from "@/components/ui/animated-icon-button";
import { AIResponseRenderer } from "@/components/ai/ai-response-renderer";
import { BottomTabBar, bottomTabBarBaseHeight } from "@/components/navigation/bottom-tab-bar";
import { EntryTypeDistributionChart } from "@/components/insights/report/EntryTypeDistributionChart";
import {
Expand Down Expand Up @@ -196,21 +197,19 @@ export default function AIInsightReportScreen() {
onPress={handleRegenerate}
/>
<ReportSection title="AI Overview">
<Text
allowFontScaling={false}
className="text-[16px] leading-7 text-[#52525B]"
maxFontSizeMultiplier={1}
>
{reportState.report.narrative.overview}
</Text>
<AIResponseRenderer
content={reportState.report.narrative.overview}
diagnosticLabel="insight_report_overview"
variant="report"
/>
{reportState.report.narrative.dataQualityNote ? (
<Text
allowFontScaling={false}
className="mt-4 text-[14px] leading-6 text-[#71717B]"
maxFontSizeMultiplier={1}
>
{reportState.report.narrative.dataQualityNote}
</Text>
<View className="mt-4 min-w-0">
<AIResponseRenderer
content={reportState.report.narrative.dataQualityNote}
diagnosticLabel="insight_report_data_quality"
variant="report"
/>
</View>
) : null}
</ReportSection>
<ReportStatGrid analytics={reportState.report.analytics} />
Expand Down
104 changes: 83 additions & 21 deletions components/ai-chat/ai-chat-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
Animated,
Keyboard,
KeyboardAvoidingView,
type NativeScrollEvent,
type NativeSyntheticEvent,
Pressable,
ScrollView,
Text,
Expand All @@ -21,13 +23,15 @@ import {
import { useSafeAreaInsets } from "react-native-safe-area-context";

import { AnimatedIconButton } from "@/components/ui/animated-icon-button";
import { AIResponseRenderer } from "@/components/ai/ai-response-renderer";
import { ScreenErrorState } from "@/components/states/ScreenErrorState";
import { CONNECTION_STATE_COLORS } from "@/constants/theme";
import { useAppDialog } from "@/hooks/useAppDialog";
import { useConnectivity } from "@/hooks/useConnectivity";
import { useDelayedVisibility } from "@/hooks/useDelayedVisibility";
import { generateLocalJournalResponse } from "@/lib/ai/localJournalAssistant";
import { generateRemoteJournalResponse } from "@/lib/ai/remoteJournalAssistant";
import { addSafeBreakOpportunities } from "@/lib/text/add-safe-break-opportunities";
import { useJournalStore } from "@/store/journal-store";
import { useChatStore } from "@/store/useChatStore";
import type { ChatMessage } from "@/types/chat";
Expand All @@ -52,15 +56,7 @@ const chatMessageTextStyle = {
paddingBottom: 8,
paddingTop: 3,
} as const;
const assistantMessageTextStyle = {
flexShrink: 1,
flexWrap: "wrap",
includeFontPadding: true,
overflow: "visible",
paddingBottom: 8,
paddingRight: 3,
paddingTop: 3,
} as const;
const nearBottomThreshold = 80;

export function AiChatScreen({
avatarUrl,
Expand All @@ -73,13 +69,15 @@ export function AiChatScreen({
const connectivity = useConnectivity();
const requestIdRef = useRef(0);
const scrollViewRef = useRef<ScrollView>(null);
const isNearBottomRef = useRef(true);
const [message, setMessage] = useState("");
const [composerTextHeight, setComposerTextHeight] = useState(
minComposerTextHeight,
);
const [isThinking, setIsThinking] = useState(false);
const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false);
const [keyboardOffset, setKeyboardOffset] = useState(0);
const [showJumpToLatest, setShowJumpToLatest] = useState(false);
const journalEntries = useJournalStore((state) => state.entries);
const chatMessages = useChatStore((state) => state.messages);
const chatHasHydrated = useChatStore((state) => state.hasHydrated);
Expand Down Expand Up @@ -151,7 +149,9 @@ export function AiChatScreen({

useEffect(() => {
requestIdRef.current += 1;
isNearBottomRef.current = true;
setIsThinking(false);
setShowJumpToLatest(false);
}, [userId]);

useEffect(() => {
Expand Down Expand Up @@ -236,6 +236,33 @@ export function AiChatScreen({
);
}

function handleChatScroll(
event: NativeSyntheticEvent<NativeScrollEvent>,
) {
const { contentOffset, contentSize, layoutMeasurement } = event.nativeEvent;
const isNearBottom =
contentOffset.y + layoutMeasurement.height >=
contentSize.height - nearBottomThreshold;

isNearBottomRef.current = isNearBottom;
setShowJumpToLatest(!isNearBottom);
}

function handleChatContentSizeChange() {
if (isNearBottomRef.current) {
scrollViewRef.current?.scrollToEnd({ animated: true });
return;
}

setShowJumpToLatest(true);
}

function handleJumpToLatest() {
isNearBottomRef.current = true;
setShowJumpToLatest(false);
scrollViewRef.current?.scrollToEnd({ animated: true });
}

async function handleSendMessage(nextMessage = message) {
const trimmedMessage = nextMessage.trim();

Expand Down Expand Up @@ -299,6 +326,7 @@ export function AiChatScreen({
content: remoteResponse.message,
createdAt: new Date().toISOString(),
id: createChatMessageId(),
isPartial: remoteResponse.isPartial,
relatedEntryIds: remoteResponse.relatedEntryIds,
role: "assistant",
source: remoteResponse.source,
Expand Down Expand Up @@ -414,8 +442,10 @@ export function AiChatScreen({
paddingTop: 16,
}}
keyboardShouldPersistTaps="handled"
onContentSizeChange={() => scrollViewRef.current?.scrollToEnd()}
onContentSizeChange={handleChatContentSizeChange}
onScroll={handleChatScroll}
ref={scrollViewRef}
scrollEventThrottle={16}
showsVerticalScrollIndicator={false}
>
<View className="items-center">
Expand Down Expand Up @@ -473,6 +503,21 @@ export function AiChatScreen({

</ScrollView>

{showJumpToLatest ? (
<View className="items-center px-6 pb-2">
<Pressable
accessibilityRole="button"
className="min-h-10 items-center justify-center rounded-full bg-white px-5"
onPress={handleJumpToLatest}
style={{ boxShadow: "0 3px 10px rgba(39, 39, 42, 0.16)" }}
>
<Text className="text-[14px] font-bold leading-6 text-[#FF2056]">
Jump to latest
</Text>
</Pressable>
</View>
) : null}

<LinearGradient
colors={["rgba(250, 247, 242, 0)", "#FAF7F2"]}
className="px-6 pt-4"
Expand Down Expand Up @@ -629,10 +674,9 @@ function ChatBubble({
userBubbleMaxWidth: number;
}) {
const isUser = message.role === "user";
const messageText =
isFirstAssistant && message.role === "assistant"
? `Hi ${displayName} 🌸 ${message.content}`
: message.content;
const firstAssistantGreeting = isFirstAssistant
? `Hi ${displayName} 🌸`
: null;
const messageTime = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "2-digit",
Expand Down Expand Up @@ -671,7 +715,7 @@ function ChatBubble({
>
<BubbleMessageText
className="text-[16px] font-semibold text-rose-50"
text={messageText}
text={message.content}
/>
</LinearGradient>
<Text className="mt-2 pr-1 text-[11px] font-medium leading-4 text-[#A1A1AA]">
Expand Down Expand Up @@ -707,12 +751,30 @@ function ChatBubble({
overflow: "visible",
}}
>
<BubbleMessageText
className="text-[16px] text-[#51515B]"
style={assistantMessageTextStyle}
text={messageText}
selectable={false}
{firstAssistantGreeting ? (
<Text
className="text-[16px] leading-6 text-[#51515B]"
selectable
>
{firstAssistantGreeting}
</Text>
) : null}
<AIResponseRenderer
content={message.content}
diagnosticLabel="ai_chat_message"
testID={`assistant-message-${message.id}`}
variant="chat"
/>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{message.isPartial ? (
<Text
accessibilityRole="alert"
className="mt-3 text-[13px] font-semibold leading-6 text-[#9F1239]"
selectable
>
This response stopped before it was complete. Ask DearDiary to
continue, or try your question again.
</Text>
) : null}
</LinearGradient>
<Text className="mt-2 pl-1 text-[11px] font-medium leading-4 text-[#A1A1AA]">
{messageTime}
Expand All @@ -739,7 +801,7 @@ function BubbleMessageText({
selectable={selectable}
style={style}
>
{text}
{addSafeBreakOpportunities(text)}
</Text>
);
}
Expand Down
Loading