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

Load and Store Friend Schedules from Firebase #187

Open
wants to merge 12 commits into
base: bog-changes-s23
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@
}
],
"no-plusplus": ["warn", { "allowForLoopAfterthoughts": true }],
"prettier/prettier": "warn",
"prettier/prettier": [
"warn",
{
"endOfLine": "auto"
}
],
"react/require-default-props": "off",
"no-await-in-loop": "off",
"camelcase": "off",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@sentry/react": "^6.12.0",
"@sentry/tracing": "^6.12.0",
"@types/lodash": "^4.14.192",
"@types/react-map-gl": "^6.1.3",
"axios": "^0.21.4",
"cheerio": "^1.0.0-rc.3",
Expand All @@ -23,6 +24,7 @@
"html-entities": "^2.3.3",
"immer": "^9.0.6",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"mapbox-gl": "^2.4.1",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.1",
Expand Down
238 changes: 206 additions & 32 deletions src/components/AppDataLoader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import produce, { Immutable, Draft, original, castDraft } from 'immer';
import React, { useCallback, useMemo } from 'react';

import {
ScheduleContextValue,
TermsContext,
ScheduleContext,
ScheduleContextValue,
FriendContext,
FriendContextValue,
} from '../../contexts';
import { AccountContext, AccountContextValue } from '../../contexts/account';
import { Oscar } from '../../data/beans';
Expand All @@ -15,21 +17,30 @@ import {
TermScheduleData,
ScheduleVersion,
ScheduleData,
FriendTermData,
FriendInfo,
FriendScheduleData,
} from '../../data/types';
import { lexicographicCompare } from '../../utils/misc';
import {
StageLoadUIState,
StageLoadTerms,
StageEnsureValidTerm,
StageLoadAccount,
StageLoadRawFriendData,
StageLoadRawScheduleDataHybrid,
StageMigrateScheduleData,
StageCreateScheduleDataProducer,
StageExtractTermScheduleData,
StageLoadOscarData,
StageExtractScheduleVersion,
StageSkeletonProps,
StageCreateFriendDataProducer,
StageExtractFriendTermData,
StageLoadRawFriendScheduleDataFromFirebaseFunction,
StageExtractFriendInfo,
} from './stages';
import { softError, ErrorWithFields } from '../../log';

export type DataLoaderProps = {
children: React.ReactNode;
Expand Down Expand Up @@ -99,48 +110,70 @@ export default function DataLoader({
termScheduleData,
updateTermScheduleData,
}): React.ReactElement => (
<StageLoadOscarData
<GroupLoadFriendScheduleData
skeletonProps={{ termsState, accountState }}
term={currentTerm}
accountState={accountState}
currentTerm={currentTerm}
>
{({ oscar }): React.ReactElement => (
<StageExtractScheduleVersion
{({
friendScheduleData,
updateFriendTermData,
updateFriendInfo,
}): React.ReactElement => (
<StageLoadOscarData
skeletonProps={{ termsState, accountState }}
currentVersionRaw={currentVersionRaw}
setVersion={setVersion}
termScheduleData={termScheduleData}
updateTermScheduleData={
updateTermScheduleData
}
term={currentTerm}
>
{({
currentVersion,
scheduleVersion,
updateScheduleVersion,
}): React.ReactElement => (
<ContextProvider
terms={terms}
currentTerm={currentTerm}
setTerm={setTerm}
currentVersion={currentVersion}
{({ oscar }): React.ReactElement => (
<StageExtractScheduleVersion
skeletonProps={{
termsState,
accountState,
}}
currentVersionRaw={currentVersionRaw}
setVersion={setVersion}
oscar={oscar}
scheduleVersion={scheduleVersion}
updateScheduleVersion={
updateScheduleVersion
}
termScheduleData={termScheduleData}
updateTermScheduleData={
updateTermScheduleData
}
accountState={accountState}
>
{children}
</ContextProvider>
{({
currentVersion,
scheduleVersion,
updateScheduleVersion,
}): React.ReactElement => (
<ContextProvider
terms={terms}
currentTerm={currentTerm}
setTerm={setTerm}
currentVersion={currentVersion}
setVersion={setVersion}
oscar={oscar}
scheduleVersion={scheduleVersion}
updateScheduleVersion={
updateScheduleVersion
}
termScheduleData={termScheduleData}
updateTermScheduleData={
updateTermScheduleData
}
accountState={accountState}
friendScheduleData={
friendScheduleData
}
updateFriendTermData={
updateFriendTermData
}
updateFriendInfo={updateFriendInfo}
>
{children}
</ContextProvider>
)}
</StageExtractScheduleVersion>
)}
</StageExtractScheduleVersion>
</StageLoadOscarData>
)}
</StageLoadOscarData>
</GroupLoadFriendScheduleData>
)}
</StageExtractTermScheduleData>
)}
Expand Down Expand Up @@ -203,6 +236,87 @@ function GroupLoadScheduleData({
);
}

type GroupLoadFriendScheduleDataProps = {
skeletonProps?: StageSkeletonProps;
accountState: AccountContextValue;
currentTerm: string;
children: (props: {
friendScheduleData: Immutable<FriendScheduleData>;
updateFriendTermData: (
applyDraft: (
draft: Draft<FriendTermData>
) => void | Immutable<FriendTermData>
) => void;
updateFriendInfo: (
applyDraft: (draft: Draft<FriendInfo>) => void | Immutable<FriendInfo>
) => void;
}) => React.ReactNode;
};

function GroupLoadFriendScheduleData({
skeletonProps,
accountState,
currentTerm,
children,
}: GroupLoadFriendScheduleDataProps): React.ReactElement {
return (
<StageLoadRawFriendData
skeletonProps={skeletonProps}
accountState={accountState}
currentTerm={currentTerm}
>
{({ rawFriendData, setFriendData }): React.ReactElement => (
<StageCreateFriendDataProducer setFriendData={setFriendData}>
{({ updateFriendData }): React.ReactElement => (
<StageExtractFriendTermData
skeletonProps={skeletonProps}
accountState={accountState}
currentTerm={currentTerm}
rawFriendData={rawFriendData}
updateFriendData={updateFriendData}
>
{({
termFriendData,
updateFriendTermData,
}): React.ReactElement => (
<StageLoadRawFriendScheduleDataFromFirebaseFunction
skeletonProps={skeletonProps}
accountState={accountState}
currentTerm={currentTerm}
termFriendData={termFriendData}
>
{({ rawFriendScheduleData }): React.ReactElement => (
<StageExtractFriendInfo
skeletonProps={skeletonProps}
accountState={accountState}
rawFriendScheduleData={rawFriendScheduleData}
friendInfo={rawFriendData.info}
updateFriendData={updateFriendData}
>
{({
friendScheduleData,
updateFriendInfo,
}): React.ReactElement => (
<>
{children({
friendScheduleData,
updateFriendTermData,
updateFriendInfo,
})}
</>
)}
</StageExtractFriendInfo>
)}
</StageLoadRawFriendScheduleDataFromFirebaseFunction>
)}
</StageExtractFriendTermData>
)}
</StageCreateFriendDataProducer>
)}
</StageLoadRawFriendData>
);
}

type ContextProviderProps = {
terms: string[];
currentTerm: string;
Expand All @@ -223,6 +337,15 @@ type ContextProviderProps = {
) => void | Immutable<TermScheduleData>
) => void;
accountState: AccountContextValue;
friendScheduleData: Immutable<FriendScheduleData>;
updateFriendTermData: (
applyDraft: (
draft: Draft<FriendTermData>
) => void | Immutable<FriendTermData>
) => void;
updateFriendInfo: (
applyDraft: (draft: Draft<FriendInfo>) => void | Immutable<FriendInfo>
) => void;
children: React.ReactNode;
};

Expand All @@ -244,6 +367,9 @@ function ContextProvider({
termScheduleData,
updateTermScheduleData,
accountState,
friendScheduleData,
updateFriendTermData,
updateFriendInfo,
children,
}: ContextProviderProps): React.ReactElement {
// Create a `updateSchedule` function
Expand Down Expand Up @@ -293,6 +419,38 @@ function ContextProvider({
currentVersion,
});

// Create a rename friend function.
const renameFriend = useCallback(
(id: string, newName: string): void => {
updateFriendInfo((draft) => {
const existingDraft = draft[id];
if (existingDraft === undefined) {
softError(
new ErrorWithFields({
message:
"renameFriend called with current friend id that doesn't exist; ignoring",
fields: {
allFriendNames: Object.entries(draft).map(
([friendId, { name }]) => ({
id: friendId,
name,
})
),
id,
friendCount: Object.keys(draft).length,
newName,
},
})
);
return;
}

existingDraft.name = newName;
});
},
[updateFriendInfo]
);

// Memoize the context values so that they are stable
const scheduleContextValue = useMemo<ScheduleContextValue>(
() => [
Expand Down Expand Up @@ -331,11 +489,27 @@ function ContextProvider({
]
);

const friendContextValue = useMemo<FriendContextValue>(
() => [
{
friends: friendScheduleData,
},
{
renameFriend,
updateFriendTermData,
updateFriendInfo,
},
],
[friendScheduleData, renameFriend, updateFriendTermData, updateFriendInfo]
);

return (
<TermsContext.Provider value={terms}>
<ScheduleContext.Provider value={scheduleContextValue}>
<AccountContext.Provider value={accountState}>
{children}
<FriendContext.Provider value={friendContextValue}>
{children}
</FriendContext.Provider>
</AccountContext.Provider>
</ScheduleContext.Provider>
</TermsContext.Provider>
Expand Down
Loading