From b8c271cce1658b46b9cd00e781593d9b0f6dfbf0 Mon Sep 17 00:00:00 2001 From: Jamie Brunton Date: Sat, 17 Jan 2026 17:12:10 +0000 Subject: [PATCH] Add show all intensities toggle on packs page --- apps/mobile/app/(app)/packs/questions.tsx | 58 +++++- apps/mobile/src/store/packsStore.ts | 210 +++++++++++----------- 2 files changed, 166 insertions(+), 102 deletions(-) diff --git a/apps/mobile/app/(app)/packs/questions.tsx b/apps/mobile/app/(app)/packs/questions.tsx index 6d56cf1..aa89094 100644 --- a/apps/mobile/app/(app)/packs/questions.tsx +++ b/apps/mobile/app/(app)/packs/questions.tsx @@ -1,4 +1,4 @@ -import { View, Text, StyleSheet, RefreshControl, Platform, Alert, Pressable } from "react-native"; +import { View, Text, StyleSheet, RefreshControl, Platform, Alert, Pressable, Switch } from "react-native"; import { usePacksStore, useAuthStore, useSubscriptionStore } from "../../../src/store"; import { Ionicons } from "@expo/vector-icons"; import { router } from "expo-router"; @@ -258,7 +258,7 @@ function CategorySection({ } export default function QuestionPacksScreen() { - const { packs, categories, enabledPackIds, togglePack, isLoading, fetchPacks } = usePacksStore(); + const { packs, categories, enabledPackIds, togglePack, isLoading, fetchPacks, showAllIntensities, setShowAllIntensities } = usePacksStore(); const { user, partner } = useAuthStore(); const { subscription } = useSubscriptionStore(); @@ -335,6 +335,10 @@ export default function QuestionPacksScreen() { setTeaserPack(null); }, []); + const handleToggleComfortZone = useCallback((value: boolean) => { + setShowAllIntensities(value); + }, [setShowAllIntensities]); + const enabledPackCount = enabledPackIds.length; const totalQuestions = packs .filter(p => enabledPackIds.includes(p.id)) @@ -442,6 +446,32 @@ export default function QuestionPacksScreen() { {totalQuestions} {totalQuestions === 1 ? "question" : "questions"} from {enabledPackCount} {enabledPackCount === 1 ? "pack" : "packs"} + + handleToggleComfortZone(!showAllIntensities)} + > + + + + Show all intensities + + + + {/* Categories */} @@ -610,6 +640,30 @@ const styles = StyleSheet.create({ color: colors.textSecondary, fontWeight: "500", }, + comfortZoneToggle: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + backgroundColor: "rgba(255, 255, 255, 0.05)", + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + borderRadius: radius.md, + borderWidth: 1, + borderColor: colors.glass.border, + marginTop: spacing.md, + width: "100%", + maxWidth: 280, + }, + comfortZoneToggleLeft: { + flexDirection: "row", + alignItems: "center", + gap: spacing.sm, + }, + comfortZoneToggleText: { + ...typography.subhead, + color: colors.textSecondary, + fontWeight: "500", + }, // Scroll Content scrollContent: { diff --git a/apps/mobile/src/store/packsStore.ts b/apps/mobile/src/store/packsStore.ts index d763762..580f685 100644 --- a/apps/mobile/src/store/packsStore.ts +++ b/apps/mobile/src/store/packsStore.ts @@ -1,7 +1,7 @@ -import { create } from "zustand"; -import { supabase } from "../lib/supabase"; -import { Events } from "../lib/analytics"; -import type { QuestionPack, Category } from "@/types"; +import { create } from "zustand"; +import { supabase } from "../lib/supabase"; +import { Events } from "../lib/analytics"; +import type { QuestionPack, Category } from "@/types"; import { useAuthStore } from "./authStore"; const DEFAULT_MAX_INTENSITY = 2; @@ -21,30 +21,34 @@ const getUserMaxIntensity = () => { }; interface PacksState { - packs: QuestionPack[]; - categories: Category[]; - enabledPackIds: string[]; - isLoading: boolean; - fetchPacks: () => Promise; - fetchEnabledPacks: () => Promise; - ensureEnabledPacksLoaded: () => Promise; - togglePack: (packId: string) => Promise<{ success: boolean; reason?: string }>; - clearPacks: () => void; -} - -export const usePacksStore = create((set, get) => ({ - packs: [], - categories: [], - enabledPackIds: [], - isLoading: false, - - fetchPacks: async () => { - set({ isLoading: true }); - + packs: QuestionPack[]; + categories: Category[]; + enabledPackIds: string[]; + isLoading: boolean; + showAllIntensities: boolean; + fetchPacks: () => Promise; + fetchEnabledPacks: () => Promise; + ensureEnabledPacksLoaded: () => Promise; + togglePack: (packId: string) => Promise<{ success: boolean; reason?: string }>; + setShowAllIntensities: (value: boolean) => void; + clearPacks: () => void; +} + +export const usePacksStore = create((set, get) => ({ + packs: [], + categories: [], + enabledPackIds: [], + isLoading: false, + showAllIntensities: false, + + fetchPacks: async () => { + set({ isLoading: true }); + // Get user's max intensity preference const maxIntensity = getUserMaxIntensity(); + const showAllIntensities = get().showAllIntensities; + - // Fetch categories const { data: categories } = await supabase .from("categories") @@ -62,8 +66,8 @@ export const usePacksStore = create((set, get) => ({ .eq("is_public", true) .order("sort_order"); - // Filter out packs above user's intensity preference - if (maxIntensity < 5) { + // Filter out packs above user's intensity preference (unless showing all) + if (maxIntensity < 5 && !showAllIntensities) { query = query.or(`max_intensity.is.null,max_intensity.lte.${maxIntensity}`); } @@ -86,76 +90,82 @@ export const usePacksStore = create((set, get) => ({ isLoading: false }); }, - - fetchEnabledPacks: async () => { - const coupleId = useAuthStore.getState().user?.couple_id; - if (!coupleId) { - set({ enabledPackIds: [] }); - return; - } - - const { data: couplePacks } = await supabase - .from("couple_packs") - .select("pack_id") - .eq("couple_id", coupleId) - .eq("enabled", true); - - const ids = couplePacks?.map(cp => cp.pack_id) || []; - set({ enabledPackIds: ids }); - }, - - ensureEnabledPacksLoaded: async () => { - // Only fetch if not already loaded - lightweight check for swipe screen - const coupleId = useAuthStore.getState().user?.couple_id; - if (!coupleId) return; - - if (get().enabledPackIds.length === 0) { - await get().fetchEnabledPacks(); - } - }, - - togglePack: async (packId: string): Promise<{ success: boolean; reason?: string }> => { - const coupleId = useAuthStore.getState().user?.couple_id; - if (!coupleId) { - return { success: false, reason: "no_couple" }; - } - - const isEnabled = get().enabledPackIds.includes(packId); - const newValue = !isEnabled; - - // Optimistic update - const newIds = newValue - ? [...get().enabledPackIds, packId] - : get().enabledPackIds.filter(id => id !== packId); - - set({ enabledPackIds: newIds }); - - const { error } = await supabase - .from("couple_packs") - .upsert({ - couple_id: coupleId, - pack_id: packId, - enabled: newValue - }); - - if (error) { - console.error("Error toggling pack:", error); - // Revert by refetching - get().fetchEnabledPacks(); - return { success: false, reason: "error" }; - } - - // Track pack enable/disable - if (newValue) { - Events.packEnabled(packId); - } else { - Events.packDisabled(packId); - } - - return { success: true }; - }, - - clearPacks: () => { - set({ enabledPackIds: [] }); - }, -})); + + fetchEnabledPacks: async () => { + const coupleId = useAuthStore.getState().user?.couple_id; + if (!coupleId) { + set({ enabledPackIds: [] }); + return; + } + + const { data: couplePacks } = await supabase + .from("couple_packs") + .select("pack_id") + .eq("couple_id", coupleId) + .eq("enabled", true); + + const ids = couplePacks?.map(cp => cp.pack_id) || []; + set({ enabledPackIds: ids }); + }, + + ensureEnabledPacksLoaded: async () => { + // Only fetch if not already loaded - lightweight check for swipe screen + const coupleId = useAuthStore.getState().user?.couple_id; + if (!coupleId) return; + + if (get().enabledPackIds.length === 0) { + await get().fetchEnabledPacks(); + } + }, + + togglePack: async (packId: string): Promise<{ success: boolean; reason?: string }> => { + const coupleId = useAuthStore.getState().user?.couple_id; + if (!coupleId) { + return { success: false, reason: "no_couple" }; + } + + const isEnabled = get().enabledPackIds.includes(packId); + const newValue = !isEnabled; + + // Optimistic update + const newIds = newValue + ? [...get().enabledPackIds, packId] + : get().enabledPackIds.filter(id => id !== packId); + + set({ enabledPackIds: newIds }); + + const { error } = await supabase + .from("couple_packs") + .upsert({ + couple_id: coupleId, + pack_id: packId, + enabled: newValue + }); + + if (error) { + console.error("Error toggling pack:", error); + // Revert by refetching + get().fetchEnabledPacks(); + return { success: false, reason: "error" }; + } + + // Track pack enable/disable + if (newValue) { + Events.packEnabled(packId); + } else { + Events.packDisabled(packId); + } + + return { success: true }; + }, + + setShowAllIntensities: (value: boolean) => { + set({ showAllIntensities: value }); + // Refetch packs with new filter setting + get().fetchPacks(); + }, + + clearPacks: () => { + set({ enabledPackIds: [] }); + }, +}));