Skip to content

Commit

Permalink
more fixes for datetime stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
dylmye committed Apr 12, 2023
1 parent 5552b0b commit d501e50
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 76 deletions.
5 changes: 3 additions & 2 deletions src/components/AddTripItemCard/AddEditTripItemForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { useCustomTheme } from "../../contexts/CustomTheme";
import poweredByGoogleLightMode from "../../assets/images/powered_by_google_light_mode.png";
import poweredByGoogleDarkMode from "../../assets/images/powered_by_google_dark_mode.png";
import styles from "./styles.module.css";
import { butcherDatetimeTimezone } from "../../helpers/dates";

export interface AddEditTripItemFormProps {
showCancel?: boolean;
Expand Down Expand Up @@ -143,8 +144,8 @@ const AddEditTripItemForm = ({
helperText: "'All day' option coming soon",
fullWidth: true,
}}
minDate={dayjs(tripDetails?.startsAt)}
maxDate={dayjs(tripDetails?.endsAt)}
minDate={dayjs(butcherDatetimeTimezone(tripDetails?.startsAt))}
maxDate={dayjs(butcherDatetimeTimezone(tripDetails?.endsAt))}
validate={(value: string) => {
const requireCheck = fieldIsRequired(value);
const startsAtAfterTripStart = dayjs
Expand Down
5 changes: 1 addition & 4 deletions src/components/AddTripItemCard/AddTripItemCardContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { TripItemType } from "../../types/TripItemType";
import TripItemDraft from "../../types/TripItemDraft";
import { useAddTripItem } from "../../store/features/trips";
import { getTripItemCategory } from "../../helpers/tripItems";
import { forceDateInUserTimezone } from "../../helpers/dates";
import AddEditTripItemForm from "./AddEditTripItemForm";
import { AddTripItemCardProps } from ".";

Expand Down Expand Up @@ -44,9 +43,7 @@ const AddTripItemCardContents = ({
category: getTripItemCategory(initialValues as { type: TripItemType }),
type: (initialValues.type as TripItemType) ?? null,
title: "",
startsAt: forceDateInUserTimezone(
initialValues.date as string
).format(),
startsAt: initialValues.date as string,
endsAt: null,
}}
onSubmit={onSubmit}
Expand Down
51 changes: 33 additions & 18 deletions src/components/AddTripModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const AddTripModal = (props: ModalProps) => {

const TOTAL_STEPS = online ? 3 : 2;

const today = useMemo(() => dayjs(), []);
const today = useMemo(() => dayjs.utc(), []);

const goBack = useCallback(() => setActiveStep(activeStep - 1), [activeStep]);
const goForward = useCallback(
Expand Down Expand Up @@ -124,28 +124,43 @@ const AddTripModal = (props: ModalProps) => {
),
});

/**
* Upload a blob to Firebase Storage and get its URI
* @param blob The file to upload
* @param tripId The trip ID to upload the file under
* @returns The newly created file's URI
*/
const getCoverImageUri = async (
blob: File,
tripId: string
): Promise<string> => {
let coverImageUri: string | null = null;
const extension = getExtensionByMimetype(blob.type);
const storageRef = ref(storage, `trip-thumbs/${tripId}.${extension}`);

setFormImageUploading(true);
let res: UploadResult | null = null;
try {
res = await uploadBytes(storageRef, blob);
} catch (e) {
if (e instanceof FirebaseError) {
setFormError(getUploadErrorFriendlyText(e));
console.error("trip thumb upload failed:", e.code, res?.metadata);
}
}
setFormImageUploading(false);

coverImageUri = await getDownloadURL(storageRef);
return coverImageUri;
};

const onFormSubmit = async (values: TripDraft) => {
let coverImageUri: string | null = null;

setFormError(null);
if (values.coverImageBlob) {
const extension = getExtensionByMimetype(values.coverImageBlob.type);
const storageRef = ref(storage, `trip-thumbs/${values.id}.${extension}`);

setFormImageUploading(true);
let res: UploadResult | null = null;
try {
res = await uploadBytes(storageRef, values.coverImageBlob);
delete values.coverImageBlob;
} catch (e) {
if (e instanceof FirebaseError) {
setFormError(getUploadErrorFriendlyText(e));
console.error("trip thumb upload failed:", e.code, res?.metadata);
}
}
setFormImageUploading(false);

coverImageUri = await getDownloadURL(storageRef);
coverImageUri = await getCoverImageUri(values.coverImageBlob, values.id);
delete values.coverImageBlob;
}
const newTrip = await addTrip({ ...values, image: coverImageUri });
setActiveStep(0);
Expand Down
6 changes: 1 addition & 5 deletions src/components/TripItineraryItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
getTripItemTypeLabel,
renderExtraText,
} from "../../helpers/tripItems";
import { forceDateInUserTimezone, formatTime } from "../../helpers/dates";
import { formatTime } from "../../helpers/dates";
import styles from "./styles.module.css";

export interface TripItineraryItemProps {
Expand Down Expand Up @@ -204,10 +204,6 @@ const TripItineraryItem = ({
initialValues={{
...item,
category: getTripItemCategory(item),
startsAt: forceDateInUserTimezone(item.startsAt).format(),
endsAt:
item.endsAt &&
forceDateInUserTimezone(item.endsAt)?.format(),
}}
onSubmit={onUpdateTripItem}>
<AddEditTripItemForm
Expand Down
10 changes: 7 additions & 3 deletions src/features/tripDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import Trip from "../../types/Trip";
import { useGetTripById } from "../../store/features/trips";
import { tripIsExample, tripIsOwnedByUser } from "../../helpers/trips";
import { groupTripItemsByDay } from "../../helpers/tripItems";
import { dateCompare, formatDate } from "../../helpers/dates";
import {
butcherDatetimeTimezone,
dateCompare,
formatDate,
} from "../../helpers/dates";
import { COLOURS } from "../../helpers/colours";
import { auth } from "../../firebase";
import TripItineraryItem from "../../components/TripItineraryItem";
Expand Down Expand Up @@ -262,10 +266,10 @@ const TripDetails = () => {
<Box sx={{ marginTop: 2 }}>
<EmptyTripCard
id={trip.id}
startsAt={trip.startsAt}
startsAt={butcherDatetimeTimezone(trip.startsAt)}
title={trip.title}
location={trip.location}
endsAt={trip.endsAt}
endsAt={butcherDatetimeTimezone(trip.endsAt)}
public={trip.public}
/>
</Box>
Expand Down
52 changes: 37 additions & 15 deletions src/helpers/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,37 @@ import TripItem from "../types/Tripitem";
import Trip from "../types/Trip";
import TripSnapshot from "../types/firebase/TripSnapshot";
import TripItemSnapshot from "../types/firebase/TripItemSnapshot";
import { formatFirebaseDateTime } from "./dates";
import { forceDateInUserTimezone, formatFirebaseDateTime } from "./dates";

/**
* Storing dates in Firebase:
* - all date-times should be stored in UTC without conversion:
* for ex., user in UTC-4 entering 18:30 should store as 18:30 UTC
* - all dates without times should be stored at 00:00 UTC
*/

/**
* Convert a dayjs-compat date string to Firebase's Timestamp object.
* Accurate to seconds.
* @param date The date to convert
* @returns The Firebase Timestamp representation of the date provided
*/
export const convertDateStringToTimestamp = (date: string): Timestamp =>
new Timestamp(dayjs(date).utc(true).unix(), 0);
new Timestamp(dayjs(date).unix(), 0);

/**
* Translate timestamps on trips.
*/
export const convertTripDocument: FirestoreDataConverter<Trip> = {
toFirestore(trip: Trip): TripSnapshot {
const startsAt =
trip.startsAt && dayjs.utc(trip.startsAt).startOf("day").format();
const endsAt = trip.endsAt && dayjs.utc(trip.endsAt).endOf("day").format();

return {
...trip,
startsAt: convertDateStringToTimestamp(trip.startsAt as string),
endsAt: convertDateStringToTimestamp(trip.endsAt as string),
startsAt: convertDateStringToTimestamp(startsAt!),
endsAt: convertDateStringToTimestamp(endsAt!),
createdAtUtc: convertDateStringToTimestamp(trip.createdAtUtc),
updatedAtUtc: convertDateStringToTimestamp(trip.updatedAtUtc),
};
Expand All @@ -35,12 +52,12 @@ export const convertTripDocument: FirestoreDataConverter<Trip> = {
return {
...data,
id,
startsAt: data.startsAt && dayjs.unix(data.startsAt.seconds).format(),
endsAt: data.endsAt && dayjs.unix(data.endsAt.seconds).format(),
startsAt: data.startsAt && formatFirebaseDateTime(data.startsAt),
endsAt: data.endsAt && formatFirebaseDateTime(data.endsAt),
createdAtUtc:
data.createdAtUtc && dayjs.unix(data.createdAtUtc.seconds).format(),
data.createdAtUtc && formatFirebaseDateTime(data.createdAtUtc),
updatedAtUtc:
data.updatedAtUtc && dayjs.unix(data.updatedAtUtc.seconds).format(),
data.updatedAtUtc && formatFirebaseDateTime(data.updatedAtUtc),
};
},
};
Expand All @@ -56,22 +73,23 @@ const tripItemTimestampKeys: (keyof TripItem)[] = [
*/
export const convertTripItemDocuments: FirestoreDataConverter<TripItem> = {
toFirestore(tripItem: TripItem): TripItemSnapshot {
// convert any
// convert any timestamps
Object.keys(tripItem)
.filter((k) => tripItemTimestampKeys.includes(k as keyof TripItem))
.forEach((k) => {
// @ts-ignore typescript is stupid i promise this works
tripItem[k] =
!!tripItem[k as keyof TripItem] &&
new Timestamp(
dayjs(tripItem[k as keyof TripItem] as string).unix(),
0
convertDateStringToTimestamp(
dayjs.utc(tripItem[k as keyof TripItem] as string).format()
);
});

const startsAt = dayjs.utc(tripItem.startsAt).format();

return {
...tripItem,
startsAt: new Timestamp(dayjs(tripItem.startsAt).unix(), 0),
startsAt: convertDateStringToTimestamp(startsAt),
};
},
fromFirestore(
Expand All @@ -87,13 +105,17 @@ export const convertTripItemDocuments: FirestoreDataConverter<TripItem> = {
if (!data[k]) {
return;
}
data[k] = formatFirebaseDateTime(data[k]);
data[k] = forceDateInUserTimezone(
formatFirebaseDateTime(data[k])
).format();
});

return {
...data,
id,
startsAt: formatFirebaseDateTime(data.startsAt),
startsAt: forceDateInUserTimezone(
formatFirebaseDateTime(data.startsAt)
).format(),
};
},
};
Expand Down
7 changes: 5 additions & 2 deletions src/helpers/dates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const formatDate = (
};

export const formatTime = (date: string | dayjs.Dayjs, compact = true) => {
let value: dayjs.Dayjs | string = dayjs.utc(date);
let value: dayjs.Dayjs | string = dayjs(date);
value = value.format("LT");
if (compact) {
return value.toLocaleLowerCase().replace(" ", "");
Expand All @@ -57,10 +57,13 @@ export const formatDateTime = (
};

export const formatFirebaseDateTime = (date: string | Timestamp): string => {
if (!date) {
return "<InvalidDate>";
}
if (typeof date === "string") {
return dayjs(date).format();
}
return dayjs.unix(date.seconds).format();
return dayjs.unix(date.seconds).utc().format();
};

export const tripIsInState = (
Expand Down
22 changes: 8 additions & 14 deletions src/store/features/trips/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,14 @@ const addTripItemByTripId: TripActions["addTripItemByTripId"] = async ({
const { category, ...filteredTripItem } = tripItem;
const ref = getTripItemsCollection(tripId);
try {
await addDoc(ref, {
// @ts-ignore id should be missing as it's not made yet lol
const newTripItem = convertTripItemDocuments.toFirestore({
...filteredTripItem,
startsAt: convertDateStringToTimestamp(
filteredTripItem.startsAt as string
),
endsAt:
filteredTripItem.endsAt &&
convertDateStringToTimestamp(filteredTripItem.endsAt as string),
title: filteredTripItem.title?.length
? filteredTripItem.title
: getTripItemTypeLabel(filteredTripItem.type),
} as TripItemSnapshot);
}) as TripItemSnapshot;
await addDoc(ref, newTripItem);
} catch (e) {
throw new Error(`[store/firestore] error adding a trip item: ${e}`);
}
Expand All @@ -111,13 +107,11 @@ const updateTripItemById: TripActions["updateTripItemById"] = async ({
const ref = getTripItemsCollection(tripId);
try {
const { id, category, ...filteredTripItem } = data;
await updateDoc(doc(ref, data.id), {
const converted = convertTripItemDocuments.toFirestore({
...filteredTripItem,
startsAt: convertDateStringToTimestamp(filteredTripItem.startsAt),
endsAt:
filteredTripItem.endsAt &&
convertDateStringToTimestamp(filteredTripItem.endsAt),
});
id,
}) as TripItemSnapshot;
await updateDoc(doc(ref, data.id), converted);
} catch (e) {
throw new Error(`[store/firestore] error updating a trip item: ${e}`);
}
Expand Down
18 changes: 5 additions & 13 deletions src/store/features/trips/redux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ import TripDraft from "../../../types/TripDraft";
import TripDetails from "../../../types/TripDetails";
import Trip from "../../../types/Trip";
import { getTripItemTypeLabel } from "../../../helpers/tripItems";
import {
butcherDatetimeTimezone,
dateCompare,
tripIsInState,
} from "../../../helpers/dates";
import { dateCompare, tripIsInState } from "../../../helpers/dates";
import SliceNames from "../../../enums/SliceNames";
import exampleTrip from "../../../data/exampleTrip";
import { RootState } from "../../../app/store";
Expand Down Expand Up @@ -116,12 +112,10 @@ const tripSlice = createSlice({
title: filteredPayload?.title?.length
? filteredPayload.title
: getTripItemTypeLabel(filteredPayload.type),
startsAt: butcherDatetimeTimezone(filteredPayload.startsAt)!,
endsAt: butcherDatetimeTimezone(filteredPayload.endsAt!),
startsAt: filteredPayload.startsAt,
endsAt: filteredPayload.endsAt,
};

console.log(newTripItem);

const items: TripItem[] = [...(trip?.items || []), newTripItem];

tripsAdapter.updateOne(state, { id: payload.tripId, changes: { items } });
Expand Down Expand Up @@ -155,12 +149,10 @@ const tripSlice = createSlice({
const newDataFormatted: TripItem = {
...newData,
id: newData.id!,
startsAt: butcherDatetimeTimezone(newData.startsAt)!,
endsAt: butcherDatetimeTimezone(newData.endsAt!),
startsAt: newData.startsAt,
endsAt: newData.endsAt,
};

console.log(newDataFormatted);

const items: TripItem[] = [...allOtherItems, newDataFormatted];

tripsAdapter.updateOne(state, {
Expand Down

0 comments on commit d501e50

Please sign in to comment.