Skip to content

Commit

Permalink
🐛 fix(web): Keep new event on grid during save
Browse files Browse the repository at this point in the history
  • Loading branch information
that-one-arab committed Dec 22, 2024
1 parent 56eac93 commit 4e85f7f
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/core/src/types/event.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface Schema_Event {
title?: string;
updatedAt?: Date;
user?: string;
optimistic?: boolean;
}

export interface Query_Event extends Query {
Expand Down
11 changes: 11 additions & 0 deletions packages/web/src/common/utils/event.util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { schema } from "normalizr";
import dayjs, { Dayjs } from "dayjs";
import { v4 as uuidv4 } from "uuid";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
import isBetween from "dayjs/plugin/isBetween";
Expand Down Expand Up @@ -209,3 +210,13 @@ export const prepEvtBeforeSubmit = (draft: Schema_GridEvent) => {

export const normalizedEventsSchema = () =>
new schema.Entity("events", {}, { idAttribute: "_id" });

export const createOptimisticEvent = (event: Schema_Event) => {
const _event = {
...event,
_id: `optimistic-${uuidv4()}`,
optimistic: true,
} as Schema_Event;

return _event;
};
9 changes: 9 additions & 0 deletions packages/web/src/ducks/events/event.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export interface Action_InsertEvents extends Action {
payload: Entities_Event | undefined;
}

export interface Action_ReplaceEvent extends Action {
payload: Payload_ReplaceEvent;
}

export interface Action_TimezoneChange extends Action {
payload: { timezone: string };
}
Expand Down Expand Up @@ -80,6 +84,11 @@ export interface Payload_EditEvent {
shouldRemove?: boolean;
}

export interface Payload_ReplaceEvent {
oldEventId: string;
newEvent: Schema_Event;
}

export interface Payload_GetPaginatedEvents extends Filters_Pagination {
priorities: Priorities[];
}
Expand Down
55 changes: 46 additions & 9 deletions packages/web/src/ducks/events/sagas/event.sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { EventApi } from "@web/ducks/events/event.api";
import { selectEventById } from "@web/ducks/events/selectors/event.selectors";
import { selectPaginatedEventsBySectionType } from "@web/ducks/events/selectors/util.selectors";
import {
createOptimisticEvent,
handleError,
normalizedEventsSchema,
} from "@web/common/utils/event.util";
Expand Down Expand Up @@ -112,38 +113,74 @@ function* convertTimedEvent({ payload }: Action_ConvertTimedEvent) {
}

function* createEvent({ payload }: Action_CreateEvent) {
const event = createOptimisticEvent(payload);
try {
// Insert the optimistic event into the store to immediately display the event
if (payload.isSomeday) {
yield put(getSomedayEventsSlice.actions.insert(event._id));
} else {
yield put(getWeekEventsSlice.actions.insert(event._id));
}
yield put(
eventsEntitiesSlice.actions.insert(
normalize<Schema_Event>(event, normalizedEventsSchema()).entities.events
)
);

// Send a request to create the event
const res = (yield call(
EventApi.create,
payload
)) as Response_CreateEventSaga;

const normalizedEvent = normalize<Schema_Event>(
res.data,
normalizedEventsSchema()
);

// Replace the optimistic event with the actual event
if (payload.isSomeday) {
yield put(getSomedayEventsSlice.actions.insert(res.data._id));
yield put(
getSomedayEventsSlice.actions.replace({
oldSomedayId: event._id,
newSomedayId: res.data._id,
})
);
} else {
yield put(getWeekEventsSlice.actions.insert(res.data._id));
yield put(
getWeekEventsSlice.actions.replace({
oldWeekId: event._id,
newWeekId: res.data._id,
})
);
}

yield put(
eventsEntitiesSlice.actions.insert(normalizedEvent.entities.events)
eventsEntitiesSlice.actions.replace({
oldEventId: event._id,
newEvent: res.data,
})
);

yield put(createEventSlice.actions.success());
} catch (error) {
yield put(createEventSlice.actions.error());
yield call(deleteEvent, {
payload: { _id: event._id },
} as Action_DeleteEvent);
handleError(error as Error);
}
}

export function* deleteEvent({ payload }: Action_DeleteEvent) {
// TODO: Ideally we should pass the entire event object instead of just its id
// to the `deleteEvent` payload. Gives us more context to work with.
const event = (yield select((state: RootState) =>
selectEventById(state, payload._id)
)) as Schema_Event;

try {
yield put(getWeekEventsSlice.actions.delete(payload));
yield put(eventsEntitiesSlice.actions.delete(payload));
yield call(EventApi.delete, payload._id);
if (!event.optimistic) {
yield call(EventApi.delete, payload._id);
}

yield put(deleteEventSlice.actions.success());
} catch (error) {
yield put(deleteEventSlice.actions.error());
Expand Down
5 changes: 5 additions & 0 deletions packages/web/src/ducks/events/slices/event.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Action_DeleteEvent,
Action_EditEvent,
Action_InsertEvents,
Action_ReplaceEvent,
Action_TimezoneChange,
Entities_Event,
Payload_EditEvent,
Expand Down Expand Up @@ -52,6 +53,10 @@ export const eventsEntitiesSlice = createSlice({
insert: (state, action: Action_InsertEvents) => {
state.value = { ...state.value, ...action.payload };
},
replace: (state, action: Action_ReplaceEvent) => {
delete state.value[action.payload.oldEventId];
state.value[action.payload.newEvent._id] = action.payload.newEvent;
},
updateAfterTzChange: (state, action: Action_TimezoneChange) => {
const nextState = changeTimezones(state, action.payload.timezone);
state.value = nextState.value;
Expand Down
12 changes: 12 additions & 0 deletions packages/web/src/ducks/events/slices/someday.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ export const getSomedayEventsSlice = createAsyncSlice<
}
},

replace: (
state,
action: { payload: { oldSomedayId: string; newSomedayId: string } }
) => {
state.value.data = state.value.data.map((id: string) => {
if (id === action.payload.oldSomedayId) {
return action.payload.newSomedayId;
}
return id;
});
},

reorder: (state, action) => {
return;
},
Expand Down
11 changes: 11 additions & 0 deletions packages/web/src/ducks/events/slices/week.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@ export const getWeekEventsSlice = createAsyncSlice<
state.value.data.push(action.payload);
}
},
replace: (
state,
action: { payload: { oldWeekId: string; newWeekId: string } }
) => {
state.value.data = state.value.data.map((id: string) => {
if (id === action.payload.oldWeekId) {
return action.payload.newWeekId;
}
return id;
});
},
},
});

0 comments on commit 4e85f7f

Please sign in to comment.