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
48 changes: 24 additions & 24 deletions src/components/FocusTimer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import { useAuth } from '@/contexts/useAuth';
import { supabase } from '@/integrations/supabase/client';
import { Play, Square, Coffee, Clock } from 'lucide-react';
Expand Down Expand Up @@ -41,22 +41,21 @@ export default function FocusTimer() {
fetchProfileState();
}, [user]);

// Timer logic
useEffect(() => {
let interval: NodeJS.Timeout;
const updateProfile = useCallback(async (inFocus: boolean, focusTime?: number) => {
if (!user) return;

if (isActive && timeLeft > 0) {
interval = setInterval(() => {
setTimeLeft((prev) => prev - 1);
}, 1000);
} else if (isActive && timeLeft === 0) {
handleTimerComplete();
const updates: any = { is_in_focus_mode: inFocus };
if (focusTime !== undefined) {
updates.focus_time_this_week = focusTime;
}

return () => clearInterval(interval);
}, [isActive, timeLeft]);
await supabase
.from('profiles')
.update(updates)
.eq('id', user.id);
}, [user]);

const handleTimerComplete = async () => {
const handleTimerComplete = useCallback(async () => {
if (!isBreak) {
// Completed a work session
toast({
Expand All @@ -83,21 +82,22 @@ export default function FocusTimer() {
setIsActive(false);
setTimeLeft(workDuration * 60);
}
};
}, [isBreak, toast, workDuration, breakDuration, user, focusTimeThisWeek, updateProfile]);

const updateProfile = async (inFocus: boolean, focusTime?: number) => {
if (!user) return;
// Timer logic
useEffect(() => {
let interval: NodeJS.Timeout;

const updates: any = { is_in_focus_mode: inFocus };
if (focusTime !== undefined) {
updates.focus_time_this_week = focusTime;
if (isActive && timeLeft > 0) {
interval = setInterval(() => {
setTimeLeft((prev) => prev - 1);
}, 1000);
} else if (isActive && timeLeft === 0) {
handleTimerComplete();
}

await supabase
.from('profiles')
.update(updates)
.eq('id', user.id);
};
return () => clearInterval(interval);
}, [isActive, timeLeft, handleTimerComplete]);

const toggleTimer = async () => {
const newIsActive = !isActive;
Expand Down
60 changes: 30 additions & 30 deletions src/components/GroupPomodoro.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { Play, Square, Coffee, Clock } from 'lucide-react';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -61,32 +61,7 @@ export default function GroupPomodoro({ roomId }: GroupPomodoroProps) {
};
}, [roomId]);

// Countdown logic
useEffect(() => {
let interval: NodeJS.Timeout;

if (timerState !== 'idle' && endTime) {
interval = setInterval(() => {
const now = new Date();
const diff = Math.max(0, Math.floor((endTime.getTime() - now.getTime()) / 1000));

setTimeLeft(diff);

// Timer completed!
if (diff === 0) {
handleTimerComplete();
}
}, 1000);
} else {
setTimeLeft(timerState === 'break' ? breakDuration * 60 : workDuration * 60);
}

return () => {
if (interval) clearInterval(interval);
};
}, [timerState, endTime, workDuration, breakDuration]);

const handleTimerComplete = async () => {
const handleTimerComplete = useCallback(async () => {
if (timerState === 'work') {
toast({
title: "Group Focus Session Complete! 🎉",
Expand All @@ -100,9 +75,9 @@ export default function GroupPomodoro({ roomId }: GroupPomodoroProps) {
});
await setGroupTimer('idle', 0);
}
};
}, [timerState, toast, breakDuration, setGroupTimer]);

const setGroupTimer = async (newState: 'idle' | 'work' | 'break', durationMinutes: number) => {
const setGroupTimer = useCallback(async (newState: 'idle' | 'work' | 'break', durationMinutes: number) => {
let newEndTime = null;

if (newState !== 'idle') {
Expand All @@ -119,7 +94,32 @@ export default function GroupPomodoro({ roomId }: GroupPomodoroProps) {
timer_break_duration: breakDuration
})
.eq('id', roomId);
};
}, [roomId, workDuration, breakDuration]);

// Countdown logic
useEffect(() => {
let interval: NodeJS.Timeout;

if (timerState !== 'idle' && endTime) {
interval = setInterval(() => {
const now = new Date();
const diff = Math.max(0, Math.floor((endTime.getTime() - now.getTime()) / 1000));

setTimeLeft(diff);

// Timer completed!
if (diff === 0) {
handleTimerComplete();
}
}, 1000);
} else {
setTimeLeft(timerState === 'break' ? breakDuration * 60 : workDuration * 60);
}

return () => {
if (interval) clearInterval(interval);
};
}, [timerState, endTime, workDuration, breakDuration, handleTimerComplete]);

const formatTime = (seconds: number) => {
const m = Math.floor(seconds / 60);
Expand Down
61 changes: 30 additions & 31 deletions src/components/Room.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef, useCallback } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { supabase } from '@/integrations/supabase/client';
import { useAuth } from '@/contexts/useAuth';
Expand Down Expand Up @@ -30,6 +29,32 @@ export default function Room() {
const [showInviteUI, setShowInviteUI] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);

const fetchRoomDetails = useCallback(async () => {
const { data, error } = await supabase.from('study_rooms' as any).select('*').eq('id', id).single();
if (error) {
console.error("Error fetching room:", error);
if (error.code === 'PGRST116') {
alert("Room not found or you don't have access.");
navigate('/rooms');
}
}
if (data) setRoom(data);
}, [id, navigate]);

const fetchMessages = useCallback(async () => {
const { data, error } = await supabase
.from('study_room_messages' as any)
.select('*, profiles(name, avatar_url)')
.eq('room_id', id)
.order('created_at', { ascending: true });

if (error) {
console.error("Database fetch error:", error.message, error.details);
} else if (data) {
setMessages(data);
}
}, [id]);

useEffect(() => {
if (!id || !user) return;

Expand Down Expand Up @@ -57,9 +82,9 @@ export default function Room() {

setParticipants(onlineUsers);

setActivities([
setActivities((prev) => [
`${onlineUsers.length} participant(s) online`,
...activities,
...prev,
]);
})
.on('postgres_changes', {
Expand Down Expand Up @@ -95,38 +120,12 @@ export default function Room() {

if (roomChannel) supabase.removeChannel(roomChannel);
};
}, [id, user]);
}, [id, user, fetchMessages, fetchRoomDetails]);

useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);

const fetchRoomDetails = async () => {
const { data, error } = await supabase.from('study_rooms' as any).select('*').eq('id', id).single();
if (error) {
console.error("Error fetching room:", error);
if (error.code === 'PGRST116') {
alert("Room not found or you don't have access.");
navigate('/rooms');
}
}
if (data) setRoom(data);
};

const fetchMessages = async () => {
const { data, error } = await supabase
.from('study_room_messages' as any)
.select('*, profiles(name, avatar_url)')
.eq('room_id', id)
.order('created_at', { ascending: true });

if (error) {
console.error("Database fetch error:", error.message, error.details);
} else if (data) {
setMessages(data);
}
};

const handleSendMessage = async (e: React.FormEvent) => {
e.preventDefault();
if (!newMessage.trim() || !user) return;
Expand Down
9 changes: 4 additions & 5 deletions src/components/Whiteboard/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState, useCallback } from "react";
import { supabase } from "@/integrations/supabase/client";
import { useAuth } from "@/contexts/useAuth";
import {
Expand Down Expand Up @@ -105,7 +104,7 @@ export default function Canvas({ roomId }: Props) {
}
};

const replayCanvas = () => {
const replayCanvas = useCallback(() => {
const ctx = getContext();

if (!ctx) return;
Expand All @@ -115,7 +114,7 @@ export default function Canvas({ roomId }: Props) {
for (const event of strokesRef.current) {
drawEvent(ctx, event);
}
};
}, []);

const persistEvent = async (event: WhiteboardEvent) => {
await supabase.from("whiteboard_events" as any).insert({
Expand Down Expand Up @@ -209,7 +208,7 @@ export default function Canvas({ roomId }: Props) {
return () => {
supabase.removeChannel(channel);
};
}, [roomId]);
}, [roomId, user?.id, replayCanvas]);

const getCoordinates = (
e: React.MouseEvent<HTMLCanvasElement>
Expand Down
61 changes: 30 additions & 31 deletions src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Suspense, lazy, useEffect, useState } from "react";
import { Suspense, lazy, useEffect, useState, useCallback } from "react";
import { motion } from "framer-motion";
import { useNavigate } from "react-router-dom";
import RecommendationPanel from "@/components/recommendations/RecommendationPanel";
Expand Down Expand Up @@ -71,35 +70,8 @@ const Dashboard = () => {
user?.email?.split("@")[0] ||
"Learner";

// Fetch Profile
useEffect(() => {
if (!user) return;

const fetchProfile = async () => {
const { data, error } = await supabase
.from("profiles")
.select("*")
.eq("id", user.id)
.single<Profile>(); // 👈 tell TS this is a single Profile row

if (error) {
console.error(error);
return;
}

if (data) {
setProfile(data); // TS now knows `data` is Profile
fetchRecommendedPeers(data); // safe to pass
}
};



fetchProfile();
}, [user]);

// Recommended Peers
const fetchRecommendedPeers = async (myProfile: Profile) => {
const fetchRecommendedPeers = useCallback(async (myProfile: Profile) => {
if (!user?.id) return;

try {
Expand Down Expand Up @@ -136,7 +108,34 @@ const Dashboard = () => {
} catch (err) {
console.error("Failed to fetch recommended peers:", err);
}
};
}, [user?.id]);

// Fetch Profile
useEffect(() => {
if (!user) return;

const fetchProfile = async () => {
const { data, error } = await supabase
.from("profiles")
.select("*")
.eq("id", user.id)
.single<Profile>(); // 👈 tell TS this is a single Profile row

if (error) {
console.error(error);
return;
}

if (data) {
setProfile(data); // TS now knows `data` is Profile
fetchRecommendedPeers(data); // safe to pass
}
};



fetchProfile();
}, [user, fetchRecommendedPeers]);

// Sessions
useEffect(() => {
Expand Down
Loading
Loading