diff --git a/companion/components/LoginScreen.tsx b/companion/components/LoginScreen.tsx index 218ab79887..e4ea04f3e3 100644 --- a/companion/components/LoginScreen.tsx +++ b/companion/components/LoginScreen.tsx @@ -1,10 +1,10 @@ +import { useAuth } from "../contexts/AuthContext"; +import { showErrorAlert } from "../utils/alerts"; +import { openInAppBrowser } from "../utils/browser"; +import { CalComLogo } from "./CalComLogo"; import React from "react"; import { View, Text, TouchableOpacity, Platform, StyleSheet } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useAuth } from "../contexts/AuthContext"; -import { CalComLogo } from "./CalComLogo"; -import { showErrorAlert } from "../utils/alerts"; -import { openInAppBrowser } from "../utils/browser"; export function LoginScreen() { const { loginWithOAuth, loading } = useAuth(); @@ -66,7 +66,7 @@ export function LoginScreen() { diff --git a/companion/components/LogoutButton.tsx b/companion/components/LogoutButton.tsx index 5dc50e213b..1b98820050 100644 --- a/companion/components/LogoutButton.tsx +++ b/companion/components/LogoutButton.tsx @@ -1,7 +1,7 @@ -import React, { useState } from "react"; -import { TouchableOpacity, Text, Alert, Platform } from "react-native"; import { useAuth } from "../contexts/AuthContext"; import { LogoutConfirmModal } from "./LogoutConfirmModal"; +import React, { useState } from "react"; +import { TouchableOpacity, Text, Alert, Platform } from "react-native"; interface LogoutButtonProps { className?: string; @@ -62,7 +62,7 @@ export function LogoutButton({ className = "" }: LogoutButtonProps) { <> diff --git a/companion/components/SvgImage.tsx b/companion/components/SvgImage.tsx index 4848f1fd25..511ff09e9d 100644 --- a/companion/components/SvgImage.tsx +++ b/companion/components/SvgImage.tsx @@ -28,7 +28,9 @@ export const SvgImage: React.FC = ({ uri, width, height, style }) source={{ uri }} style={{ width, height }} resizeMode="contain" - onError={() => console.log("Image load error:", uri)} + onError={() => { + // Image load errors are expected for invalid/missing URLs - silently handled + }} /> ); @@ -41,8 +43,8 @@ export const SvgImage: React.FC = ({ uri, width, height, style }) uri={uri} width={width} height={height} - onError={(error) => { - console.log("SVG load error, falling back to Image:", error); + onError={() => { + // SVG load errors trigger fallback to regular Image component setUseFallback(true); }} /> diff --git a/companion/components/Tooltip.tsx b/companion/components/Tooltip.tsx index 3dbb300069..b18a643a4c 100644 --- a/companion/components/Tooltip.tsx +++ b/companion/components/Tooltip.tsx @@ -1,69 +1,10 @@ -import React, { useState, useRef, useEffect } from "react"; -import { View, Text, Platform } from "react-native"; - interface TooltipProps { text: string; children: React.ReactElement; } export function Tooltip({ text, children }: TooltipProps) { - const [showTooltip, setShowTooltip] = useState(false); - const [position, setPosition] = useState({ top: 0, left: 0 }); - const containerRef = useRef(null); - - if (Platform.OS !== "web") { - return children; - } - - const handleMouseEnter = (e: any) => { - if (containerRef.current) { - const rect = containerRef.current.getBoundingClientRect(); - setPosition({ - top: rect.top - 40, - left: rect.left + rect.width / 2 + 20, - }); - setShowTooltip(true); - } - }; - - const handleMouseLeave = () => { - setShowTooltip(false); - }; - - return ( - - {children} - {showTooltip ? ( - - {/* @ts-ignore - web-only styles */} - - {text} - - - ) : null} - - ); + // Native Tooltip is a no-op; web implementation lives in Tooltip.web.tsx + void text; + return children; } diff --git a/companion/components/Tooltip.web.tsx b/companion/components/Tooltip.web.tsx new file mode 100644 index 0000000000..dbed2544a0 --- /dev/null +++ b/companion/components/Tooltip.web.tsx @@ -0,0 +1,50 @@ +import React, { useState } from "react"; + +interface TooltipProps { + text: string; + children: React.ReactElement; +} + +export function Tooltip({ text, children }: TooltipProps) { + const [showTooltip, setShowTooltip] = useState(false); + + const handleMouseEnter = () => { + setShowTooltip(true); + }; + + const handleMouseLeave = () => { + setShowTooltip(false); + }; + + return ( + + {children} + {showTooltip ? ( + + {text} + + ) : null} + + ); +} diff --git a/companion/components/event-type-detail/tabs/AvailabilityTab.tsx b/companion/components/event-type-detail/tabs/AvailabilityTab.tsx index 0202c63bc3..75961b0869 100644 --- a/companion/components/event-type-detail/tabs/AvailabilityTab.tsx +++ b/companion/components/event-type-detail/tabs/AvailabilityTab.tsx @@ -1,7 +1,7 @@ +import { Schedule } from "../../../services/calcom"; +import { Ionicons } from "@expo/vector-icons"; import React from "react"; import { View, Text, TouchableOpacity } from "react-native"; -import { Ionicons } from "@expo/vector-icons"; -import { Schedule } from "../../../services/calcom"; interface DaySchedule { day: string; @@ -15,12 +15,21 @@ interface AvailabilityTabProps { setShowScheduleDropdown: (show: boolean) => void; schedulesLoading: boolean; scheduleDetailsLoading: boolean; - selectedScheduleDetails: any; + selectedScheduleDetails: Schedule | null; getDaySchedules: () => DaySchedule[]; formatTime: (time: string) => string; selectedTimezone: string; } +const sectionDividerStyle = { + borderTopWidth: 1, + borderTopColor: "#E5E5EA", + marginLeft: -20, + marginRight: -20, + paddingLeft: 20, + paddingRight: 20, +}; + export function AvailabilityTab(props: AvailabilityTabProps) { return ( @@ -44,17 +53,7 @@ export function AvailabilityTab(props: AvailabilityTabProps) { {props.selectedSchedule ? ( <> - + {props.scheduleDetailsLoading ? ( Loading schedule details... @@ -71,7 +70,9 @@ export function AvailabilityTab(props: AvailabilityTabProps) { {dayInfo.available && dayInfo.startTime && dayInfo.endTime - ? `${props.formatTime(dayInfo.startTime)} - ${props.formatTime(dayInfo.endTime)}` + ? `${props.formatTime(dayInfo.startTime)} - ${props.formatTime( + dayInfo.endTime + )}` : "Unavailable"} @@ -85,17 +86,7 @@ export function AvailabilityTab(props: AvailabilityTabProps) { )} - + Timezone diff --git a/companion/components/event-type-list-item/EventTypeListItem.ios.tsx b/companion/components/event-type-list-item/EventTypeListItem.ios.tsx index cc01b013f9..65315449f2 100644 --- a/companion/components/event-type-list-item/EventTypeListItem.ios.tsx +++ b/companion/components/event-type-list-item/EventTypeListItem.ios.tsx @@ -1,13 +1,13 @@ -import { Ionicons } from "@expo/vector-icons"; -import React from "react"; -import { View, Text, Pressable } from "react-native"; -import { Host, ContextMenu, Button, Image, HStack } from "@expo/ui/swift-ui"; - -import { buttonStyle, frame, padding } from "@expo/ui/swift-ui/modifiers"; -import { EventTypeListItemProps } from "./types"; -import { isLiquidGlassAvailable } from "expo-glass-effect"; +import { formatDuration } from "../../utils/formatters"; import { getEventDuration } from "../../utils/getEventDuration"; import { normalizeMarkdown } from "../../utils/normalizeMarkdown"; +import { EventTypeListItemProps } from "./types"; +import { Host, ContextMenu, Button, Image, HStack } from "@expo/ui/swift-ui"; +import { buttonStyle, frame, padding } from "@expo/ui/swift-ui/modifiers"; +import { Ionicons } from "@expo/vector-icons"; +import { isLiquidGlassAvailable } from "expo-glass-effect"; +import React from "react"; +import { View, Text, Pressable } from "react-native"; export const EventTypeListItem = ({ item, @@ -25,9 +25,12 @@ export const EventTypeListItem = ({ const duration = getEventDuration(item); const isLast = index === filteredEventTypes.length - 1; + type ButtonSystemImage = React.ComponentProps["systemImage"]; + type EventTypeIcon = Exclude; + const eventTypes: { label: string; - icon: any; + icon: EventTypeIcon; onPress: () => void; role: "default" | "destructive"; }[] = [ @@ -63,18 +66,6 @@ export const EventTypeListItem = ({ }, ]; - const formatDuration = (minutes: number | undefined) => { - if (!minutes || minutes <= 0) { - return "0m"; - } - if (minutes < 60) { - return `${minutes}m`; - } - const hours = Math.floor(minutes / 60); - const remainingMinutes = minutes % 60; - return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`; - }; - return ( diff --git a/companion/components/event-type-list-item/EventTypeListItem.tsx b/companion/components/event-type-list-item/EventTypeListItem.tsx index 2d080b7520..4a282092b4 100644 --- a/companion/components/event-type-list-item/EventTypeListItem.tsx +++ b/companion/components/event-type-list-item/EventTypeListItem.tsx @@ -1,13 +1,13 @@ +import { Tooltip } from "../../components/Tooltip"; +import { formatDuration } from "../../utils/formatters"; +import { getEventDuration } from "../../utils/getEventDuration"; +import { normalizeMarkdown } from "../../utils/normalizeMarkdown"; +import { EventTypeListItemProps } from "./types"; import { Ionicons } from "@expo/vector-icons"; import React from "react"; import { View, Text, TouchableOpacity } from "react-native"; import Svg, { Path } from "react-native-svg"; -import { Tooltip } from "../../components/Tooltip"; -import { EventTypeListItemProps } from "./types"; -import { getEventDuration } from "../../utils/getEventDuration"; -import { normalizeMarkdown } from "../../utils/normalizeMarkdown"; - export const EventTypeListItem = ({ item, index, @@ -21,18 +21,6 @@ export const EventTypeListItem = ({ const duration = getEventDuration(item); const isLast = index === filteredEventTypes.length - 1; - const formatDuration = (minutes: number | undefined) => { - if (!minutes || minutes <= 0) { - return "0m"; - } - if (minutes < 60) { - return `${minutes}m`; - } - const hours = Math.floor(minutes / 60); - const remainingMinutes = minutes % 60; - return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`; - }; - return (