app\containers\SampleFullstackApps\Email\reducers\emailConstants.js
export const FETCH_EMAIL = 'FETCH_EMAIL';
export const FETCH_EMAIL_SUCCESS = 'FETCH_EMAIL_SUCCESS';
export const FETCH_EMAIL_FAILED = 'FETCH_EMAIL_FAILED';
app\containers\SampleFullstackApps\Email\reducers\emailActions.js
/* Load */
export const loadMailSuccess = payload => ({
type: types.FETCH_EMAIL_SUCCESS,
payload,
});
app\containers\SampleFullstackApps\Email\reducers\emailSagas.js
function* read() {
const channel = yield call(subscribe);
while (true) {
const action = yield take(channel);
yield put(action);
}
}
function* watchEmail() {
while (true) {
emailList.path = 'emails/';
const job = yield fork(read);
yield take([FETCH_EMAIL]);
yield cancel(job);
}
}
import EmailModels from '../models';
import Init from '../models/init';
import { loadMailSuccess } from './emailActions';
const emailList = new EmailModels({
onLoad: loadMailSuccess,
}, Init);
function subscribe() {
return eventChannel(emit => emailList.subscribe(emit));
}
app\containers\SampleFullstackApps\Email\reducers\emailReducer.js
case FETCH_EMAIL_SUCCESS:
return state.withMutations((mutableState) => {
const items = fromJS(action.payload);
mutableState
.set('inbox', new List(items))
.set('loading', false);
});
\app\containers\SampleFullstackApps\Email\index.js
import { getVisibleMail } from './reducers/selectors';
const { emailData } = this.props;
const mapStateToProps = state => ({
emailData: getVisibleMail(state.get(reducer)),
});
return {
<EmailList
emailData={emailData}
openMail={openMail}
filterPage={currentPage}
keyword={keyword}
moveTo={update}
remove={remove}
toggleStar={update}
reply={this.handleReply}
loading={loading}
/>
}
app\components\Email\EmailList.js
const getItem = dataArray => dataArray.map(data => {
const renderHTML = { __html: mail.get('content') };
if (mail.get('subject').toLowerCase().indexOf(keyword) === -1) {
return false;
}
return (
<ExpansionPanel className={classes.emailList} key={mail.get('key')} 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.get('stared') ? (<Star className={classes.iconOrange} />) : (<StarBorder />) }</IconButton>
</Tooltip>
{mail.get('category') !== 'spam'
? (<Avatar alt="avatar" src={mail.get('avatar')} className={classes.avatar} />)
: (<Avatar alt="avatar" className={classes.avatar}><ReportIcon /></Avatar>)
}
<Typography className={classes.heading}>
{mail.get('category') === 'sent' && ('To ')}
{mail.get('name')}
<Typography variant="caption">{mail.get('date')}</Typography>
</Typography>
</div>
<div className={classes.column}>
<Typography className={classes.secondaryHeading} noWrap>{mail.get('subject')}</Typography>
{getCategory(mail.get('category'))}
</div>
</ExpansionPanelSummary>
<ExpansionPanelDetails className={classes.details}>
<section>
<div className={classes.topAction}>
<Typography className={classes.headMail}>
{mail.get('category') !== 'sent' && (
<Fragment>
<FormattedMessage {...messages.from} />
{mail.get('name')}
to me
</Fragment>
)}
</Typography>
<div className={classes.opt}>
<Tooltip id="tooltip-mark" title={intl.formatMessage(messages.stared)}>
<IconButton onClick={() => this.handleStared(mail)}>{mail.get('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.get('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>
);
});
app\containers\SampleFullstackApps\Email\reducers\emailConstants.js
export const SEND_MAIL = 'SEND_MAIL';
export const SEND_MAIL_SUCCESS = 'SEND_MAIL_SUCCESS';
export const SEND_MAIL_FAILED = 'SEND_MAIL_FAILED';
app\containers\SampleFullstackApps\Email\reducers\emailActions.js
export const sendAction = payload => ({
type: types.SEND_MAIL,
payload,
});
export const sendActionSuccess = payload => ({
type: types.SEND_MAIL_SUCCESS,
payload,
});
export const sendActionFailed = error => ({
type: types.SEND_MAIL_FAILED,
payload: { error },
});
app\containers\SampleFullstackApps\Email\reducers\emailSagas.js
const sendMail = write.bind(null, emailList, emailList.push, sendActionFailed);
function* watchSendEmail() {
while (true) {
const { payload } = yield take(SEND_MAIL);
yield fork(sendMail, payload);
}
}
app\containers\SampleFullstackApps\Email\reducers\emailReducer.js
case SEND_MAIL_SUCCESS:
return state.withMutations((mutableState) => {
const items = action.payload;
mutableState.set('inbox', state.get('inbox').unshift(items));
mutableState
.set('selectedMailId', '')
.set('openFrm', false)
.set('notifMsg', notif.sent)
.set('processing', false);
});
app\containers\SampleFullstackApps\Email\index.js
import { sendAction } from './reducers/emailActions';
const constDispatchToProps = dispatch => ({
sendEmail: bindActionCreators(sendAction, dispatch),
});
return (
<ComposeEmail
to={to}
subject={subject}
compose={this.handleCompose}
validMail={validMail}
sendEmail={sendEmail}
inputChange={this.handleChange}
open={openFrm}
closeForm={discard}
processing={processing}
/>
)
app\components\Email\ComposeEmail.js
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={() => this.handleSend(newData)}
>
{processing && <CircularProgress size={24} className={classes.buttonProgress} />}
<FormattedMessage {...messages.send} />
<Send className={classes.sendIcon} />
</Button>
)
app\containers\SampleFullstackApps\Email\reducers\emailConstants.js
export const UPDATE_MAIL = 'UPDATE_MAIL';
export const UPDATE_MAIL_SUCCESS = 'UPDATE_MAIL_SUCCESS';
export const UPDATE_MAIL_FAILED = 'UPDATE_MAIL_FAILED';
app\containers\SampleFullstackApps\Email\reducers\emailActions.js
export const updateAction = (mail, changes) => ({
type: types.UPDATE_MAIL,
payload: { mail, changes },
});
export const updateActionSuccess = payload => ({
type: types.UPDATE_MAIL_SUCCESS,
payload,
});
export const updateActionFailed = error => ({
type: types.UPDATE_MAIL_SUCCESS,
payload: { error },
});
app\containers\SampleFullstackApps\Email\reducers\emailSagas.js
const updateMail = write.bind(null, emailList, emailList.update, updateActionFailed);
function* watchUpdateEmail() {
while (true) {
const { payload } = yield take(UPDATE_MAIL);
yield fork(updateMail, payload.mail.key, payload.changes);
}
}
app\containers\SampleFullstackApps\Email\reducers\emailReducer.js
case UPDATE_MAIL_SUCCESS:
return state.withMutations((mutableState) => {
mutableState
.set(
'inbox',
state.get('inbox').map(mail => (mail.key === action.payload.key ? action.payload : mail))
)
.set('notifMsg', notif.updated);
});
app\containers\SampleFullstackApps\Email\index.js
import { updateAction } from './reducers/emailActions';
const constDispatchToProps = dispatch => ({
update: bindActionCreators(updateAction, dispatch),
});
return (
<EmailList
emailData={emailData}
openMail={openMail}
filterPage={currentPage}
keyword={keyword}
moveTo={update}
remove={remove}
toggleStar={update}
reply={this.handleReply}
loading={loading}
/>
)
app\components\Email\EmailList.js
handleMoveTo = (item, category) => {
const { moveTo } = this.props;
moveTo(item, { category });
this.setState({ anchorElOpt: null });
}
handleStared = mail => {
const { toggleStar } = this.props;
toggleStar(mail, { stared: !mail.get('stared') });
}
return (
<IconButton onClick={() => this.handleStared(mail)} className={classes.starBtn}>{mail.get('stared') ? (<Star className={classes.iconOrange} />) : (<StarBorder />) }</IconButton>
<MenuItem selected onClick={() => this.handleMoveTo(itemToMove, 'updates')}>
<Flag className={classes.iconOrange} />
<FormattedMessage {...messages.updates} />
</MenuItem>
)
app\containers\SampleFullstackApps\Email\reducers\emailConstants.js
export const DELETE_MAIL = 'DELETE_MAIL';
export const DELETE_MAIL_SUCCESS = 'DELETE_MAIL_SUCCESS';
export const DELETE_MAIL_FAILED = 'DELETE_MAIL_FAILED';
app\containers\SampleFullstackApps\Email\reducers\emailActions.js
/* Delete */
export const deleteAction = payload => ({
type: types.DELETE_MAIL,
payload,
});
export const deleteActionSuccess = payload => ({
type: types.DELETE_MAIL_SUCCESS,
payload,
});
export const deleteActionFailed = error => ({
type: types.DELETE_MAIL_FAILED,
payload: { error },
});
app\containers\SampleFullstackApps\Email\reducers\emailSagas.js
const removeMail = write.bind(null, emailList, emailList.remove, deleteActionFailed);
function* watchRemoveEmail() {
while (true) {
const { payload } = yield take(DELETE_MAIL);
yield fork(removeMail, payload.key);
}
}
app\containers\SampleFullstackApps\Email\reducers\emailReducer.js
case DELETE_MAIL_SUCCESS:
return state.withMutations((mutableState) => {
mutableState
.set('inbox',
state.get('inbox').filter(mail => mail.key !== action.payload.key)
)
.set('notifMsg', notif.removed);
});
app\containers\SampleFullstackApps\Email\index.js
import { deleteAction } from './reducers/emailActions';
const constDispatchToProps = dispatch => ({
remove: bindActionCreators(deleteAction, dispatch),
});
return (
<EmailList
emailData={emailData}
openMail={openMail}
filterPage={currentPage}
keyword={keyword}
moveTo={update}
remove={remove}
toggleStar={update}
reply={this.handleReply}
loading={loading}
/>
)
app/components/Email/EmailList.js
return (
<IconButton className={classes.button} aria-label="Delete" onClick={() => remove(mail)}><Delete /></IconButton>
)