Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fix: Optimistically render when drag-and-dropping event #209

Merged
4 changes: 4 additions & 0 deletions packages/web/src/common/types/web.event.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface Schema_GridEvent extends Schema_Event {
siblingsCount?: number;
}

export interface Schema_OptimisticEvent extends Schema_Event {
_id: string; // We guarantee that we have an _id for optimistic events, unlike `Schema_Event`
}

export interface Schema_SelectedDates {
startDate: Date;
startTime: SelectOption<string>;
Expand Down
14 changes: 9 additions & 5 deletions packages/web/src/common/utils/event.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import { YEAR_MONTH_DAY_COMPACT_FORMAT } from "@core/constants/date.constants";
import { Categories_Event, Schema_Event } from "@core/types/event.types";
import { Origin, Priorities } from "@core/constants/core.constants";
import { Status } from "@core/errors/status.codes";
import { Response_GetEventsSaga } from "@web/ducks/events/event.types";

import {
Schema_GridEvent,
Schema_OptimisticEvent,
Schema_SomedayEventsColumn,
} from "../types/web.event.types";
import { removeGridFields } from "./grid.util";
import { removeGridProperties } from "./grid.util";
import {
COLUMN_WEEK,
COLUMN_MONTH,
Expand Down Expand Up @@ -191,7 +193,7 @@ export const prepEvtAfterDraftDrop = (
};

export const prepEvtBeforeConvertToSomeday = (draft: Schema_GridEvent) => {
const event = removeGridFields(draft);
const event = removeGridProperties(draft);

if (event.recurrence) {
delete event.recurrence;
Expand All @@ -201,7 +203,7 @@ export const prepEvtBeforeConvertToSomeday = (draft: Schema_GridEvent) => {
};

export const prepEvtBeforeSubmit = (draft: Schema_GridEvent) => {
const _event = removeGridFields({ ...draft });
const _event = removeGridProperties({ ...draft });

const event = {
..._event,
Expand All @@ -211,8 +213,10 @@ export const prepEvtBeforeSubmit = (draft: Schema_GridEvent) => {
return event;
};

export const createOptimisticEvent = (event: Schema_Event) => {
const _event: Schema_Event = {
export const replaceIdWithOptimisticId = (
event: Schema_Event
): Schema_OptimisticEvent => {
const _event: Schema_OptimisticEvent = {
...event,
_id: `${ID_OPTIMISTIC_PREFIX}-${uuidv4()}`,
};
Expand Down
11 changes: 8 additions & 3 deletions packages/web/src/common/utils/grid.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,17 +488,22 @@ const normalizeDayNums = (days: number[]) => {
});
};

export const removeGridFields = (event: Schema_GridEvent): Schema_Event => {
export const removeGridProperties = (event: Schema_GridEvent): Schema_Event => {
const {
isEditing,
importanceIndex,
isOpen,
row,
siblingsCount,
...eventWithoutGridFields
...eventWithoutGridProps
} = event;

return eventWithoutGridFields;
return eventWithoutGridProps;
};

export const removeSomedayProperties = (event: Schema_Event): Schema_Event => {
const { order, recurrence, ...eventWithoutSomedayProps } = event;
return eventWithoutSomedayProps;
};

export const widthMinusPadding = (width: number) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/web/src/ducks/events/event.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Schema_Event,
} from "@core/types/event.types";
import { CompassApi } from "@web/common/apis/compass.api";
import { AxiosPromise } from "axios";

const EventApi = {
create: (event: Schema_Event) => {
Expand All @@ -17,7 +18,7 @@ const EventApi = {
_id: string,
event: Schema_Event,
params: { applyTo?: Categories_Recur }
) => {
): AxiosPromise<Schema_Event> => {
if (params?.applyTo) {
return CompassApi.put(`/event/${_id}?applyTo=${params.applyTo}`, event);
}
Expand Down
124 changes: 8 additions & 116 deletions packages/web/src/ducks/events/sagas/event.sagas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { normalize } from "normalizr";
import dayjs from "dayjs";
import { call, put, takeLatest, select } from "@redux-saga/core/effects";
import { call, put, select } from "@redux-saga/core/effects";
import { Params_Events, Schema_Event } from "@core/types/event.types";
import { YEAR_MONTH_DAY_FORMAT } from "@core/constants/date.constants";
import { RootState } from "@web/store";
Expand All @@ -10,7 +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,
replaceIdWithOptimisticId,
handleError,
} from "@web/common/utils/event.util";
import { Schema_GridEvent } from "@web/common/types/web.event.types";
Expand All @@ -25,7 +25,6 @@ import {
} from "../slices/event.slice";
import { getWeekEventsSlice } from "../slices/week.slice";
import {
Action_ConvertSomedayEvent,
Action_ConvertTimedEvent,
Action_CreateEvent,
Action_DeleteEvent,
Expand All @@ -38,56 +37,13 @@ import {
Entities_Event,
} from "../event.types";
import { getSomedayEventsSlice } from "../slices/someday.slice";
import { Action_Someday_Reorder } from "../slices/someday.slice.types";
import {
insertOptimisticEvent,
normalizedEventsSchema,
replaceOptimisticId,
} from "./event.saga.util";
} from "./saga.util";

function* convertSomedayEvent({ payload }: Action_ConvertSomedayEvent) {
try {
const { _id, updatedFields } = payload;

const currEvent = (yield select((state: RootState) =>
selectEventById(state, _id)
)) as Response_GetEventsSaga;

const updatedEvent = { ...currEvent, ...updatedFields };
delete updatedEvent.order;
delete updatedEvent.recurrence;

const res = yield call(EventApi.edit, _id, updatedEvent);
const event = res.data as Schema_Event;
yield put(getWeekEventsSlice.actions.insert(event._id));

const normalizedEvent = normalize<Schema_Event>(
event,
normalizedEventsSchema()
);
yield put(
eventsEntitiesSlice.actions.insert(normalizedEvent.entities.events)
);

const somedayEvents: Response_GetEventsSaga = (yield select(
(state: RootState) => selectPaginatedEventsBySectionType(state, "someday")
)) as Response_GetEventsSaga;

const remainingSomedayEvents = somedayEvents.data.filter(
(id) => id !== _id
);
yield put(
getSomedayEventsSlice.actions.success({
data: remainingSomedayEvents,
})
);
} catch (error) {
yield put(getSomedayEventsSlice.actions.error());
handleError(error as Error);
}
}

function* convertTimedEvent({ payload }: Action_ConvertTimedEvent) {
export function* convertTimedEvent({ payload }: Action_ConvertTimedEvent) {
try {
const res = yield call(EventApi.edit, payload.event._id, payload.event);
const event = res.data as Schema_Event;
Expand Down Expand Up @@ -118,8 +74,8 @@ function* convertTimedEvent({ payload }: Action_ConvertTimedEvent) {
}
}

function* createEvent({ payload }: Action_CreateEvent) {
const event = createOptimisticEvent(payload);
export function* createEvent({ payload }: Action_CreateEvent) {
const event = replaceIdWithOptimisticId(payload);
const optimisticId = event._id;

try {
Expand Down Expand Up @@ -163,18 +119,6 @@ export function* deleteEvent({ payload }: Action_DeleteEvent) {
}
}

export function* deleteSomedayEvent({ payload }: Action_DeleteEvent) {
try {
yield put(eventsEntitiesSlice.actions.delete(payload));

yield call(EventApi.delete, payload._id);
} catch (error) {
yield put(getSomedayEventsSlice.actions.error());
handleError(error as Error);
yield put(getSomedayEventsSlice.actions.request());
}
}

export function* editEvent({ payload }: Action_EditEvent) {
const { _id, applyTo, event, shouldRemove } = payload;

Expand All @@ -193,7 +137,7 @@ export function* editEvent({ payload }: Action_EditEvent) {
}
}

function* getCurrentMonthEvents({ payload }: Action_GetPaginatedEvents) {
export function* getCurrentMonthEvents({ payload }: Action_GetPaginatedEvents) {
try {
const startDate = dayjs().startOf("month").format(YEAR_MONTH_DAY_FORMAT);
const endDate = dayjs().endOf("month").format(YEAR_MONTH_DAY_FORMAT);
Expand Down Expand Up @@ -238,31 +182,7 @@ function* getEvents(
}
}

export function* getSomedayEvents({ payload }: Action_GetEvents) {
try {
const res = (yield call(EventApi.get, {
someday: true,
startDate: payload.startDate,
endDate: payload.endDate,
})) as Response_GetEventsSuccess;

const normalizedEvents = normalize<Schema_Event>(res.data, [
normalizedEventsSchema(),
]);
yield put(
eventsEntitiesSlice.actions.insert(normalizedEvents.entities.events)
);

const data = {
data: normalizedEvents.result as Payload_NormalizedAsyncAction,
};
yield put(getSomedayEventsSlice.actions.success(data));
} catch (error) {
yield put(getSomedayEventsSlice.actions.error());
}
}

function* getWeekEvents({ payload }: Action_GetEvents) {
export function* getWeekEvents({ payload }: Action_GetEvents) {
try {
const data = (yield call(getEvents, payload)) as Response_GetEventsSaga;
yield put(getWeekEventsSlice.actions.success(data));
Expand All @@ -271,31 +191,3 @@ function* getWeekEvents({ payload }: Action_GetEvents) {
handleError(error as Error);
}
}

function* reorderSomedayEvents({ payload }: Action_Someday_Reorder) {
try {
yield call(EventApi.reorder, payload);
} catch (error) {
yield put(getSomedayEventsSlice.actions.error());
handleError(error as Error);
}
}

/************
* Assemble
***********/
export function* eventsSagas() {
yield takeLatest(getWeekEventsSlice.actions.request, getWeekEvents);
yield takeLatest(getWeekEventsSlice.actions.convert, convertTimedEvent);
yield takeLatest(
getCurrentMonthEventsSlice.actions.request,
getCurrentMonthEvents
);
yield takeLatest(getSomedayEventsSlice.actions.convert, convertSomedayEvent);
yield takeLatest(getSomedayEventsSlice.actions.request, getSomedayEvents);
yield takeLatest(getSomedayEventsSlice.actions.delete, deleteSomedayEvent);
yield takeLatest(getSomedayEventsSlice.actions.reorder, reorderSomedayEvents);
yield takeLatest(createEventSlice.actions.request, createEvent);
yield takeLatest(editEventSlice.actions.request, editEvent);
yield takeLatest(deleteEventSlice.actions.request, deleteEvent);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { schema } from "normalizr";
import { put } from "redux-saga/effects";
import { put, select } from "redux-saga/effects";
import { normalize } from "normalizr";
import { Schema_Event } from "@core/types/event.types";
import { Schema_GridEvent } from "@web/common/types/web.event.types";
import { RootState } from "@web/store";

import { getSomedayEventsSlice } from "../slices/someday.slice";
import { getWeekEventsSlice } from "../slices/week.slice";
import { eventsEntitiesSlice } from "../slices/event.slice";
import { selectEventById } from "../selectors/event.selectors";

export function* getEventById(_id: string) {
const currEvent = (yield select((state: RootState) =>
selectEventById(state, _id)
)) as Schema_Event;
return currEvent;
}

export function* insertOptimisticEvent(
event: Schema_GridEvent,
Expand Down
Loading