Преглед изворни кода

fix issue where updating contact list vigorously would crash the server

Ramona Plogmann пре 1 година
родитељ
комит
58d9344daf

+ 30 - 13
client/src/components/Social/FriendOrUnfriendButton.jsx

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from "react";
 import { useAuth0, withAuthenticationRequired } from "@auth0/auth0-react";
 import Loading from "../Loading";
-import { IconButton } from "@material-ui/core";
+import { CircularProgress, IconButton } from "@material-ui/core";
 import { PersonAdd, PersonAddDisabled } from "@material-ui/icons";
 import { makeStyles } from "@material-ui/styles";
 import { array, bool, func, object } from "prop-types";
@@ -18,33 +18,42 @@ const useStyles = makeStyles(theme => ({
       backgroundColor: theme.palette.background.default,
     }
   },
-  addFriend: {
-    color: theme.palette.primary.main,
-  },
-  removeFriend: {
-    color: theme.palette.error.main,
-  },
+  loadingCircle: {
+    position: 'absolute',
+    top: '50%',
+    left: '50%',
+    marginTop: '-12px',
+    marginLeft: '-12px',
+  }
 }));
 
 /** Button that will toggle whether given contact is included in current user's contacts array. */
 const FriendOrUnfriendButton = (props) => {
   const classes = useStyles();
-  const { otherUser, afterUpdateContacts, contacts: givenContacts } = props;
+  const { otherUser, afterUpdateContacts, contacts: givenContacts, isSaving: isSavingSomething, setIsSaving: setIsSavingInParentComponent } = props;
   const { user } = useAuth0();
 
   const [contacts, setContacts] = useState(givenContacts);
   const [isContact, setIsContact] = useState(false);
+  const [isSavingThisOne, setIsSavingThisOne] = useState(false);
   const [checkedIsContact, setCheckedIsContact] = useState(!!givenContacts);
 
   const updateContacts = (newContacts) => {
-    if (user) {
+    if (user && !isSavingSomething && !isSavingThisOne) {
+      setIsSaving(true);
       updateUserContacts(user.sub, newContacts, (updatedContacts) => {
-        if (afterUpdateContacts) afterUpdateContacts(updatedContacts);
+        setIsSaving(false);
+        if (afterUpdateContacts) afterUpdateContacts();
         setContacts(updatedContacts);
       });
     }
   }
 
+  const setIsSaving = (value) => {
+    setIsSavingThisOne(value);
+    if (setIsSavingInParentComponent) setIsSavingInParentComponent(value);
+  };
+
   const fetchContacts = () => {
     if (user) {
       const userId = user.sub;
@@ -78,10 +87,13 @@ const FriendOrUnfriendButton = (props) => {
   if (!checkedIsContact) return null;
 
   return (
-    <IconButton edge="end" className={(props.inCircle) ? classes.iconButtonInCircle : ''} onClick={() => isContact ? removeFriend() : addFriend()}>
+    <IconButton disabled={isSavingSomething || isSavingThisOne} edge="end" className={(props.inCircle) ? classes.iconButtonInCircle : ''} onClick={() => (isContact && !isSavingSomething) ? removeFriend() : addFriend()}>
       {isContact
-        ? <PersonAddDisabled className={classes.removeFriend} />
-        : <PersonAdd className={classes.addFriend} />}
+        ? <PersonAddDisabled color={isSavingSomething || isSavingThisOne ? "disabled" : "error"} />
+        : <PersonAdd color={isSavingSomething || isSavingThisOne ? "disabled" : "primary"} />}
+      {isSavingThisOne && (
+        <CircularProgress size={24} color="inherit" className={classes.loadingCircle} />
+      )}
     </IconButton>
   );
 }
@@ -95,12 +107,17 @@ FriendOrUnfriendButton.propTypes = {
   contacts: array,
   /** optional styling that displays button in a white circle */
   inCircle: bool,
+  /** optional styling that displays button in a white circle */
+  isSaving: bool,
+  /** function to change isSaving in parent component */
+  setIsSaving: func,
 }
 
 FriendOrUnfriendButton.defaultProps = {
   afterUpdateContacts: null,
   contacts: null,
   inCircle: false,
+  isSaving: false,
 }
 
 export default withAuthenticationRequired(FriendOrUnfriendButton, {

+ 2 - 2
client/src/components/Social/Social.jsx

@@ -36,14 +36,14 @@ const Social = () => {
   const [contacts, setContacts] = useState([]);
   const [noContactsFound, setNoContactsFound] = useState(false);
 
-  const fetchContacts = (updateContactsData = false) => {
+  const fetchContacts = (shouldUpdateContactsData = false) => { // at the first render, update contacts once from database, all the subsequent times not.
     if (user) {
       const userId = user.sub;
       fetchContactsOfUser(userId, (contactsFound) => {
         setContacts(contactsFound);
         if (contactsFound.length === 0) {
           setNoContactsFound(true);
-        } else if (updateContactsData) updateContactsFromAuth0(userId, contactsFound.map(c => c.user_id), setContacts);
+        } else if (shouldUpdateContactsData === true) updateContactsFromAuth0(userId, contactsFound.map(c => c.user_id), setContacts);
       });
     }
   }

+ 5 - 4
client/src/components/Social/UserSearch.jsx

@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
 import SimpleCloseX from "../Buttons/SimpleCloseX";
 import { arrayOf, bool, func, shape } from "prop-types";
 import FriendOrUnfriendButton from "./FriendOrUnfriendButton";
-import { ContactAvatar, getContactName, getContactPicture } from "./social.util";
+import { ContactAvatar, getContactName, getContactPicture, updateUserContacts } from "./social.util";
 
 const useStyles = makeStyles(theme => ({
   userSearchBox: {
@@ -77,10 +77,11 @@ const UserSearch = (props) => {
   const { user } = useAuth0();
   const { t } = useTranslation();
 
+  const { closeSearch, open, contacts, afterUpdateContacts, openContact } = props;
+
   const [userList, setUserList] = useState([]);
   const [query, setQuery] = useState('');
-
-  const { closeSearch, open, contacts, afterUpdateContacts, openContact } = props;
+  const [isUpdatingContactList, setIsUpdatingContactList] = useState(false);
 
   const getUsers = () => {
     if (query) {
@@ -124,7 +125,7 @@ const UserSearch = (props) => {
             </ListItemAvatar>
             <ListItemText primary={getContactName(u)} primaryTypographyProps={{ className: classes.listItemText }} />
             <ListItemSecondaryAction>
-              <FriendOrUnfriendButton otherUser={u} contacts={contacts} afterUpdateContacts={afterUpdateContacts} />
+              <FriendOrUnfriendButton otherUser={u} contacts={contacts} afterUpdateContacts={afterUpdateContacts} isSaving={isUpdatingContactList} setIsSaving={setIsUpdatingContactList} />
             </ListItemSecondaryAction>
           </ListItem>
           <Divider />

+ 1 - 1
client/src/components/Social/social.util.jsx

@@ -44,7 +44,7 @@ export const updateContactsFromAuth0 = (userId, contactIDs, updateContacts) => {
 export const updateUserContacts = (userId, newContacts, updateContacts) => {
   if (userId) {
     updateUserSettingsForCategory(userId, 'contacts', newContacts, (settings) => {
-      const sortedContacts = settings.contacts.sort((a, b) => a.name.toUpperCase() > b.name.toUpperCase() ? 1 : -1)
+      const sortedContacts = settings.contacts.sort((a, b) => a.name.toUpperCase() > b.name.toUpperCase() ? 1 : -1);
       updateContacts(sortedContacts);
     });
   }