diff --git a/companion/app/(tabs)/(more)/index.ios.tsx b/companion/app/(tabs)/(more)/index.ios.tsx index c262635cc7..9173a75c96 100644 --- a/companion/app/(tabs)/(more)/index.ios.tsx +++ b/companion/app/(tabs)/(more)/index.ios.tsx @@ -6,6 +6,7 @@ import { useState } from "react"; import { Alert, Pressable, ScrollView, Text, TouchableOpacity, View } from "react-native"; import { LogoutConfirmModal } from "@/components/LogoutConfirmModal"; import { useAuth } from "@/contexts/AuthContext"; +import { useQueryContext } from "@/contexts/QueryContext"; import { useUserProfile } from "@/hooks"; import { showErrorAlert } from "@/utils/alerts"; import { openInAppBrowser } from "@/utils/browser"; @@ -22,11 +23,14 @@ interface MoreMenuItem { export default function More() { const router = useRouter(); const { logout } = useAuth(); + const { clearCache } = useQueryContext(); const [showLogoutModal, setShowLogoutModal] = useState(false); const { data: userProfile } = useUserProfile(); const performLogout = async () => { try { + // Clear in-memory cache before logout + await clearCache(); await logout(); } catch (error) { const message = error instanceof Error ? error.message : String(error); @@ -111,7 +115,7 @@ export default function More() { {/* Content */} { try { + // Clear in-memory cache before logout + await clearCache(); await logout(); } catch (error) { const message = error instanceof Error ? error.message : String(error); @@ -84,7 +88,7 @@ export default function More() { ]; return ( - +
Promise; +}; + +const SELECTED_COLOR = "#000000"; + export default function TabLayout() { if (Platform.OS === "web") { return ; @@ -10,42 +18,65 @@ export default function TabLayout() { return ( - } - /> + {Platform.select({ + ios: , + android: ( + } + selectedColor={SELECTED_COLOR} + /> + ), + })} Event Types - } - /> + {Platform.select({ + ios: , + android: ( + } + selectedColor={SELECTED_COLOR} + /> + ), + })} Bookings - } - /> + {Platform.select({ + ios: , + android: ( + } + selectedColor={SELECTED_COLOR} + /> + ), + })} Availability - } - /> + {Platform.select({ + ios: , + android: ( + } + selectedColor={SELECTED_COLOR} + /> + ), + })} More diff --git a/companion/components/event-type-list-item/EventTypeListItem.ios.tsx b/companion/components/event-type-list-item/EventTypeListItem.ios.tsx index 4c88ec1407..5e02f3bce1 100644 --- a/companion/components/event-type-list-item/EventTypeListItem.ios.tsx +++ b/companion/components/event-type-list-item/EventTypeListItem.ios.tsx @@ -22,6 +22,34 @@ export const EventTypeListItem = ({ useEventTypeListItemData(item); const isLast = index === filteredEventTypes.length - 1; + // Calculate badge count to force remount when badges change + // This fixes SwiftUI Host caching stale layout measurements + const hasSeats = + item.seats && + !("disabled" in item.seats && item.seats.disabled) && + "seatsPerTimeSlot" in item.seats && + item.seats.seatsPerTimeSlot && + item.seats.seatsPerTimeSlot > 0; + const hasRecurrence = + item.recurrence && + !("disabled" in item.recurrence && item.recurrence.disabled) && + "occurrences" in item.recurrence && + item.recurrence.occurrences; + const requiresConfirmation = + item.confirmationPolicy && + !("disabled" in item.confirmationPolicy && item.confirmationPolicy.disabled) && + "type" in item.confirmationPolicy && + item.confirmationPolicy.type === "always"; + + const badgeCount = [ + true, // duration always present + item.hidden, + hasSeats, + hasPrice, + hasRecurrence, + requiresConfirmation, + ].filter(Boolean).length; + type ButtonSystemImage = React.ComponentProps["systemImage"]; type EventTypeIcon = Exclude; @@ -63,6 +91,8 @@ export const EventTypeListItem = ({ }, ]; + // Use minHeight based on badge count to ensure proper spacing + // This fixes SwiftUI Host caching stale layout measurements return ( - + {/* Calculate minHeight based on badge rows to ensure proper spacing */} + {/* 1-3 badges = 1 row, 4-5 badges = likely 2 rows, 6 badges = 2 rows */} + handleEventTypePress(item)} style={{ - paddingTop: 16, - paddingBottom: 22, paddingLeft: 16, + paddingBottom: 10, flex: 1, marginRight: 12, }} @@ -114,7 +151,7 @@ export const EventTypeListItem = ({ /> - + { try { await clearAuth(); + // Clear all cached queries to ensure fresh data on re-login + try { + await clearQueryCache(); + } catch (cacheError) { + console.warn("Failed to clear query cache during logout:", cacheError); + } resetAuthState(); } catch (error) { const message = getErrorMessage(error); diff --git a/companion/services/oauthService.ts b/companion/services/oauthService.ts index 9f6f706a49..9265a4ad1c 100644 --- a/companion/services/oauthService.ts +++ b/companion/services/oauthService.ts @@ -221,7 +221,7 @@ export class CalComOAuthService { authUrl: string ): Promise<{ type: "success"; params: Record } | { type: "error" }> { const result = await WebBrowser.openAuthSessionAsync(authUrl, this.config.redirectUri, { - preferEphemeralSession: false, + preferEphemeralSession: true, }); if (result.type === "success") {