[#25] changed loading order + added condition to keep loading screen while loading calendar list
This commit is contained in:
@@ -26,7 +26,7 @@ function CalendarPopover({
|
||||
const [description, setDescription] = useState("");
|
||||
const [color, setColor] = useState("");
|
||||
const [timeZone, setTimeZone] = useState("");
|
||||
const timezones = Intl.supportedValuesOf?.("timeZone") || [];
|
||||
const timezones = Intl.supportedValuesOf?.("timeZone") ?? [];
|
||||
const handleSave = () => {
|
||||
|
||||
dispatch(createCalendar({ name, description, color }));
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getCalendar, getCalendars } from "./CalendarApi";
|
||||
import getOpenPaasUserId from "../User/userAPI";
|
||||
import { parseCalendarEvent } from "../Events/eventUtils";
|
||||
|
||||
export const getCalendarsAsync = createAsyncThunk<
|
||||
export const getCalendarsListAsync = createAsyncThunk<
|
||||
Record<string, Calendars>, // Return type
|
||||
string // Arg type (access_token)
|
||||
>("calendars/getCalendars", async (access_token: string) => {
|
||||
@@ -21,51 +21,65 @@ export const getCalendarsAsync = createAsyncThunk<
|
||||
? cal["calendarserver:source"]._links.self.href
|
||||
.replace("/calendars/", "")
|
||||
.replace(".json", "")
|
||||
: cal._links.self.href
|
||||
.replace("/calendars/", "")
|
||||
.replace(".json", "")
|
||||
: cal._links.self.href.replace("/calendars/", "").replace(".json", "");
|
||||
|
||||
const color = cal["apple:color"];
|
||||
const calendarDetails = await getCalendar(id, access_token);
|
||||
const events = calendarDetails._embedded["dav:item"].map(
|
||||
(eventdata: any) => {
|
||||
const datas = eventdata.data[2][0][1];
|
||||
return parseCalendarEvent(datas, color);
|
||||
}
|
||||
);
|
||||
// const calendarDetails = await getCalendar(id, access_token);
|
||||
// const events = calendarDetails._embedded["dav:item"].map(
|
||||
// (eventdata: any) => {
|
||||
// const datas = eventdata.data[2][0][1];
|
||||
// return parseCalendarEvent(datas, color);
|
||||
// }
|
||||
// );
|
||||
|
||||
importedCalendars[id] = { id, name, description, color, events };
|
||||
importedCalendars[id] = { id, name, description, color, events: [] };
|
||||
}
|
||||
|
||||
return importedCalendars;
|
||||
});
|
||||
|
||||
export const getCalendarDetailAsync = createAsyncThunk<
|
||||
{ calId: string; events: CalendarEvent[] }, // Return type
|
||||
{ access_token: string; calId: string } // Arg type
|
||||
>("calendars/getCalendarDetails", async ({ access_token, calId }) => {
|
||||
const calendar = await getCalendar(calId, access_token);
|
||||
const color = calendar["apple:color"];
|
||||
const events: CalendarEvent[] = calendar._embedded["dav:item"].map(
|
||||
(eventdata: any) => {
|
||||
const datas = eventdata.data[2][0][1];
|
||||
return parseCalendarEvent(datas, color);
|
||||
}
|
||||
);
|
||||
|
||||
return { calId, events };
|
||||
});
|
||||
|
||||
const CalendarSlice = createSlice({
|
||||
name: "calendars",
|
||||
initialState: {} as Record<string, Calendars>,
|
||||
initialState: { list: {} as Record<string, Calendars>, pending: false },
|
||||
reducers: {
|
||||
createCalendar: (state, action: PayloadAction<Record<string, string>>) => {
|
||||
const id = Date.now().toString(36);
|
||||
state[id] = {} as Calendars;
|
||||
state[id].name = action.payload.name;
|
||||
state[id].color = action.payload.color;
|
||||
state[id].description = action.payload.description;
|
||||
state[id].events = [];
|
||||
state.list[id] = {} as Calendars;
|
||||
state.list[id].name = action.payload.name;
|
||||
state.list[id].color = action.payload.color;
|
||||
state.list[id].description = action.payload.description;
|
||||
state.list[id].events = [];
|
||||
},
|
||||
addEvent: (
|
||||
state,
|
||||
action: PayloadAction<{ calendarUid: string; event: CalendarEvent }>
|
||||
) => {
|
||||
if (!state[action.payload.calendarUid].events) {
|
||||
state[action.payload.calendarUid].events = [];
|
||||
if (!state.list[action.payload.calendarUid].events) {
|
||||
state.list[action.payload.calendarUid].events = [];
|
||||
}
|
||||
state[action.payload.calendarUid].events.push(action.payload.event);
|
||||
state.list[action.payload.calendarUid].events.push(action.payload.event);
|
||||
},
|
||||
removeEvent: (
|
||||
state,
|
||||
action: PayloadAction<{ calendarUid: string; eventUid: string }>
|
||||
) => {
|
||||
state[action.payload.calendarUid as string].events = state[
|
||||
state.list[action.payload.calendarUid as string].events = state.list[
|
||||
action.payload.calendarUid
|
||||
].events.filter((event) => {
|
||||
if (event.uid !== action.payload.calendarUid) {
|
||||
@@ -75,14 +89,41 @@ const CalendarSlice = createSlice({
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(
|
||||
getCalendarsAsync.fulfilled,
|
||||
(state, action: PayloadAction<Record<string, Calendars>>) => {
|
||||
Object.keys(action.payload).forEach((id) => {
|
||||
state[id] = action.payload[id];
|
||||
});
|
||||
}
|
||||
);
|
||||
builder
|
||||
.addCase(
|
||||
getCalendarsListAsync.fulfilled,
|
||||
(state, action: PayloadAction<Record<string, Calendars>>) => {
|
||||
state.pending = false;
|
||||
Object.keys(action.payload).forEach((id) => {
|
||||
state.list[id] = action.payload[id];
|
||||
});
|
||||
}
|
||||
)
|
||||
.addCase(
|
||||
getCalendarDetailAsync.fulfilled,
|
||||
(
|
||||
state,
|
||||
action: PayloadAction<{ calId: string; events: CalendarEvent[] }>
|
||||
) => {
|
||||
state.pending = false;
|
||||
if (!state.list[action.payload.calId]) {
|
||||
state.list[action.payload.calId] = {
|
||||
id: action.payload.calId,
|
||||
events: [] as CalendarEvent[],
|
||||
} as Calendars;
|
||||
}
|
||||
state.list[action.payload.calId].events = action.payload.events;
|
||||
state.list[action.payload.calId].events.forEach((event) => {
|
||||
event.color = state.list[action.payload.calId].color;
|
||||
});
|
||||
}
|
||||
)
|
||||
.addCase(getCalendarDetailAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
})
|
||||
.addCase(getCalendarsListAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ function EventPopover({
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const organizer = useAppSelector((state) => state.user.organiserData);
|
||||
const calendars = useAppSelector((state) => state.calendars);
|
||||
const calendars = useAppSelector((state) => state.calendars.list);
|
||||
const [title, setTitle] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [location, setLocation] = useState("");
|
||||
@@ -46,8 +46,8 @@ function EventPopover({
|
||||
const handleSave = () => {
|
||||
const newEvent: CalendarEvent = {
|
||||
title,
|
||||
start: new Date(start || ""),
|
||||
end: new Date(end || ""),
|
||||
start: new Date(start ?? ""),
|
||||
end: new Date(end ?? ""),
|
||||
uid: Date.now().toString(36),
|
||||
description,
|
||||
location,
|
||||
|
||||
@@ -40,22 +40,22 @@ export function parseCalendarEvent(
|
||||
break;
|
||||
case "organizer":
|
||||
event.organizer = {
|
||||
cn: params?.cn || "",
|
||||
cn: params?.cn ?? "",
|
||||
cal_address: value.replace(/^mailto:/, ""),
|
||||
};
|
||||
break;
|
||||
case "attendee":
|
||||
(event.attendee as userAttendee[]).push({
|
||||
cn: params?.cn || "",
|
||||
cn: params?.cn ?? "",
|
||||
cal_address: value.replace(/^mailto:/, ""),
|
||||
partstat: params?.partstat || "",
|
||||
rsvp: params?.rsvp || "",
|
||||
role: params?.role || "",
|
||||
cutype: params?.cutype || "",
|
||||
partstat: params?.partstat ?? "",
|
||||
rsvp: params?.rsvp ?? "",
|
||||
role: params?.role ?? "",
|
||||
cutype: params?.cutype ?? "",
|
||||
});
|
||||
break;
|
||||
case "dtstamp":
|
||||
event.stamp = new Date(value);
|
||||
event.stamp = value;
|
||||
break;
|
||||
case "sequence":
|
||||
event.sequence = Number(value);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { push } from "redux-first-history";
|
||||
|
||||
export function HandleLogin() {
|
||||
const userData = useAppSelector((state) => state.user.userData);
|
||||
const calendars = useAppSelector((state) => state.calendars);
|
||||
const dispatch = useAppDispatch();
|
||||
useEffect(() => {
|
||||
const initiateLogin = async () => {
|
||||
@@ -31,7 +32,9 @@ export function HandleLogin() {
|
||||
if (!userData) {
|
||||
return <Error />;
|
||||
}
|
||||
dispatch(push("/calendar"));
|
||||
if (!calendars.pending) {
|
||||
dispatch(push("/calendar"));
|
||||
}
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Callback } from "./oidcAuth";
|
||||
import { useAppDispatch } from "../../app/hooks";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import { push } from "redux-first-history";
|
||||
import { setTokens, setUserData } from "./userSlice";
|
||||
import { getOpenPaasUserIdAsync, setTokens, setUserData } from "./userSlice";
|
||||
import { Loading } from "../../components/Loading/Loading";
|
||||
import { getCalendarsAsync } from "../Calendars/CalendarSlice";
|
||||
import {
|
||||
getCalendarDetailAsync,
|
||||
getCalendarsListAsync,
|
||||
} from "../Calendars/CalendarSlice";
|
||||
|
||||
export function CallbackResume() {
|
||||
const dispatch = useAppDispatch();
|
||||
const hasRun = useRef(false);
|
||||
const calendars = useAppSelector((state) => state.calendars);
|
||||
const userId = useAppSelector((state) => state.user.userData?.openpaasId);
|
||||
const saved = sessionStorage.getItem("redirectState")
|
||||
? JSON.parse(sessionStorage.getItem("redirectState")!)
|
||||
: null;
|
||||
@@ -22,7 +27,9 @@ export function CallbackResume() {
|
||||
const data = await Callback(saved?.code_verifier, saved?.state);
|
||||
dispatch(setUserData(data?.userinfo));
|
||||
dispatch(setTokens(data?.tokenSet));
|
||||
dispatch(getCalendarsAsync(data?.tokenSet.access_token || ""));
|
||||
dispatch(getOpenPaasUserIdAsync(data?.tokenSet.access_token ?? ""));
|
||||
dispatch(getCalendarsListAsync(data?.tokenSet.access_token ?? ""));
|
||||
|
||||
sessionStorage.removeItem("redirectState");
|
||||
// Redirect to main page after successful callback
|
||||
dispatch(push("/"));
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as client from "openid-client";
|
||||
|
||||
export const clientConfig = {
|
||||
url: process.env.PUBLIC_SSO_BASE_URL || "",
|
||||
client_id: process.env.PUBLIC_SSO_CLIENT_ID || "",
|
||||
scope: process.env.PUBLIC_SSO_SCOPE || "",
|
||||
redirect_uri: process.env.PUBLIC_SSO_REDIRECT_URI || "",
|
||||
response_type: process.env.PUBLIC_SSO_RESPONSE_TYPE || "",
|
||||
code_challenge_method: process.env.PUBLIC_SSO_CODE_CHALLENGE_METHOD || "",
|
||||
post_logout_redirect_uri: process.env.PUBLIC_SSO_POST_LOGOUT_REDIRECT || "",
|
||||
url: process.env.PUBLIC_SSO_BASE_URL ?? "",
|
||||
client_id: process.env.PUBLIC_SSO_CLIENT_ID ?? "",
|
||||
scope: process.env.PUBLIC_SSO_SCOPE ?? "",
|
||||
redirect_uri: process.env.PUBLIC_SSO_REDIRECT_URI ?? "",
|
||||
response_type: process.env.PUBLIC_SSO_RESPONSE_TYPE ?? "",
|
||||
code_challenge_method: process.env.PUBLIC_SSO_CODE_CHALLENGE_METHOD ?? "",
|
||||
post_logout_redirect_uri: process.env.PUBLIC_SSO_POST_LOGOUT_REDIRECT ?? "",
|
||||
};
|
||||
|
||||
export async function getClientConfig() {
|
||||
|
||||
@@ -2,6 +2,7 @@ export interface userData {
|
||||
email: string;
|
||||
sid: string;
|
||||
sub: string;
|
||||
openpaasId?: string;
|
||||
}
|
||||
|
||||
export interface userOrganiser {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { userData, userOrganiser } from "./userDataTypes";
|
||||
import getOpenPaasUserId from "./userAPI";
|
||||
|
||||
export const getOpenPaasUserIdAsync = createAsyncThunk<
|
||||
string, // Return type
|
||||
string // Arg type
|
||||
>("user/getOpenPaasUserId", async (access_token) => {
|
||||
const user = await getOpenPaasUserId(access_token);
|
||||
|
||||
return user.id;
|
||||
});
|
||||
|
||||
export const userSlice = createSlice({
|
||||
name: "user",
|
||||
@@ -21,6 +31,11 @@ export const userSlice = createSlice({
|
||||
state.tokens = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(getOpenPaasUserIdAsync.fulfilled, (state, action) => {
|
||||
state.userData.openpaasId = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
|
||||
Reference in New Issue
Block a user