Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default function AvailabilityDetail() {
marginRight: -8,
}}
>
<Ionicons name="ellipsis-horizontal-circle" size={24} color="#007AFF" />
<Ionicons name="ellipsis-horizontal-circle" size={24} color="#000000" />
</TouchableOpacity>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
Expand Down
4 changes: 2 additions & 2 deletions companion/app/(tabs)/(bookings)/index.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function Bookings() {
label: currentFilterOption?.label || "Filter",
labelStyle: {
fontWeight: "600",
color: "#007AFF",
color: "#000000",
},
menu: {
title: "Filter by Status",
Expand Down Expand Up @@ -123,7 +123,7 @@ export default function Bookings() {
},
labelStyle: {
fontWeight: "600",
color: "#007AFF",
color: "#000000",
},
menu: {
title: menuItems.length > 0 ? "Filter by Event Type" : "No Event Types",
Expand Down
9 changes: 7 additions & 2 deletions companion/app/(tabs)/(bookings)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ export default function Bookings() {
<DropdownMenuTrigger asChild>
<AppPressable
className="flex-row items-center rounded-lg border border-gray-200 bg-white"
style={{ paddingHorizontal: 8, paddingVertical: 6 }}
style={{
paddingHorizontal: 8,
paddingVertical: 6,
flexDirection: "row",
alignItems: "center",
}}
>
<Ionicons name="options-outline" size={14} color="#333" />
<Text
className={`text-sm ${selectedEventTypeId !== null ? "text-[#007AFF] font-semibold" : "text-[#333]"}`}
className={`text-sm ${selectedEventTypeId !== null ? "text-[#000000] font-semibold" : "text-[#333]"}`}
style={{ marginLeft: 4 }}
numberOfLines={1}
>
Expand Down
45 changes: 21 additions & 24 deletions companion/app/(tabs)/(event-types)/event-type-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export default function EventTypeDetail() {
const [conferencingOptions, setConferencingOptions] = useState<ConferencingOption[]>([]);
const [conferencingLoading, setConferencingLoading] = useState(false);
const [eventTypeData, setEventTypeData] = useState<EventType | null>(null);
const [bookingUrl, setBookingUrl] = useState<string>("");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bookingUrl state is never populated from API data

High Severity

The bookingUrl state is initialized to an empty string but setBookingUrl is never called. The applyEventTypeData function loads many fields from the API response but never sets bookingUrl from eventType.bookingUrl. This causes handlePreview and handleCopyLink to always fail with "Booking URL not available" since the empty string is falsy.

Additional Locations (1)

Fix in Cursor Fix in Web

const [saving, setSaving] = useState(false);
const [beforeEventBuffer, setBeforeEventBuffer] = useState("No buffer time");
const [afterEventBuffer, setAfterEventBuffer] = useState("No buffer time");
Expand Down Expand Up @@ -945,29 +946,19 @@ export default function EventTypeDetail() {
};

const handlePreview = async () => {
const eventTypeSlug = eventSlug || "preview";
let link: string;
try {
link = await CalComAPIService.buildEventTypeLink(eventTypeSlug);
} catch (error) {
safeLogError("Failed to generate preview link:", error);
showErrorAlert("Error", "Failed to generate preview link. Please try again.");
if (!bookingUrl) {
showErrorAlert("Error", "Booking URL not available. Please save the event type first.");
return;
}
await openInAppBrowser(link, "event type preview");
await openInAppBrowser(bookingUrl, "event type preview");
};

const handleCopyLink = async () => {
const eventTypeSlug = eventSlug || "event-link";
let link: string;
try {
link = await CalComAPIService.buildEventTypeLink(eventTypeSlug);
} catch (error) {
safeLogError("Failed to copy link:", error);
showErrorAlert("Error", "Failed to copy link. Please try again.");
if (!bookingUrl) {
showErrorAlert("Error", "Booking URL not available. Please save the event type first.");
return;
}
await Clipboard.setStringAsync(link);
await Clipboard.setStringAsync(bookingUrl);
showSuccessAlert("Success", "Link copied!");
};

Expand Down Expand Up @@ -1279,7 +1270,10 @@ export default function EventTypeDetail() {
{/* Tab Navigation Dropdown Menu */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<AppPressable className="flex-row items-center gap-1 px-2 py-2">
<AppPressable
className="flex-row items-center gap-1 px-2 py-2"
style={{ flexDirection: "row", alignItems: "center" }}
>
<Text className="text-[16px] font-semibold text-[#000000]" numberOfLines={1}>
{tabs.find((tab) => tab.id === activeTab)?.label ?? "Basics"}
</Text>
Expand Down Expand Up @@ -1433,6 +1427,7 @@ export default function EventTypeDetail() {
onUpdateLocation={handleUpdateLocation}
locationOptions={getLocationOptionsForDropdown()}
conferencingLoading={conferencingLoading}
bookingUrl={bookingUrl}
/>
) : null}

Expand Down Expand Up @@ -2342,15 +2337,17 @@ export default function EventTypeDetail() {
<View className="bg-white pl-4">
<View
className="flex-row items-center justify-between pr-4"
style={{ height: 44 }}
style={{ height: 44, flexDirection: "row", alignItems: "center" }}
>
<Text className="text-[17px] text-black">Hidden</Text>
<Switch
value={isHidden}
onValueChange={setIsHidden}
trackColor={{ false: "#E5E5EA", true: "#000000" }}
thumbColor="#FFFFFF"
/>
<View style={{ justifyContent: "center", height: "100%" }}>
<Switch
value={isHidden}
onValueChange={setIsHidden}
trackColor={{ false: "#E5E5EA", true: "#000000" }}
thumbColor="#FFFFFF"
/>
</View>
</View>
</View>
</View>
Expand Down
28 changes: 18 additions & 10 deletions companion/app/(tabs)/(event-types)/index.ios.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, ContextMenu, Host, HStack, Image as SwiftUIImage } from "@expo/ui/swift-ui";
import { Button, Host, Image as SwiftUIImage } from "@expo/ui/swift-ui";
import * as Haptics from "expo-haptics";
import { buttonStyle, controlSize, fixedSize, frame, padding } from "@expo/ui/swift-ui/modifiers";
import { buttonStyle, controlSize, frame, padding } from "@expo/ui/swift-ui/modifiers";
import { Ionicons } from "@expo/vector-icons";
import * as Clipboard from "expo-clipboard";
import { isLiquidGlassAvailable } from "expo-glass-effect";
Expand Down Expand Up @@ -28,7 +28,7 @@ import {
useUserProfile,
} from "@/hooks";
import { useEventTypeFilter } from "@/hooks/useEventTypeFilter";
import { CalComAPIService, type EventType } from "@/services/calcom";
import type { EventType } from "@/services/calcom";
import { showErrorAlert, showSuccessAlert } from "@/utils/alerts";
import { openInAppBrowser } from "@/utils/browser";
import { getAvatarUrl } from "@/utils/getAvatarUrl";
Expand Down Expand Up @@ -111,21 +111,27 @@ export default function EventTypesIOS() {
};

const handleCopyLink = async (eventType: EventType) => {
if (!eventType.bookingUrl) {
showErrorAlert("Error", "Booking URL not available for this event type.");
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mobile app handlers lack fallback URL unlike extension

Medium Severity

The mobile app's handleCopyLink, _handleShare, and handlePreview functions show an error and return early if eventType.bookingUrl is missing, with no fallback URL construction. The extension code uses eventType.bookingUrl || fallbackUrl pattern. Since bookingUrl is optional in the type definition, event types without this field will fail in the mobile app but work in the extension. Previously, CalComAPIService.buildEventTypeLink() always generated a URL.

Additional Locations (1)

Fix in Cursor Fix in Web

try {
const link = await CalComAPIService.buildEventTypeLink(eventType.slug);
await Clipboard.setStringAsync(link);
await Clipboard.setStringAsync(eventType.bookingUrl);
showSuccessAlert("Link Copied", "Event type link copied!");
} catch {
showErrorAlert("Error", "Failed to copy link. Please try again.");
}
};

const _handleShare = async (eventType: EventType) => {
if (!eventType.bookingUrl) {
showErrorAlert("Error", "Booking URL not available for this event type.");
return;
}
try {
const link = await CalComAPIService.buildEventTypeLink(eventType.slug);
await Share.share({
message: `Book a meeting: ${eventType.title}`,
url: link,
url: eventType.bookingUrl,
});
} catch {
showErrorAlert("Error", "Failed to share link. Please try again.");
Expand Down Expand Up @@ -226,10 +232,12 @@ export default function EventTypesIOS() {
};

const handlePreview = async (eventType: EventType) => {
if (!eventType.bookingUrl) {
showErrorAlert("Error", "Booking URL not available for this event type.");
return;
}
try {
const link = await CalComAPIService.buildEventTypeLink(eventType.slug);
// For mobile, use in-app browser
await openInAppBrowser(link, "event type preview");
await openInAppBrowser(eventType.bookingUrl, "event type preview");
} catch {
console.error("Failed to open preview");
showErrorAlert("Error", "Failed to open preview. Please try again.");
Expand Down
30 changes: 19 additions & 11 deletions companion/app/(tabs)/(event-types)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
useEventTypes,
} from "@/hooks";
import { useEventTypeFilter } from "@/hooks/useEventTypeFilter";
import { CalComAPIService, type EventType } from "@/services/calcom";
import type { EventType } from "@/services/calcom";
import { showErrorAlert, showSuccessAlert } from "@/utils/alerts";
import { openInAppBrowser } from "@/utils/browser";
import { getEventDuration } from "@/utils/getEventDuration";
Expand Down Expand Up @@ -133,22 +133,27 @@ export default function EventTypes() {
};

const handleCopyLink = async (eventType: EventType) => {
if (!eventType.bookingUrl) {
showErrorAlert("Error", "Booking URL not available for this event type.");
return;
}
try {
const link = await CalComAPIService.buildEventTypeLink(eventType.slug);
await Clipboard.setStringAsync(link);

await Clipboard.setStringAsync(eventType.bookingUrl);
showSuccessAlert("Link Copied", "Event type link copied!");
} catch {
showErrorAlert("Error", "Failed to copy link. Please try again.");
}
};

const _handleShare = async (eventType: EventType) => {
if (!eventType.bookingUrl) {
showErrorAlert("Error", "Booking URL not available for this event type.");
return;
}
try {
const link = await CalComAPIService.buildEventTypeLink(eventType.slug);
await Share.share({
message: `Book a meeting: ${eventType.title}`,
url: link,
url: eventType.bookingUrl,
});
} catch {
showErrorAlert("Error", "Failed to share link. Please try again.");
Expand Down Expand Up @@ -280,14 +285,15 @@ export default function EventTypes() {
};

const handlePreview = async (eventType: EventType) => {
if (!eventType.bookingUrl) {
showErrorAlert("Error", "Booking URL not available for this event type.");
return;
}
try {
const link = await CalComAPIService.buildEventTypeLink(eventType.slug);
// Open in browser
if (Platform.OS === "web") {
window.open(link, "_blank");
window.open(eventType.bookingUrl, "_blank");
} else {
// For mobile, use in-app browser
await openInAppBrowser(link, "event type preview");
await openInAppBrowser(eventType.bookingUrl, "event type preview");
}
} catch {
console.error("Failed to open preview");
Expand Down Expand Up @@ -443,6 +449,7 @@ export default function EventTypes() {
/>
<TouchableOpacity
className="min-w-[60px] flex-row items-center justify-center gap-1 rounded-lg bg-black px-2.5 py-2"
style={{ flexDirection: "row", alignItems: "center", justifyContent: "center" }}
onPress={handleCreateNew}
>
<Ionicons name="add" size={18} color="#fff" />
Expand Down Expand Up @@ -507,6 +514,7 @@ export default function EventTypes() {
/>
<TouchableOpacity
className="min-w-[60px] flex-row items-center justify-center gap-1 rounded-lg bg-black px-2.5 py-2"
style={{ flexDirection: "row", alignItems: "center", justifyContent: "center" }}
onPress={handleCreateNew}
>
<Ionicons name="add" size={18} color="#fff" />
Expand Down
2 changes: 1 addition & 1 deletion companion/app/profile-sheet.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export default function ProfileSheet() {
}}
>
{/* Profile Header */}
<View className="mt-20 border-b border-gray-200 px-6">
<View className="mt-20 px-6">
{isLoading ? (
<View className="items-center py-8">
<ActivityIndicator size="large" color="#000" />
Expand Down
2 changes: 1 addition & 1 deletion companion/app/profile-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default function ProfileSheet() {
contentContainerStyle={{ paddingBottom: insets.bottom + 20 }}
>
{/* Profile Header */}
<View className="border-b border-gray-200 px-6 py-6">
<View className="px-6 py-6">
{isLoading ? (
<View className="items-center py-8">
<ActivityIndicator size="large" color="#000" />
Expand Down
Loading
Loading