Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5a39e31
feat: ✨ adds check for calendar permission provision
aizilerts Jan 10, 2026
0d9fce6
feat: ✨ added toggle for google calendar display and preloading when …
aizilerts Jan 10, 2026
cea9707
fix: 🐛 moved button
aizilerts Jan 10, 2026
d6be077
feat: ✨ added redirect when user hasn't given gcal perms in personal …
aizilerts Jan 11, 2026
a1c496d
feat: ✨ added params to get request to control forced oauth
aizilerts Jan 14, 2026
f045efc
fix: 🐛 fixed some nits, removed fetchedCalendar
aizilerts Jan 14, 2026
0c9c647
Merge remote-tracking branch 'origin/main' into achen-237-google-cale…
alexanderl19 Jan 18, 2026
c89e9e5
feat: ✨ temp always show google calendar
alexanderl19 Jan 18, 2026
59523d4
chore: 🔧 trying to fix auth part fiveeeeuh
aizilerts Jan 19, 2026
0269c09
Merge branch 'achen-237-google-calendar-permission-request' of https:…
aizilerts Jan 19, 2026
05b4392
chore: 🔧 trying to fix auth part 6
aizilerts Jan 19, 2026
de38786
chore: 🔧 trying to fix auth :(
aizilerts Jan 19, 2026
1ac3d1c
chore: 🔧 oh i wonder what im trying to do
aizilerts Jan 19, 2026
691caa5
chore: 🔧 im losing my mind
aizilerts Jan 19, 2026
8f1bb3a
chore: 🔧 testing
aizilerts Jan 19, 2026
0c26c95
chore: 🔧 testing
aizilerts Jan 19, 2026
577c480
chore: 🔧 testing
aizilerts Jan 19, 2026
0740076
Merge branch 'main' of https://github.com/icssc/ZotMeet into achen-23…
aizilerts Feb 18, 2026
46ecee9
Merge branch 'main' into achen-237-google-calendar-permission-request
KevinWu098 Feb 28, 2026
7ec77e2
fix: 🐛 cleaned up old changes
aizilerts Apr 1, 2026
f6310c2
chore: 🔧 remove console log
aizilerts Apr 1, 2026
185e9f9
Merge branch 'main' of https://github.com/icssc/ZotMeet into achen-23…
aizilerts Apr 5, 2026
4638924
chore: 🔧 removed unecessary param from group responses
aizilerts Apr 5, 2026
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
21 changes: 16 additions & 5 deletions src/app/auth/login/google/callback/route.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { OAuth2Tokens } from "arctic";
import { decodeIdToken } from "arctic";
import { and, eq } from "drizzle-orm";
import { eq } from "drizzle-orm";
import { cookies } from "next/headers";
import { db } from "@/db";
import { oauthAccounts, users } from "@/db/schema";
import { oauthAccounts } from "@/db/schema";
import { setSessionTokenCookie } from "@/lib/auth/cookies";
import { oauth } from "@/lib/auth/oauth";
import { createSession, generateSessionToken } from "@/lib/auth/session";
Expand All @@ -22,9 +22,8 @@ export async function GET(request: Request): Promise<Response> {
const codeVerifier = cookieStore.get("oauth_code_verifier")?.value ?? null;
const redirectUrl = cookieStore.get("auth_redirect_url")?.value ?? "/";

cookieStore.delete("auth_redirect_url");
cookieStore.delete("oauth_state");
cookieStore.delete("oauth_code_verifier");
cookieStore.delete("session");
cookieStore.delete({ name: "session", path: "/" });

if (
code === null ||
Expand Down Expand Up @@ -127,7 +126,16 @@ export async function GET(request: Request): Promise<Response> {
oauthRefreshToken: googleRefreshToken,
oauthAccessTokenExpiresAt: googleTokenExpiry,
});

cookieStore.delete("session");
cookieStore.delete({ name: "session", path: "/" });
Comment on lines +130 to +131
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

issue: why are we deleting the same cookies twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Originally I had both of these just to make sure the cookies were being deleted, as I was having issues with that. I looked at the session token cookie function again and I believe we only need the second one, so I deleted the first


await setSessionTokenCookie(sessionToken, session.expiresAt);

cookieStore.delete("oauth_state");
cookieStore.delete("oauth_code_verifier");
cookieStore.delete("auth_redirect_url");
Comment on lines +135 to +137
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

question: why are we only deleting these inside of this code path, instead of always?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, the cookies should also be deleted for the new user branch. I added the same deletions into that branch


const userRecord = await getUserById(existingUser.id);

if (!userRecord) {
Expand All @@ -145,6 +153,9 @@ export async function GET(request: Request): Promise<Response> {
oauthRefreshToken: googleRefreshToken,
oauthAccessTokenExpiresAt: googleTokenExpiry,
});

// cookieStore.delete("session");
// cookieStore.delete({ name: "session", path: "/" });
Comment thread
aizilerts marked this conversation as resolved.
await setSessionTokenCookie(sessionToken, session.expiresAt);

memberId = user.memberId;
Expand Down
10 changes: 9 additions & 1 deletion src/app/auth/login/google/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import {
generateState,
} from "arctic";
import { cookies, headers } from "next/headers";
import { deleteSessionTokenCookie } from "@/lib/auth/cookies";
import { oauth } from "@/lib/auth/oauth";

export async function GET(): Promise<Response> {
export async function GET(request: Request): Promise<Response> {
const { searchParams } = new URL(request.url);
const promptValue = searchParams.get("prompt") || "consent";
const state = generateState();
const codeVerifier = generateCodeVerifier();

await deleteSessionTokenCookie();

const url = new URL(
oauth.createAuthorizationURLWithPKCE(
"https://auth.icssc.club/authorize",
Expand All @@ -23,8 +29,10 @@ export async function GET(): Promise<Response> {
],
),
);
url.searchParams.set("prompt", promptValue);

const cookieStore = await cookies();

cookieStore.set("oauth_state", state, {
path: "/",
httpOnly: true,
Expand Down
61 changes: 40 additions & 21 deletions src/components/availability/availability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { fetchGoogleCalendarEvents } from "@actions/availability/google/calendar/action";
import { useDrag } from "@use-gesture/react";
import { formatInTimeZone } from "date-fns-tz";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useShallow } from "zustand/shallow";
import {
Expand Down Expand Up @@ -133,10 +134,15 @@ export function Availability({
user: UserProfile | null;
scheduledBlocks: SelectScheduledMeeting[];
}) {
const router = useRouter();
const availabilityView = useAvailabilityViewStore(
(state) => state.availabilityView,
);

const overlayGoogleCalendar = useAvailabilityViewStore(
(state) => state.overlayGoogleCalendar,
);

const selectionIsLocked = useGroupSelectionStore(
(state) => state.selectionIsLocked,
);
Expand Down Expand Up @@ -218,6 +224,7 @@ export function Availability({
const [googleCalendarEvents, setGoogleCalendarEvents] = useState<
GoogleCalendarEvent[]
>([]);
// const [hasFetchedCalendar, setHasFetchedCalendar] = useState(false);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: comment


const [availabilityDates, setAvailabilityDates] = useState(() =>
deriveInitialAvailability({
Expand Down Expand Up @@ -280,27 +287,39 @@ export function Availability({
}, [confirmSave]);

useEffect(() => {
if (availabilityDates.length > 0 && anchorNormalizedDate.length > 0) {
const firstDateISO = anchorNormalizedDate[0].toISOString();

const lastDateObj = new Date(
anchorNormalizedDate[anchorNormalizedDate.length - 1],
);
lastDateObj.setHours(23, 59, 59, 999);
const lastDateISO = lastDateObj.toISOString();

fetchGoogleCalendarEvents(firstDateISO, lastDateISO)
.then((events) => {
setGoogleCalendarEvents(events);
})
.catch((error) => {
console.error("Error fetching Google Calendar events:", error);
setGoogleCalendarEvents([]);
});
} else {
setGoogleCalendarEvents([]);
if (!overlayGoogleCalendar) {
return;
}
}, [availabilityDates, anchorNormalizedDate]);

if (availabilityDates.length === 0 || anchorNormalizedDate.length === 0)
return;

const firstDateISO = anchorNormalizedDate[0].toISOString();
const lastDateObj = new Date(
anchorNormalizedDate[anchorNormalizedDate.length - 1],
);
lastDateObj.setHours(23, 59, 59, 999);

fetchGoogleCalendarEvents(firstDateISO, lastDateObj.toISOString()).then(
(result) => {
if (
result.status === "missing_scope" ||
result.status === "not_authenticated"
) {
router.push("/auth/login/google/?prompt=consent");

return;
}
setGoogleCalendarEvents(result.events);
},
);
}, [
overlayGoogleCalendar,
availabilityDates,
anchorNormalizedDate,
// hasFetchedCalendar,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit

router,
]);

const members = useMemo(() => {
const presentMemberIds = [
Expand Down Expand Up @@ -658,6 +677,7 @@ export function Availability({
currentPageAvailability={currentPageAvailability}
googleCalendarEvents={googleCalendarEvents}
meetingDates={meetingData.dates}
showGoogleCalendar={overlayGoogleCalendar}
/>
)}
</tr>
Expand Down Expand Up @@ -685,7 +705,6 @@ export function Availability({
availabilityDates={availabilityDates}
fromTime={fromTimeMinutes}
members={members}
timezone={userTimezone}
anchorNormalizedDate={anchorNormalizedDate}
currentPageAvailability={currentPageAvailability}
availabilityTimeBlocks={availabilityTimeBlocks}
Expand Down
6 changes: 3 additions & 3 deletions src/components/availability/group-availability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ export function GroupAvailability({
],
);

const handleMouseDown = useCallback(
const _handleMouseDown = useCallback(
({
zotDateIndex,
blockIndex,
Expand All @@ -328,7 +328,7 @@ export function GroupAvailability({
[isScheduling, setStartBlockSelection, setEndBlockSelection],
);

const handleMouseMove = useCallback(
const _handleMouseMove = useCallback(
({
zotDateIndex,
blockIndex,
Expand All @@ -343,7 +343,7 @@ export function GroupAvailability({
[isScheduling, startBlockSelection, setEndBlockSelection],
);

const handleMouseUp = useCallback(() => {
const _handleMouseUp = useCallback(() => {
if (
isScheduling &&
startBlockSelection &&
Expand Down
1 change: 0 additions & 1 deletion src/components/availability/group-responses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ interface GroupResponsesProps {
availabilityDates: ZotDate[];
members: Member[];
fromTime: number;
timezone: string;
anchorNormalizedDate: Date[];
currentPageAvailability: ZotDate[];
availabilityTimeBlocks: number[];
Expand Down
35 changes: 25 additions & 10 deletions src/components/availability/header/availability-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
saveScheduledTimeBlock,
} from "@actions/meeting/schedule/action";
import GoogleIcon from "@mui/icons-material/Google";
import { FormControlLabel, Switch } from "@mui/material";
import {
CalendarCheck,
CalendarPlus,
Expand All @@ -27,6 +28,7 @@ import type { UserProfile } from "@/lib/auth/user";
import { cn } from "@/lib/utils";
import type { ZotDate } from "@/lib/zotdate";
import { useAvailabilityViewStore } from "@/store/useAvailabilityViewStore";
import { useBestTimesToggleStore } from "@/store/useBestTimesToggleStore";
import { useScheduleSelectionStore } from "@/store/useScheduleSelectionStore";

interface AvailabilityHeaderProps {
Expand Down Expand Up @@ -64,15 +66,20 @@ export function AvailabilityHeader({
})),
);

const { overlayGoogleCalendar, setOverlayGoogleCalendar } =
useAvailabilityViewStore(
useShallow((state) => ({
overlayGoogleCalendar: state.overlayGoogleCalendar,
setOverlayGoogleCalendar: state.setOverlayGoogleCalendar,
})),
);

const handleCancel = () => {
onCancel();
setChangeableTimezone(true);
setAvailabilityView("group");
};

// const [isGuestDialogOpen, setIsGuestDialogOpen] = useState(false);
// const [guestName, setGuestName] = useState("");

const [_isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
Expand All @@ -83,8 +90,6 @@ export function AvailabilityHeader({

const handleSave = async () => {
if (!user) {
// setIsGuestDialogOpen(true);

return;
}
setChangeableTimezone(true);
Expand All @@ -100,11 +105,6 @@ export function AvailabilityHeader({
setHasAvailability(true);
setAvailabilityView("group");
onSave();

// Clear guest member name
if (!user) {
// setGuestName("");
}
} else {
console.error("Error saving availability:", response.body.error);
}
Expand Down Expand Up @@ -179,6 +179,10 @@ export function AvailabilityHeader({
}
};

const handleToggleCalendar = (event: React.ChangeEvent<HTMLInputElement>) => {
setOverlayGoogleCalendar(event.target.checked);
};

return (
<>
<div className="px-2 pt-8">
Expand Down Expand Up @@ -220,6 +224,17 @@ export function AvailabilityHeader({
<span className="hidden md:flex">Save</span>
<CircleCheckIcon />
</Button>
<FormControlLabel
className="ml-2"
control={
<Switch
checked={overlayGoogleCalendar}
onChange={handleToggleCalendar}
size="small"
/>
}
label="Google Calendar"
/>
</>
) : (
<>
Expand Down
8 changes: 7 additions & 1 deletion src/components/availability/personal-availability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface PersonalAvailabilityProps {
currentPageAvailability: ZotDate[];
googleCalendarEvents: GoogleCalendarEvent[];
meetingDates: string[];
showGoogleCalendar: boolean;
}

export function PersonalAvailability({
Expand All @@ -24,12 +25,17 @@ export function PersonalAvailability({
currentPageAvailability,
googleCalendarEvents,
meetingDates,
showGoogleCalendar,
}: PersonalAvailabilityProps) {
const [isStateUnsaved, setIsStateUnsaved] = useState(false);
const initialAvailabilityRef = useRef<string | null>(null);

const visibleGoogleCalendarEvents = showGoogleCalendar
? googleCalendarEvents
: [];

const { processedCellSegments } = useGoogleCalendar({
googleCalendarEvents,
googleCalendarEvents: visibleGoogleCalendarEvents,
currentPageAvailability,
availabilityTimeBlocks,
meetingDates,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function AvailabilityTableHeader({
}: AvailabilityTableHeaderProps) {
//extra day calculation for day spillover
//put in here to prevent infinite adding, recalculates everytime something changes
const [newBlocks, newAvailDates] = newZonedPageAvailAndDates(
const [newBlocks, _newAvailDates] = newZonedPageAvailAndDates(
currentPageAvailability,
null,
doesntNeedDay,
Expand Down
6 changes: 3 additions & 3 deletions src/components/creation/creation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function Creation({ user }: { user: UserProfile | null }) {
(isoString) => new ZotDate(new Date(isoString)),
);
}, [urlState.selectedDates]);
const [recommendation, setRecommendation] = useState<boolean>(false);
const [_recommendation, _setRecommendation] = useState<boolean>(false);

// Helper to update selected days.
const setSelectedDays = (daysOrUpdater: React.SetStateAction<ZotDate[]>) => {
Expand All @@ -63,8 +63,8 @@ export function Creation({ user }: { user: UserProfile | null }) {
void setUrlState({ meetingName: newName });
};

const meetingLocation = urlState.meetingLocation;
const setMeetingLocation = (locOrUpdater: React.SetStateAction<string>) => {
const _meetingLocation = urlState.meetingLocation;
const _setMeetingLocation = (locOrUpdater: React.SetStateAction<string>) => {
const newLoc =
typeof locOrUpdater === "function"
? locOrUpdater(urlState.meetingLocation)
Expand Down
Loading
Loading