Read / Fetch



API Redux Toolkit Query (RTK)

  • File: app\containers\SampleFirebaseApps\Email\services\emailApi.js
  • Call load fetch action in API function
                      
    fetchMails: builder.query({
      queryFn: async () => {
        const emailRef = child(dbRef, 'emails/');
        const querySnapshot = await get(emailRef).then((snapshot) => {
          if (snapshot.exists()) {
            return snapshot.val();
          }
          return false;
        }).catch((error) => error);
    
        const emails = Object.keys(querySnapshot).map(key => ({
          id: key,
          ...querySnapshot[key]
        }));
        return { data: emails.reverse() };
      },
      providesTags: ['Email'],
    }),
                      
                    
  • Refetch data changes
                      
    refetchEmail: builder.mutation({
      queryFn: async () => ({
        data: null
      }),
      invalidatesTags: ['Email'],
    }),
    
                      
                    

Reducer/Slice

  • File: app\containers\SampleFirebaseApps\Email\slice\emailSlice.js
  • Set local state to load items in front-end side
                      
    fetchData: (state, action) => {
      const items = action.payload;
      state.inbox = items;
    },
                      
                    

Container

  • File: \app\containers\SampleFirebaseApps\Email\index.js
  • Load state data to container
                      
    import { useFetchMailsQuery } from './services/emailApi';                  
    import { fetchData } from './reducers/emailSlice';                  
    
    const emailData = useSelector(state => state.emailFb.inbox);
    
    const { data, isLoading } = useFetchMailsQuery();
    
    const dispatch = useDispatch();
    
    useEffect(() => {
      dispatch(fetchData(data));
    }, [data]);
      
    return {
      <EmailList
        loading={isLoading}
        emailData={emailData}
        openMail={(payload) => dispatch(open(payload))}
        filterPage={currentPage}
        keyword={keyword}
        moveTo={(mail, category) => handleMove({ mail, category })}
        remove={(mail) => handleDelete(mail)}
        toggleStar={(mail) => handleToggle(mail)}
        reply={handleReply}
      />
    }
                      
                    

Components

  • File: app\components\Email\EmailList.js
  • Populate emailData prop as dataArray to component
                      
    const getItem = dataArray => dataArray.map(data => {
      const renderHTML = { __html: mail.content };
      if (mail.subject.toLowerCase().indexOf(keyword) === -1) {
        return false;
      }
      return (
        <ExpansionPanel className={classes.emailList} key={mail.id} onChange={() => openMail(mail)}>
          <ExpansionPanelSummary className={classes.emailSummary} expandIcon={<ExpandMoreIcon />}>
            <div className={classes.fromHeading}>
              <Tooltip id="tooltip-mark" title={intl.formatMessage(messages.stared)}>
                <IconButton onClick={() => this.handleStared(mail)} className={classes.starBtn}>{mail.stared ? (<Star className={classes.iconOrange} />) : (<StarBorder />) }</IconButton>
              </Tooltip>
              {mail.category !== 'spam'
                ? (<Avatar alt="avatar" src={mail.avatar} className={classes.avatar} />)
                : (<Avatar alt="avatar" className={classes.avatar}><ReportIcon /></Avatar>)
              }
              <Typography className={classes.heading}>
                {mail.category === 'sent' && ('To ')}
                {mail.name}
                <Typography variant="caption">{mail.date}</Typography>
              </Typography>
            </div>
            <div className={classes.column}>
              <Typography className={classes.secondaryHeading} noWrap>{mail.subject}</Typography>
              {getCategory(mail.category)}
            </div>
          </ExpansionPanelSummary>
          <ExpansionPanelDetails className={classes.details}>
            <section>
              <div className={classes.topAction}>
                <Typography className={classes.headMail}>
                  {mail.category !== 'sent' && (
                    <Fragment>
                      <FormattedMessage {...messages.from} />
                       
                      {mail.name}
                       to me
                    </Fragment>
                  )}
                </Typography>
                <div className={classes.opt}>
                  <Tooltip id="tooltip-mark" title={intl.formatMessage(messages.stared)}>
                    <IconButton onClick={() => this.handleStared(mail)}>{mail.stared ? (<Star className={classes.iconOrange} />) : (<StarBorder />) }</IconButton>
                  </Tooltip>
                  <Tooltip id="tooltip-mark" title={intl.formatMessage(messages.mark_to)}>
                    <IconButton
                      className={classes.button}
                      aria-label="mark"
                      aria-owns={anchorElOpt ? 'long-menu' : null}
                      aria-haspopup="true"
                      onClick={(event) => this.handleClickOpt(event, mail)}
                    >
                      <Bookmark />
                    </IconButton>
                  </Tooltip>
                  <Tooltip id="tooltip-mark" title={intl.formatMessage(messages.remove)}>
                    <IconButton className={classes.button} aria-label="Delete" onClick={() => remove(mail)}><Delete /></IconButton>
                  </Tooltip>
                </div>
              </div>
              <div className={classes.emailContent}>
                <Typography variant="h6" gutterBottom>{mail.subject}</Typography>
                <article dangerouslySetInnerHTML={renderHTML} />
              </div>
            </section>
          </ExpansionPanelDetails>
          <Divider />
          <ExpansionPanelActions>
            <div className={classes.action}>
              <Button size="small">
                <FormattedMessage {...messages.forward} />
              </Button>
              <Button size="small" color="secondary" onClick={() => reply(mail)}>
                <FormattedMessage {...messages.reply} />
              </Button>
            </div>
          </ExpansionPanelActions>
        </ExpansionPanel>
      );
    });
                      
                    


Create (Send)



API Redux Toolkit Query (RTK)

  • File: app\containers\SampleFirebaseApps\Email\services\emailApi.js
  • Create send email action in API function
                      
    sendMail: builder.mutation({
      queryFn: async (payload) => {
        const { name, subject, content } = payload;
        const newMail = buildMessage(name, subject, content);
        const mailId = (+new Date() + Math.floor(Math.random() * 999999)).toString(36);
    
        set(ref(db, 'emails/' + mailId), newMail);
        return { data: null };
      },
      invalidatesTags: ['Email'],
    }),     
                      
                    

Reducer/Slice

  • File: app\containers\SampleFirebaseApps\Email\slice\emailSlice.js
  • Set local state to update UI changes
                      
    send: (state) => {
      state.selectedMail = '';
      state.openFrm = false;
    }, 
                      
                    

Container

  • File: app\containers\SampleFirebaseApps\Email\index.js
  • Dispatch send action in container
                      
    import { useSendMailMutation } from './services/emailApi';    
    import { send } from './reducers/emailSlice';    
    
    const [sendMail, { isLoading: sendingMail }] = useSendMailMutation();
    
    const handleSend = (payload) => {
      sendMail(payload);
      dispatch(send());
      setMessage(notif.sent);
    };
    
    return (
      <ComposeEmail
        to={field.to}
        subject={field.subject}
        compose={handleCompose}
        validMail={validMail}
        sendEmail={(payload) => handleSend(payload)}
        inputChange={(e, name) => handleChange(e, name)}
        open={openFrm}
        closeForm={() => dispatch(discard())}
        processing={sendingMail}
      />
    )
                      
                    

Component

  • File: app\components\Email\ComposeEmail.js
  • Send email values by onClick send button
                      
    handleSend = message => {
      const { sendEmail } = this.props;
      sendEmail(message);
      this.setState({ emailContent: '', files: [] });
    };
    
    return (
      <Button
        variant="contained"
        color="secondary"
        type="button"
        disabled={!to || !subject || processing}
        onClick={() => handleSend(newData)}
      >
        {processing && <CircularProgress size={24} className={classes.buttonProgress} />}
        <FormattedMessage {...messages.send} />
         
        <Send className={classes.sendIcon} />
      </Button>
    )
                      
                    


Update



API Redux Toolkit Query (RTK)

  • File: app\containers\SampleFirebaseApps\Email\services\emailServices.js
  • Create update email action in API function
                      
    toggleStar: builder.mutation({
      queryFn: async (mail) => {
        const updates = {};
        updates['/emails/' + mail.id] = { ...mail, stared: !mail.stared };
        update(ref(db), updates);
    
        return { data: null };
      },
      invalidatesTags: ['Email'],
    }),
    moveMail: builder.mutation({
      queryFn: async (payload) => {
        const { mail, category } = payload;
        const updates = {};
    
        updates['/emails/' + mail.id] = { ...mail, category };
        await update(ref(db), updates)
          .then(() => {
            console.log('changes success');
          }).catch((error) => {
            console.log(error);
          });
    
        return { data: null };
      },
      invalidatesTags: ['Email'],
    })
                      
                    

Container

  • File: app\containers\SampleFirebaseApps\Email\index.js
  • Dispatch update action in container.
                      
    import { useToggleStarMutation, useMoveMailMutation } from './services/emailApi';
    
    const [toggleStar] = useToggleStarMutation();
    const [moveMail] = useMoveMailMutation();
    
    const handleToggle = (mail) => {
      toggleStar(mail);
    };
    
    const handleMove = (payload) => {
      moveMail(payload);
      setMessage(notif.labeled);
    };
    
    return (
      <EmailList
        loading={isLoading}
        emailData={emailData}
        openMail={(payload) => dispatch(open(payload))}
        filterPage={currentPage}
        keyword={keyword}
        moveTo={(mail, category) => handleMove({ mail, category })}
        remove={(mail) => handleDelete(mail)}
        toggleStar={(mail) => handleToggle(mail)}
        reply={handleReply}
      />
    )
                      
                    

Component

  • File: app\components\Email\EmailList.js
  • Push updated Email attribute
                      
    const handleMoveTo = (item, category) => {
      moveTo(item, category);
      setAnchorElOpt(null);
    };
    
    const handleStared = mail => {
      toggleStar(mail, { stared: !mail.stared });
    };
    
    return (
      <IconButton onClick={() => handleStared(mail)} className={classes.starBtn}>{mail.stared ? (<Star className={classes.iconOrange} />) : (<StarBorder />) }</IconButton>
      
      <MenuItem selected onClick={() => handleMoveTo(itemToMove, 'updates')}>
        <Flag className={classes.iconOrange} />
         
        <FormattedMessage {...messages.updates} />
      </MenuItem>
    )
                      
                    


Delete



API Redux Toolkit Query (RTK)

  • File: app\containers\SampleFirebaseApps\Email\services\emailApi.js
  • Create remove email action in API function
                      
    deleteMail: builder.mutation({
      queryFn: async (mail) => {
        const mailId = mail.id;
    
        await remove(ref(db, 'emails/' + mailId))
          .then(() => {
            console.log('delete success');
          }).catch((error) => {
            console.log(error);
          });
        return { data: null };
      },
      invalidatesTags: ['Email'],
    }),
                      
                    

Container

  • File: app\containers\SampleFirebaseApps\Email\index.js
  • Dispatch remove action in container
                      
    import { useDeleteMailMutation } from './services/emailApi';
    
    const [deleteMail] = useDeleteMailMutation();
    
    const handleDelete = (mail) => {
      deleteMail(mail);
      setMessage(notif.removed);
    };
    
    return (
      <EmailList
        loading={isLoading}
        emailData={emailData}
        openMail={(payload) => dispatch(open(payload))}
        filterPage={currentPage}
        keyword={keyword}
        moveTo={(mail, category) => handleMove({ mail, category })}
        remove={(mail) => handleDelete(mail)}
        toggleStar={(mail) => handleToggle(mail)}
        reply={handleReply}
      />
    )
    
                      
                    

Component

  • File: app/components/Email/EmailList.js
  • Call remove function by onClick remove button
                      
    return (
      <IconButton className={classes.button} aria-label="Delete" onClick={() => remove(mail)}><Delete /></IconButton>
    )