Files
Dhairyashil ShindeandGitHub fa53f54815 feat(companion): add configurable landing page (#27267)
* feat(companion): add configurable landing page feature

- Add useUserPreferences hook for persistent storage of landing page preference
- Add LandingPagePicker component for both iOS and Android/web platforms
- Update tabs index to redirect based on user preference
- Update bookings index to accept initial filter from URL params
- Add App Settings section in More screen with landing page selector
- Clear user preferences on logout for fresh state

* fix(companion): remove try-finally for React Compiler compatibility

* fix(companion): use router.replace for dynamic landing page redirect

Replace Redirect component with router.replace() to fix TypeScript
strict typing issue with expo-router's Href type for dynamic routes.

* fix(companion): use literal route strings for TypeScript strict typing

Use switch statement with literal route strings instead of dynamic
string variable to satisfy expo-router's strict Href type checking.

* working fix

* better css
2026-02-13 11:28:42 -03:00

141 lines
5.0 KiB
TypeScript

import { isLiquidGlassAvailable } from "expo-glass-effect";
import { Image } from "expo-image";
import { Stack, useRouter } from "expo-router";
import { useState } from "react";
import { Alert, Platform, Pressable, useColorScheme } from "react-native";
import { AvailabilityListScreen } from "@/components/screens/AvailabilityListScreen";
import { useCreateSchedule, useUserProfile } from "@/hooks";
import { CalComAPIService } from "@/services/calcom";
import { showErrorAlert } from "@/utils/alerts";
import { getAvatarUrl } from "@/utils/getAvatarUrl";
export default function Availability() {
const router = useRouter();
const [searchQuery, setSearchQuery] = useState("");
const [showCreateModal, setShowCreateModal] = useState(false);
const { mutate: createScheduleMutation } = useCreateSchedule();
const { data: userProfile } = useUserProfile();
const colorScheme = useColorScheme();
const isDark = colorScheme === "dark";
const handleCreateNew = () => {
// Use native iOS Alert.prompt for a native look
Alert.prompt(
"Add a new schedule",
"Create a new availability schedule.",
[
{ text: "Cancel", style: "cancel" },
{
text: "Continue",
onPress: async (name?: string) => {
if (!name?.trim()) {
Alert.alert("Error", "Please enter a schedule name");
return;
}
// Get user's timezone (default to America/New_York if not available)
let userTimezone = "America/New_York";
try {
const userProfile = await CalComAPIService.getUserProfile();
if (userProfile.timeZone) {
userTimezone = userProfile.timeZone;
}
} catch {
console.log("Could not get user timezone, using default");
}
// Create schedule with Monday-Friday 9 AM - 5 PM default
createScheduleMutation(
{
name: name.trim(),
timeZone: userTimezone,
isDefault: false,
availability: [
{
days: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
startTime: "09:00",
endTime: "17:00",
},
],
},
{
onSuccess: (newSchedule) => {
// Navigate to edit the newly created schedule
router.push({
pathname: "/(tabs)/(availability)/availability-detail",
params: {
id: newSchedule.id.toString(),
},
});
},
onError: (error) => {
const message = error instanceof Error ? error.message : String(error);
console.error("Failed to create schedule", message);
if (__DEV__) {
const stack = error instanceof Error ? error.stack : undefined;
console.debug("[Availability] createSchedule failed", {
message,
stack,
});
}
showErrorAlert("Error", "Failed to create schedule. Please try again.");
},
}
);
},
},
],
"plain-text",
"",
"default"
);
};
return (
<>
<Stack.Header
style={{ backgroundColor: "transparent", shadowColor: "transparent" }}
blurEffect={isLiquidGlassAvailable() ? undefined : isDark ? "dark" : "light"}
hidden={Platform.OS === "android" || Platform.OS === "web"}
>
<Stack.Header.Title large>Availability</Stack.Header.Title>
<Stack.Header.Right>
{/* New Button - directly opens create dialog */}
<Stack.Header.Button onPress={handleCreateNew}>
<Stack.Header.Icon sf="plus" />
</Stack.Header.Button>
{/* Profile Button */}
{userProfile?.avatarUrl ? (
<Stack.Header.View>
<Pressable onPress={() => router.push("/profile-sheet")}>
<Image
source={{ uri: getAvatarUrl(userProfile.avatarUrl) }}
style={{ width: 32, height: 32, borderRadius: 16 }}
/>
</Pressable>
</Stack.Header.View>
) : (
<Stack.Header.Button onPress={() => router.push("/profile-sheet")}>
<Stack.Header.Icon sf="person.circle.fill" />
</Stack.Header.Button>
)}
</Stack.Header.Right>
<Stack.Header.SearchBar
placeholder="Search schedules"
onChangeText={(e) => setSearchQuery(e.nativeEvent.text)}
obscureBackground={false}
barTintColor={isDark ? "#000" : "#fff"}
/>
</Stack.Header>
<AvailabilityListScreen
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
showCreateModal={showCreateModal}
onShowCreateModalChange={setShowCreateModal}
/>
</>
);
}