Skip to content

Commit

Permalink
merge base
Browse files Browse the repository at this point in the history
  • Loading branch information
yatharth-b committed Mar 23, 2024
2 parents e1385b2 + e5933ec commit d330505
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 143 deletions.
139 changes: 69 additions & 70 deletions src/components/ComparisonContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ErrorWithFields, softError } from '../../log';
import { CLOUD_FUNCTION_BASE_URL } from '../../constants';
import InvitationModal from '../InvitationModal';
import ComparisonContainerShareBack from '../ComparisonContainerShareBack/ComparisonContainerShareBack';
import { ScheduleDeletionRequest } from '../../types';

import './stylesheet.scss';

Expand Down Expand Up @@ -89,8 +90,7 @@ export default function ComparisonContainer({
{ deleteVersion, renameVersion, patchSchedule },
] = useContext(ScheduleContext);

const [{ friends }, { updateFriendTermData, renameFriend }] =
useContext(FriendContext);
const [{ friends }, { renameFriend }] = useContext(FriendContext);

const accountContext = useContext(AccountContext);

Expand Down Expand Up @@ -147,98 +147,97 @@ export default function ComparisonContainer({
setEditValue('');
}, [editInfo, editValue, renameFriend, renameVersion]);

const deleteInvitation = useCallback(
const deleteSchedulesFromInvitee = useCallback(
async (senderId: string, versions: string[]) => {
const data = JSON.stringify({
IDToken: await (accountContext as SignedIn).getToken(),
senderId,
peerUserId: senderId,
term,
versions,
});
axios
.post(
`${CLOUD_FUNCTION_BASE_URL}/deleteInvitationFromFriend`,
`data=${data}`,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
)
.catch((err) => {
softError(
new ErrorWithFields({
message: 'delete sender record failed',
source: err,
fields: {
user: (accountContext as SignedIn).id,
sender: senderId,
term,
versions,
owner: false,
} as ScheduleDeletionRequest);

const friend = friends[senderId];
if (friend) {
axios
.post(
`${CLOUD_FUNCTION_BASE_URL}/deleteSharedSchedule`,
`data=${data}`,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
);
});
}
)
.then(() => {
const newColorMap = { ...colorMap };
versions.forEach((schedule) => {
delete newColorMap[schedule];
});
setSelected(
selected.filter(
(selectedId: string) =>
!Object.keys(friend.versions).includes(selectedId)
)
);
patchSchedule({ colorMap: newColorMap });
// updateFriendTermData((draft) => {
// delete draft.accessibleSchedules[senderId];
// });
})
.catch((err) => {
throw err;
});
}
},
[accountContext, term]
[accountContext, term, colorMap, friends, patchSchedule, selected]
);

// remove all versions of a particular friend from user (invitee) view
const handleRemoveFriend = useCallback(
(ownerId: string) => {
const friend = friends[ownerId];
if (friend) {
const newColorMap = { ...colorMap };

const versions = Object.keys(friend.versions);
versions.forEach((schedule) => {
delete newColorMap[schedule];
});
setSelected(
selected.filter(
(selectedId: string) =>
!Object.keys(friend.versions).includes(selectedId)
)
);
patchSchedule({ colorMap: newColorMap });
updateFriendTermData((draft) => {
delete draft.accessibleSchedules[ownerId];
});

// eslint-disable-next-line @typescript-eslint/no-floating-promises
deleteInvitation(ownerId, versions);
deleteSchedulesFromInvitee(ownerId, versions).catch((err) => {
softError(
new ErrorWithFields({
message: 'Failed to delete user schedule',
source: err,
fields: {
user: (accountContext as SignedIn).id,
sender: ownerId,
term,
versions,
},
})
);
});
}
},
[
friends,
selected,
colorMap,
patchSchedule,
updateFriendTermData,
deleteInvitation,
]
[friends, deleteSchedulesFromInvitee, accountContext, term]
);

const handleRemoveSchedule = useCallback(
(id: string, ownerId: string) => {
updateFriendTermData((draft) => {
if (draft.accessibleSchedules[ownerId]?.length === 1) {
delete draft.accessibleSchedules[ownerId];
} else {
draft.accessibleSchedules[ownerId] =
draft.accessibleSchedules[ownerId]?.filter(
(schedule) => schedule !== id
) ?? [];
}
deleteSchedulesFromInvitee(ownerId, [id]).catch((err) => {
softError(
new ErrorWithFields({
message: 'Failed to delete user schedule',
source: err,
fields: {
user: (accountContext as SignedIn).id,
sender: ownerId,
term,
versions: [id],
},
})
);
});
const newColorMap = { ...colorMap };
delete newColorMap[id];
patchSchedule({ colorMap: newColorMap });
setSelected(selected.filter((selectedId: string) => selectedId !== id));

// eslint-disable-next-line @typescript-eslint/no-floating-promises
deleteInvitation(ownerId, [id]);
},
[selected, colorMap, updateFriendTermData, patchSchedule, deleteInvitation]
[deleteSchedulesFromInvitee, accountContext, term]
);

const handleToggleSchedule = useCallback(
Expand Down
163 changes: 126 additions & 37 deletions src/components/InvitationAcceptModal/InvitationAcceptModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useLocalStorageState from 'use-local-storage-state';
import Button from '../Button';
import Modal from '../Modal';
import InvitationModal from '../InvitationModal';
import LoginModal from '../LoginModal';

import './stylesheet.scss';

Expand All @@ -25,6 +26,7 @@ export default function InvitationAcceptModal({
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [invitationModalOpen, setInvitationModalOpen] =
useState<boolean>(false);
const [loginModalOpen, setLoginModalOpen] = useState<boolean>(false);
const [searchParams] = useSearchParams();

const [hasSeen, setHasSeen] = useLocalStorageState(
Expand Down Expand Up @@ -63,47 +65,134 @@ export default function InvitationAcceptModal({
}}
inputEmail={searchParams.get('email') ?? undefined}
/>
<Modal show={modalOpen} onHide={onHide} width={700}>

<LoginModal
show={loginModalOpen}
onHide={(): void => {
setLoginModalOpen(false);
}}
/>

<Modal
show={modalOpen}
onHide={onHide}
width={700}
buttons={
searchParams.get('status') === 'not-logged-in'
? [
{
label: 'Login',
onClick: (): void => {
onHide();
setLoginModalOpen(true);
},
},
]
: undefined
}
>
<Button className="remove-close-button" onClick={onHide}>
<FontAwesomeIcon icon={faXmark} size="xl" />
</Button>
<div className="invitation-accept-modal-content">
<div className="heading">
{searchParams.get('status') === 'success'
? 'You have successfully added a new schedule to your view!'
: 'Failed to add the schedule, please ask the user for a new invite.'}
</div>
{searchParams.get('status') === 'success' ? (
<>
<div className="sub-heading">
You will now be able to see {searchParams.get('email')}&apos;s
schedule!
</div>

<img src="/scheduled.png" alt="ok" className="modal-image" />
<div className="modal-bottom">
<div>Would you like to share your schedule back?</div>

<div className="button-row">
<button type="submit" className="no-button" onClick={onHide}>
No
</button>
<button
type="submit"
className="share-button"
onClick={(): void => {
onHide();
setInvitationModalOpen(true);
}}
>
Share Back
</button>
</div>
</div>
</>
) : null}
</div>
{searchParams.get('status') === 'success' ? (
<SuccessContent
email={searchParams.get('email') ?? ''}
onHide={onHide}
setInvitationModalOpen={setInvitationModalOpen}
/>
) : (
<FailureContent error={searchParams.get('status') ?? ''} />
)}
</Modal>
</>
);
}

type SuccessContentProps = {
email: string;
onHide: () => void;
setInvitationModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
};

function SuccessContent({
email,
onHide,
setInvitationModalOpen,
}: SuccessContentProps): React.ReactElement {
return (
<div className="invitation-accept-modal-content">
<div className="heading">
You have successfully added a new schedule to your view!
</div>
<div className="sub-heading">
You will now be able to see {email}&apos;s schedule!
</div>

<img src="/scheduled.png" alt="ok" className="modal-image" />
<div className="modal-bottom">
<div>Would you like to share your schedule back?</div>

<div className="button-row">
<button type="submit" className="no-button" onClick={onHide}>
No
</button>
<button
type="submit"
className="share-button"
onClick={(): void => {
onHide();
setInvitationModalOpen(true);
}}
>
Share Back
</button>
</div>
</div>
</div>
);
}

type FailureContentProps = {
error: string;
};

function FailureContent({ error }: FailureContentProps): React.ReactElement {
return (
<div className="invitation-accept-modal-content">
<img src="/mascot.png" alt="buzz" className="buzz-image" />
<div className="heading">Failed to add new schedule</div>
<div className="error-sub-heading">
{error === 'invalid-invite'
? 'Invalid Invite'
: error === 'invite-expired'
? 'Invite Expired'
: error === 'not-logged-in'
? 'Not Logged In'
: "Something's wrong here.."}
</div>
<div className="error-message">
{error === 'invalid-invite' ? (
<span>
The invite request is <u>invalid</u>, please ask the user for a new
invite.
</span>
) : error === 'invite-expired' ? (
<span>
The invite request has <u>expired</u>, please ask the user for a new
invite.
</span>
) : error === 'not-logged-in' ? (
<span>
Login and click on the invite link again to add your friend&apos;s
schedule to your view.
</span>
) : (
<span>
An unknown error occurred on our end, please ask the user for a new
invite!
</span>
)}
</div>
</div>
);
}
Loading

0 comments on commit d330505

Please sign in to comment.