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
6 changes: 3 additions & 3 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"jsEngine" : "hermes",
"jsEngine": "hermes",
"scheme": "routine-app",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
Expand Down Expand Up @@ -53,9 +53,9 @@
"extra": {
"router": {},
"eas": {
"projectId": "57ef7f6c-5fc1-457a-96af-95ecc3e1ccd1"
"projectId": "4c7b89c2-7073-4c7d-b355-94ae9b09c3ce"
}
},
"owner": "rojyqaxyl"
"owner": "walterwhite919"
}
Comment on lines 53 to 60
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

Changing extra.eas.projectId and owner will repoint EAS builds/updates to a different Expo project/account. This isn’t mentioned in the PR description and is likely to break CI/CD or OTA updates for existing users. If this wasn’t intentional, revert these fields; if it was, please document why and ensure eas.json/deployment docs are updated accordingly.

Copilot uses AI. Check for mistakes.
}
65 changes: 15 additions & 50 deletions app/(tabs)/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,30 @@ import {
TouchableOpacity,
} from "react-native";
import { authClient } from "@/lib/auth-client";
import { useState, useEffect } from "react";
import { WeekDay } from "@/lib/types/routine";
import { useState, useMemo } from "react";
import { useRoutine } from "@/lib/api/routine";
import Header from "@/components/header";
import { RoutineCard } from "@/components/routine-card";
import { DAYS, getTodayIndex, getWeekDates } from "@/lib/utils/routine";
import { RoutineContent } from "@/components/routine-content";

export default function Home() {
const { isRefetching, isPending } = authClient.useSession();
const { isPending } = authClient.useSession();
const [refreshing, setRefreshing] = useState(false);
const [activeDayIndex, setActiveDayIndex] = useState(() => {
const today = new Date().getDay();
return today === 6 ? 0 : today > 5 ? 0 : today;
});
const [todayRoutine, setTodayRoutine] = useState<WeekDay | undefined>(
undefined
);
const [activeDayIndex, setActiveDayIndex] = useState(getTodayIndex);

const { data: routineData, isLoading, error, refetch } = useRoutine();

useEffect(() => {
if (routineData?.week) {
const routineForToday = routineData.week[activeDayIndex];
setTodayRoutine(routineForToday);
}
}, [activeDayIndex, routineData]);
const todayString = new Date().toDateString();
const weekDates = useMemo(() => getWeekDates(), [todayString]);
const todayRoutine = routineData?.week[activeDayIndex];

const onRefresh = async () => {
setRefreshing(true);
await refetch();
setRefreshing(false);
};

if (isRefetching || isPending) {
if (isPending) {
return (
<Container>
<View className="flex-1 justify-center items-center">
Expand All @@ -51,18 +42,6 @@ export default function Home() {
);
}

const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri"];

const today = new Date();
const dayOfWeek = today.getDay();
const sunday = new Date(today);
sunday.setDate(today.getDate() - dayOfWeek);
const weekDates = days.map((_, index) => {
const date = new Date(sunday);
date.setDate(sunday.getDate() + index);
return date.getDate();
});

return (
<Container>
<ScrollView
Expand All @@ -75,7 +54,7 @@ export default function Home() {
<Header />

<View className="p-2 bg-secondary/50 flex w-full flex-row gap-4 border border-border rounded-xl">
{days.map((day, index) => {
{DAYS.map((day, index) => {
const isActiveDay = index === activeDayIndex;
return (
<TouchableOpacity
Expand Down Expand Up @@ -129,25 +108,11 @@ export default function Home() {
</Text>
</View>
)}
{!isLoading &&
(routineData &&
routineData.week.every((day: WeekDay) => day.slots.length === 0) ? (
<View className="px-4 py-20">
<Text className="text-center text-muted-foreground text-lg">
No classes scheduled this week
</Text>
</View>
) : todayRoutine?.slots.length ? (
todayRoutine.slots.map((slot, idx) => (
<RoutineCard key={idx} slot={slot} />
))
) : routineData ? (
<View className="px-4 py-20">
<Text className="text-center text-muted-foreground text-lg">
No classes scheduled for this day
</Text>
</View>
) : null)}
<RoutineContent
isLoading={isLoading}
routineData={routineData}
todayRoutine={todayRoutine}
/>
</View>
</ScrollView>
</Container>
Expand Down
72 changes: 1 addition & 71 deletions app/(tabs)/setting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import {
ScrollView,
Text,
View,
TouchableOpacity,
Switch,
Alert,
} from "react-native";
import { authClient } from "@/lib/auth-client";
import { useColorScheme } from "@/lib/use-color-scheme";
import Ionicons from "@expo/vector-icons/Ionicons";
import { useProfile } from "@/lib/api/profile";
import { useRouter } from "expo-router";
import { SettingItem } from "@/components/setting-item";

export default function Setting() {
const router = useRouter();
Expand Down Expand Up @@ -39,75 +38,6 @@ export default function Setting() {
]);
};

const SettingItem = ({
icon,
label,
value,
onPress,
showChevron = true,
isDestructive = false,
rightElement,
isLast = false,
}: {
icon: keyof typeof Ionicons.glyphMap;
label: string;
value?: string;
onPress?: () => void;
showChevron?: boolean;
isDestructive?: boolean;
rightElement?: React.ReactNode;
isLast?: boolean;
}) => (
<View>
<TouchableOpacity
onPress={onPress}
disabled={!onPress}
className={`flex-row items-center px-5 py-4 gap-4 ${
onPress ? "active:bg-accent/50" : ""
}`}
>
<View
className={`w-9 h-9 rounded-full items-center justify-center ${
isDestructive
? "bg-destructive/15"
: isDark
? "bg-primary/20"
: "bg-primary/10"
}`}
>
<Ionicons
name={icon}
size={20}
color={isDestructive ? "#ef4444" : isDark ? "#60a5fa" : "#2563eb"}
/>
</View>
<View className="flex-1">
<Text
className={`text-base font-semibold ${
isDestructive ? "text-destructive" : "text-foreground"
}`}
>
{label}
</Text>
</View>
{value && (
<Text className="text-muted-foreground text-sm mr-3 font-medium">
{value}
</Text>
)}
{rightElement}
{showChevron && !rightElement && (
<Ionicons
name="chevron-forward"
size={18}
color={isDark ? "#9ca3af" : "#6b7280"}
/>
)}
</TouchableOpacity>
{!isLast && <View className="mx-5 h-[0.5px] bg-border" />}
</View>
);

return (
<Container>
<ScrollView className="flex-1" showsVerticalScrollIndicator={false}>
Expand Down
10 changes: 9 additions & 1 deletion app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ import { SafeAreaView } from "react-native-safe-area-context";
import { GroupsProvider } from "@/context/groupContext";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5,
retry: 2,
refetchOnWindowFocus: false,
},
},
});

const LIGHT_THEME: Theme = {
...DefaultTheme,
Expand Down
10 changes: 6 additions & 4 deletions components/profile-display.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { TouchableOpacity, Text, View } from "react-native";
import { TouchableOpacity, Text, View, ActivityIndicator } from "react-native";
import Ionicons from "@expo/vector-icons/Ionicons";
import {ProfileDisplayProps} from "@/lib/types/profile"
import { ProfileDisplayProps } from "@/lib/types/profile";
import { useProfile } from "@/lib/api/profile";

export function ProfileDisplay({ onEditPress }: ProfileDisplayProps) {
const { data: profile } = useProfile();
const { data: profile, isLoading, isError } = useProfile();

if (!profile) {
if (isLoading) return <ActivityIndicator />;

if (isError || !profile) {
return null;
}
Comment on lines +9 to 13
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

The PR description mentions adding an error state for the profile display, but on isError this component currently returns null (same as when profile is missing). Consider rendering an explicit fallback (even a small inline message) so errors aren’t silently hidden and the loading/error states are distinguishable.

Copilot uses AI. Check for mistakes.

Expand Down
44 changes: 44 additions & 0 deletions components/routine-content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { RoutineCard } from "@/components/routine-card";
import { Text, View } from "react-native";
import type { RoutineContentProps } from "@/lib/types/routine";

export function RoutineContent({
isLoading,
routineData,
todayRoutine,
}: RoutineContentProps) {
if (isLoading || !routineData) return null;

const isWeekEmpty = routineData.week.every((day) => day.slots.length === 0);

if (isWeekEmpty) {
return (
<View className="px-4 py-20">
<Text className="text-center text-muted-foreground text-lg">
No classes scheduled this week
</Text>
</View>
);
}

if (!todayRoutine?.slots.length) {
return (
<View className="px-4 py-20">
<Text className="text-center text-muted-foreground text-lg">
No classes scheduled for this day
</Text>
</View>
);
}

return (
<>
{todayRoutine.slots.map((slot) => (
<RoutineCard
key={`${slot.moduleCode}-${slot.startTime}-${slot.classType}`}
slot={slot}
/>
))}
</>
);
}
72 changes: 72 additions & 0 deletions components/setting-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { View, Text, TouchableOpacity } from "react-native";
import Ionicons from "@expo/vector-icons/Ionicons";
import { useColorScheme } from "@/lib/use-color-scheme";
import type { SettingItemProps } from "@/lib/types/setting";

export function SettingItem({
icon,
label,
value,
onPress,
showChevron = true,
isDestructive = false,
rightElement,
isLast = false,
isDark,
}: SettingItemProps) {
Comment on lines +14 to +16
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

isDark defaults to false, so if callers don’t explicitly pass it (e.g., the Settings screen after extracting this component), the icon/chevron colors will render with light-theme styling even in dark mode. Consider deriving dark mode inside the component (via useColorScheme) or making isDark required so incorrect styling can’t silently happen.

Copilot uses AI. Check for mistakes.
const { isDarkColorScheme } = useColorScheme();
const effectiveDark = isDark ?? isDarkColorScheme;

return (
<View>
<TouchableOpacity
onPress={onPress}
disabled={!onPress}
className={`flex-row items-center px-5 py-4 gap-4 ${
onPress ? "active:bg-accent/50" : ""
}`}
>
<View
className={`w-9 h-9 rounded-full items-center justify-center ${
isDestructive
? "bg-destructive/15"
: effectiveDark
? "bg-primary/20"
: "bg-primary/10"
}`}
>
<Ionicons
name={icon}
size={20}
color={
isDestructive ? "#ef4444" : effectiveDark ? "#60a5fa" : "#2563eb"
}
/>
</View>
<View className="flex-1">
<Text
className={`text-base font-semibold ${
isDestructive ? "text-destructive" : "text-foreground"
}`}
>
{label}
</Text>
</View>
{value && (
<Text className="text-muted-foreground text-sm mr-3 font-medium">
{value}
</Text>
)}
{rightElement}
{showChevron && !rightElement && (
<Ionicons
name="chevron-forward"
size={18}
color={effectiveDark ? "#9ca3af" : "#6b7280"}
/>
)}
</TouchableOpacity>
{!isLast && <View className="mx-5 h-[0.5px] bg-border" />}
</View>
);
}
4 changes: 2 additions & 2 deletions components/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ export default function SignIn() {

if (result.error) {
Toast.error(
result.error.message
result.error.message?.includes("verify")
? "Check Email to Verify First"
: "Invalid credentials"
: result.error.message || "Invalid credentials"
);
console.log("Error while logging in: ", result.error.message);
console.log(result.error);
Expand Down
Loading