diff --git a/apps/mobile/src/components/discovery/ContentTile.tsx b/apps/mobile/src/components/discovery/ContentTile.tsx
index 7e511f6..4c7936a 100644
--- a/apps/mobile/src/components/discovery/ContentTile.tsx
+++ b/apps/mobile/src/components/discovery/ContentTile.tsx
@@ -82,11 +82,6 @@ export function ContentTile({ pack, isLocked, isNew = false, progress, onPress }
{/* Content */}
- {pack.category?.name && (
-
- {pack.category.name}
-
- )}
{pack.name}
@@ -173,13 +168,6 @@ const styles = StyleSheet.create({
justifyContent: 'center',
marginTop: spacing.sm,
},
- categoryText: {
- ...typography.caption2,
- color: 'rgba(255, 255, 255, 0.7)',
- textTransform: 'uppercase',
- letterSpacing: 1,
- marginBottom: 2,
- },
titleText: {
...typography.headline,
color: colors.text,
diff --git a/apps/mobile/src/components/questions/QuestionCard.tsx b/apps/mobile/src/components/questions/QuestionCard.tsx
index 78cbc54..5ffa9b5 100644
--- a/apps/mobile/src/components/questions/QuestionCard.tsx
+++ b/apps/mobile/src/components/questions/QuestionCard.tsx
@@ -20,7 +20,7 @@ import { SharePreviewModal } from "../share";
const ACTION_BUTTON_SIZE = 72;
const ACTION_BUTTON_OVERHANG_PERCENT = 0.5; // 50% of button hangs below the card
const actionButtonOffset = ACTION_BUTTON_SIZE * ACTION_BUTTON_OVERHANG_PERCENT;
-const actionButtonBackground = colors.muted;
+const actionButtonBackground = '#3a3a42';
// Haptics helper - not supported on web
const triggerHaptic = async (style: 'light' | 'medium' | 'heavy' = 'medium') => {
diff --git a/apps/mobile/src/features/profile/ProfileScreen.tsx b/apps/mobile/src/features/profile/ProfileScreen.tsx
index ebd1c23..3ad37c3 100644
--- a/apps/mobile/src/features/profile/ProfileScreen.tsx
+++ b/apps/mobile/src/features/profile/ProfileScreen.tsx
@@ -121,7 +121,7 @@ export function ProfileScreen() {
{user?.name || "User"}
-
+
{isAnonymous ? "Unsaved account" : (user?.email || "")}
diff --git a/apps/mobile/src/features/profile/screens/ProfileSettingsScreen.tsx b/apps/mobile/src/features/profile/screens/ProfileSettingsScreen.tsx
index 618ece5..8de63f6 100644
--- a/apps/mobile/src/features/profile/screens/ProfileSettingsScreen.tsx
+++ b/apps/mobile/src/features/profile/screens/ProfileSettingsScreen.tsx
@@ -260,7 +260,7 @@ export function ProfileSettingsScreen() {
color={colors.textTertiary}
style={styles.inputIcon}
/>
-
+
{authEmail || (isAnonymous ? 'Guest account' : 'Loading...')}
@@ -407,6 +407,7 @@ const styles = StyleSheet.create({
readOnlyText: {
...typography.body,
color: colors.textSecondary,
+ flex: 1,
},
// Gender
genderDescription: {
diff --git a/apps/mobile/src/hooks/useMessageSubscription.ts b/apps/mobile/src/hooks/useMessageSubscription.ts
index 5f67ade..5a3e4c0 100644
--- a/apps/mobile/src/hooks/useMessageSubscription.ts
+++ b/apps/mobile/src/hooks/useMessageSubscription.ts
@@ -41,6 +41,10 @@ export const useMessageSubscription = (
const [loading, setLoading] = useState(true);
const isFocusedRef = useRef(false);
+ // Track messages that arrive via real-time during initial fetch to avoid race condition
+ const pendingRealtimeMessagesRef = useRef([]);
+ const isFetchingRef = useRef(false);
+
// Filter out messages that the user has deleted for themselves
const messages = useMemo(() => {
return allMessages.filter(m => !deletedMessageIds.has(m.id));
@@ -104,6 +108,8 @@ export const useMessageSubscription = (
const fetchMessagesAndMarkRead = async () => {
setLoading(true);
+ isFetchingRef.current = true;
+ pendingRealtimeMessagesRef.current = [];
setAllMessages([]); // Clear previous chat's messages immediately
const { data } = await supabase
@@ -113,7 +119,14 @@ export const useMessageSubscription = (
.order('created_at', { ascending: false });
if (data) {
- setAllMessages(data);
+ // Merge any messages that arrived via real-time during the fetch
+ const pendingMessages = pendingRealtimeMessagesRef.current;
+ const fetchedIds = new Set(data.map(m => m.id));
+ const newRealtimeMessages = pendingMessages.filter(m => !fetchedIds.has(m.id));
+
+ // Combine: new real-time messages first (newest), then fetched data
+ const mergedMessages = [...newRealtimeMessages, ...data];
+ setAllMessages(mergedMessages);
const now = new Date().toISOString();
@@ -145,6 +158,8 @@ export const useMessageSubscription = (
}
}
+ isFetchingRef.current = false;
+ pendingRealtimeMessagesRef.current = [];
setLoading(false);
};
@@ -179,7 +194,13 @@ export const useMessageSubscription = (
newMessage.read_at = now;
}
- setAllMessages(prev => [newMessage, ...prev]);
+ // If initial fetch is in progress, queue the message to avoid race condition
+ // where setAllMessages(data) would overwrite this message
+ if (isFetchingRef.current) {
+ pendingRealtimeMessagesRef.current.push(newMessage);
+ } else {
+ setAllMessages(prev => [newMessage, ...prev]);
+ }
onNewMessage?.();
}
)