From 478ca0524677dd63a4ab595e6a00f31ad3309960 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Mon, 12 Jan 2026 00:32:13 +0530 Subject: [PATCH] feat(companion): update Android booking detail page to match iOS design (#26715) * feat(companion): update Android booking detail page to match iOS design * feat(companion): add video icon button to Android header for join meeting * fix(companion): use eventType.description instead of booking.description Address Cubic AI review feedback - the booking description may contain Additional Notes which can cause duplicate display. The event type description is the proper source for this information. Co-Authored-By: unknown <> * Revert "fix(companion): use eventType.description instead of booking.description" This reverts commit 3e324313f2d72704ab51f723a52b2784b33e7867. * fix(companion): extract IOSPickerTrigger to platform-specific files for web compatibility * fix(companion): extract iOS pickers to platform-specific files for web compatibility Extract iOS-only native picker components from AvailabilityTab, BasicsTab, LimitsTab, RecurringTab, and LocationsList to platform-specific files to fix 'requireNativeViewManager is not available on web' error in browser extension. Each tab now has: - TabIOSPicker.ios.tsx - iOS implementation with native @expo/ui/swift-ui - TabIOSPicker.tsx - Web/Android fallback that returns null * fix(companion): fix Switch toggle thumb color for web/extension Change thumbColor condition from Platform.OS === 'android' to Platform.OS !== 'ios' so that web/extension also gets white thumb color instead of the default green. * fix(companion): fix Participants section padding for web/extension Move padding classes from AppPressable to a child View to ensure padding is properly applied on web. AnimatedPressable doesn't reliably apply className padding on web platforms. * fix(companion): fix unexpected text node error on web Change ternary operator in Hosts section from 'booking.user && (...)' to 'booking.user ? (...) : null' to prevent React Native Web from trying to render 'false' as a text node when booking.user is falsy. * fix(companion): wrap Stack.Header in iOS platform check for web compatibility The Stack.Header components are iOS-specific and cause 'Unexpected text node' errors on web. Wrap them in Platform.OS === 'ios' check to prevent rendering on web/Android where they're not supported. * fix(companion): convert all && conditionals to ternary operators for web compatibility React Native Web renders boolean 'false' from && operators as text nodes. Changed all conditional rendering patterns from 'condition && ()' to 'condition ? () : null' to prevent 'Unexpected text node' errors. --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../app/(tabs)/(bookings)/booking-detail.tsx | 99 +-- companion/components/LocationsList.tsx | 50 +- .../components/LocationsListIOSPicker.ios.tsx | 71 ++ .../components/LocationsListIOSPicker.tsx | 14 + .../event-type-detail/tabs/AdvancedTab.tsx | 42 +- .../tabs/AvailabilityTab.tsx | 46 +- .../tabs/AvailabilityTabIOSPicker.ios.tsx | 40 ++ .../tabs/AvailabilityTabIOSPicker.tsx | 9 + .../event-type-detail/tabs/BasicsTab.tsx | 59 +- .../tabs/BasicsTabIOSPicker.ios.tsx | 36 + .../tabs/BasicsTabIOSPicker.tsx | 9 + .../tabs/IOSPickerTrigger.ios.tsx | 36 + .../tabs/IOSPickerTrigger.tsx | 9 + .../event-type-detail/tabs/LimitsTab.tsx | 51 +- .../tabs/LimitsTabIOSPicker.ios.tsx | 36 + .../tabs/LimitsTabIOSPicker.tsx | 9 + .../event-type-detail/tabs/RecurringTab.tsx | 45 +- .../tabs/RecurringTabIOSPicker.ios.tsx | 40 ++ .../tabs/RecurringTabIOSPicker.tsx | 9 + .../screens/BookingDetailScreen.tsx | 629 ++++++++---------- 20 files changed, 698 insertions(+), 641 deletions(-) create mode 100644 companion/components/LocationsListIOSPicker.ios.tsx create mode 100644 companion/components/LocationsListIOSPicker.tsx create mode 100644 companion/components/event-type-detail/tabs/AvailabilityTabIOSPicker.ios.tsx create mode 100644 companion/components/event-type-detail/tabs/AvailabilityTabIOSPicker.tsx create mode 100644 companion/components/event-type-detail/tabs/BasicsTabIOSPicker.ios.tsx create mode 100644 companion/components/event-type-detail/tabs/BasicsTabIOSPicker.tsx create mode 100644 companion/components/event-type-detail/tabs/IOSPickerTrigger.ios.tsx create mode 100644 companion/components/event-type-detail/tabs/IOSPickerTrigger.tsx create mode 100644 companion/components/event-type-detail/tabs/LimitsTabIOSPicker.ios.tsx create mode 100644 companion/components/event-type-detail/tabs/LimitsTabIOSPicker.tsx create mode 100644 companion/components/event-type-detail/tabs/RecurringTabIOSPicker.ios.tsx create mode 100644 companion/components/event-type-detail/tabs/RecurringTabIOSPicker.tsx diff --git a/companion/app/(tabs)/(bookings)/booking-detail.tsx b/companion/app/(tabs)/(bookings)/booking-detail.tsx index 27908182ad..570a9791d7 100644 --- a/companion/app/(tabs)/(bookings)/booking-detail.tsx +++ b/companion/app/(tabs)/(bookings)/booking-detail.tsx @@ -1,5 +1,6 @@ import { Stack, useLocalSearchParams } from "expo-router"; import { useCallback, useMemo, useRef } from "react"; +import { Platform } from "react-native"; import { BookingDetailScreen } from "@/components/screens/BookingDetailScreen"; import { useAuth } from "@/contexts/AuthContext"; import { useBookingByUid } from "@/hooks/useBookings"; @@ -225,57 +226,59 @@ export default function BookingDetail() { }} /> - - Booking Details + {Platform.OS === "ios" && ( + + Booking Details - {/* Header right and left API only works on iOS ATM see: https://docs.expo.dev/versions/unversioned/sdk/router/#stackheaderright */} - - - Actions - + {/* Header right and left API only works on iOS ATM see: https://docs.expo.dev/versions/unversioned/sdk/router/#stackheaderright */} + + + Actions + - {/* Edit Event Section */} - - {bookingActionsSections.editEvent.map((action) => ( - - {action.label} - - ))} + {/* Edit Event Section */} + + {bookingActionsSections.editEvent.map((action) => ( + + {action.label} + + ))} + + + {/* After Event Section */} + + {bookingActionsSections.afterEvent.map((action) => ( + + {action.label} + + ))} + + + {/* Danger Zone Submenu */} + + {bookingActionsSections.standalone.map((action) => ( + + {action.label} + + ))} + - - {/* After Event Section */} - - {bookingActionsSections.afterEvent.map((action) => ( - - {action.label} - - ))} - - - {/* Danger Zone Submenu */} - - {bookingActionsSections.standalone.map((action) => ( - - {action.label} - - ))} - - - - + + + )} { - switch (type) { - case "address": - return "mappin.and.ellipse"; - case "link": - return "link"; - case "phone": - return "phone"; - default: - return "video"; - } -}; - export const AddLocationTrigger = ({ isHeader = false, locationOptions, @@ -116,30 +101,17 @@ export const AddLocationTrigger = ({ return trigger; } - return ( - - - - {locationOptions.flatMap((group) => - group.options - .filter((opt) => !isLocationAlreadyAdded(locations, opt.value)) - .map((option) => ( -