diff --git a/mobile/app/(app)/(tabs)/_layout.tsx b/mobile/app/(app)/(tabs)/_layout.tsx index 8e70fe5..5318c99 100644 --- a/mobile/app/(app)/(tabs)/_layout.tsx +++ b/mobile/app/(app)/(tabs)/_layout.tsx @@ -152,13 +152,28 @@ function SettingsHeaderButton() { ); } +// found.md: a push for a new message arrived but the speech bubble showed +// nothing. Polling keeps the badge current while the app stays open; reading +// a conversation invalidates conversations.* (see messages/[conversationId]). +const UNREAD_MESSAGES_POLL_MS = 30 * 1000; + function MessagesHeaderButton() { const router = useRouter(); + const trpc = useTRPC(); + const { activePetId } = useActivePet(); const { colors } = useTheme(); const styles = useMemo(() => makeStyles(colors), [colors]); + const { data } = useQuery({ + ...trpc.conversations.unreadCount.queryOptions({ petId: activePetId ?? "" }), + enabled: !!activePetId, + refetchInterval: UNREAD_MESSAGES_POLL_MS, + }); return ( router.push("/messages")} hitSlop={8} style={styles.headerButton}> - + + + + ); } @@ -172,16 +187,20 @@ function NotificationsTabIcon({ color, size }: { color: ColorValue; size: number ...trpc.notifications.unreadCount.queryOptions({ petId: activePetId ?? "" }), enabled: !!activePetId, }); - const count = data?.count ?? 0; return ( - {count > 0 && ( - - {count > 9 ? "9+" : count} - - )} + + + ); +} + +function UnreadBadge({ count, styles }: { count: number; styles: ReturnType }) { + if (count <= 0) return null; + return ( + + {count > 9 ? "9+" : count} ); }