messagepad (react is great!)

This commit is contained in:
undefined committed 2020-06-26 15:51:42 +08:00
1 parent 39ea30664d
commit 560e920624
13 files changed
+146 -279

No files matched your search

@@ -7,6 +7,7 @@ import 'jquery-scroll-lock';
import 'jquery.easing';
import i18n from 'vj/utils/i18n';
import { parse as parseMongoId } from 'vj/utils/mongoId';
import Message from './MessageComponent';
const mapStateToProps = (state) => ({
@@ -22,24 +23,17 @@ export default class MessagePadDialogueContentContainer extends React.PureCompon
$(this.refs.list).scrollLock({ strict: true });
}
UNSAFE_componentWillUpdate(nextProps) { // eslint-disable-line camelcase
if (nextProps.activeId !== this.props.activeId) {
componentDidUpdate(prevProps) {
const node = this.refs.list;
if (this.props.activeId !== prevProps.activeId) {
this.scrollToBottom = true;
this.scrollWithAnimation = false;
return;
}
const node = this.refs.list;
if (node.scrollTop + node.offsetHeight === node.scrollHeight) {
} else if (node.scrollTop + node.offsetHeight === node.scrollHeight) {
this.scrollToBottom = true;
this.scrollWithAnimation = true;
return;
}
this.scrollToBottom = false;
}
} else this.scrollToBottom = false;
componentDidUpdate() {
if (this.scrollToBottom) {
const node = this.refs.list;
const targetScrollTop = node.scrollHeight - node.offsetHeight;
if (this.scrollWithAnimation) {
$(node).stop().animate({ scrollTop: targetScrollTop }, 200, 'easeOutCubic');
@@ -53,19 +47,19 @@ export default class MessagePadDialogueContentContainer extends React.PureCompon
if (this.props.activeId === null) {
return [];
}
return _.map(this.props.item.reply, (reply, idx) => (
return _.map(this.props.item.messages, (reply) => (
<Message
key={idx}
key={reply._id}
isSelf={reply.from === UserContext._id}
faceUrl={
reply.from === this.props.item.from
? this.props.item.from_udoc.gravatar
: this.props.item.to_udoc.gravatar
reply.from === UserContext._id
? UserContext.gravatar
: this.props.item.udoc.gravatar
}
>
<div>{reply.content}</div>
<time data-tooltip={moment(reply.at).format('YYYY-MM-DD HH:mm:ss')}>
<TimeAgo datetime={reply.at} locale={i18n('timeago_locale')} />
<time data-tooltip={moment(parseMongoId(reply._id).timestamp * 1000).format('YYYY-MM-DD HH:mm:ss')}>
<TimeAgo datetime={parseMongoId(reply._id).timestamp * 1000} locale={i18n('timeago_locale')} />
</time>
</Message>
));
@@ -28,9 +28,9 @@ export default class MessagePadDialogueListContainer extends React.PureComponent
render() {
const orderedDialogues = _.orderBy(
_.values(this.props.dialogues),
(dialogue) => (dialogue.isPlaceholder
? Number.POSITIVE_INFINITY
: _.maxBy(dialogue.reply, 'at').at),
(dialogue) => (_.maxBy(dialogue.messages, '_id')
? _.maxBy(dialogue.messages, '_id')._id
: Number.POSITIVE_INFINITY),
'desc',
);
return (
@@ -38,21 +38,9 @@ export default class MessagePadDialogueListContainer extends React.PureComponent
{_.map(orderedDialogues, (dialogue) => (
<ListItem
key={dialogue._id}
userName={
dialogue.from === UserContext._id
? dialogue.to_udoc.uname
: dialogue.from_udoc.uname
}
summary={
dialogue.isPlaceholder
? ''
: _.last(dialogue.reply).content
}
faceUrl={
dialogue.from === UserContext._id
? dialogue.to_udoc.gravatar
: dialogue.from_udoc.gravatar
}
userName={dialogue.udoc.uname}
summary={(_.last(dialogue.messages)?.content) || ''}
faceUrl={dialogue.udoc.gravatar}
active={dialogue._id === this.props.activeId}
onClick={() => this.props.handleClick(dialogue._id)}
/>
@@ -27,8 +27,7 @@ const mapDispatchToProps = (dispatch) => ({
},
});
},
postSend(placeholderId, uid, value) {
if (placeholderId === null) return;
postSend(uid, value) {
const req = request.post('', {
operation: 'send',
uid,
@@ -38,23 +37,7 @@ const mapDispatchToProps = (dispatch) => ({
type: 'DIALOGUES_POST_SEND',
payload: req,
meta: {
placeholderId,
},
});
},
postReply(_id, uid, value) {
if (_id === null) return;
const req = request.post('', {
operation: 'send',
type: 'single',
uid,
content: value,
});
dispatch({
type: 'DIALOGUES_POST_REPLY',
payload: req,
meta: {
dialogueId: _id,
dialogueId: uid,
},
});
},
@@ -88,20 +71,10 @@ export default class MessagePadInputContainer extends React.PureComponent {
}
submit() {
const state = this.context.store.getState();
if (state.dialogues[this.props.activeId].isPlaceholder) {
this.props.postSend(
this.props.activeId,
state.dialogues[this.props.activeId].to,
this.props.inputValue,
);
} else {
this.props.postReply(
this.props.activeId,
state.dialogues[this.props.activeId].to,
this.props.inputValue,
);
}
this.props.postSend(
this.props.activeId,
this.props.inputValue,
);
}
render() {
@@ -26,7 +26,7 @@ export default function reducer(state = null, action) {
return action.payload;
}
case 'DIALOGUES_POST_SEND_FULFILLED': {
return action.payload.mdoc._id;
return action.meta.dialogueId;
}
default:
return state;
+30 -61
View File
@@ -3,79 +3,48 @@ import _ from 'lodash';
export default function reducer(state = {}, action) {
switch (action.type) {
case 'DIALOGUES_LOAD_DIALOGUES_FULFILLED': {
const { messages, udict } = action.payload;
return _.keyBy(
_.filter(
_.map(messages, (m) => ({
to_udoc: udict[m.to],
from_udoc: udict[m.from],
...m,
})),
(m) => m.to_udoc && m.from_udoc,
),
'_id',
);
const { messages } = action.payload;
return _.keyBy(messages, 'udoc._id');
}
case 'DIALOGUES_CREATE': {
const { id, user } = action.payload;
const { user } = action.payload;
return {
...state,
[id]: {
_id: id,
from: UserContext._id,
to: user._id,
to_udoc: { ...user },
reply: [],
isPlaceholder: true,
},
};
}
case 'DIALOGUES_POST_REPLY_FULFILLED': {
const id = action.meta.dialogueId;
const { reply } = action.payload;
return {
...state,
[id]: {
...state[id],
reply: [
...state[id].reply,
reply,
],
[user._id]: {
_id: user._id,
udoc: { ...user },
messages: [],
},
};
}
case 'DIALOGUES_POST_SEND_FULFILLED': {
const { placeholderId } = action.meta;
const id = action.meta.dialogueId;
const { mdoc } = action.payload;
return {
..._.omit(state, placeholderId),
[action.payload.mdoc._id]: action.payload.mdoc,
...state,
[id]: {
...state[id],
messages: [
...state[id].messages,
mdoc,
],
},
};
}
case 'DIALOGUES_MESSAGE_PUSH': {
const { type, data } = action.payload;
const id = data._id;
if (type === 'new') {
return {
...state,
[id]: data,
};
} if (type === 'reply') {
if (state[id] === undefined) {
window.location.reload();
return state;
}
return {
...state,
[id]: {
...state[id],
reply: [
...state[id].reply,
data.reply[0],
],
},
};
}
return state;
const { mdoc, udoc } = action.payload;
const to = mdoc.from === UserContext._id ? mdoc.to : mdoc.from;
return {
...state,
[to]: {
...state[to] || {},
udoc,
messages: [
...state[to]?.messages || [],
mdoc,
],
},
};
}
default:
return state;
+6 -14
View File
@@ -7,10 +7,10 @@ export default function reducer(state = {}, action) {
return _.fromPairs(_.map(dialogues, (d) => [d._id, '']));
}
case 'DIALOGUES_CREATE': {
const { id } = action.payload;
const { user } = action.payload;
return {
...state,
[id]: '',
[user._id]: '',
};
}
case 'DIALOGUES_INPUT_CHANGED': {
@@ -20,27 +20,19 @@ export default function reducer(state = {}, action) {
[id]: action.payload,
};
}
case 'DIALOGUES_POST_REPLY_FULFILLED': {
case 'DIALOGUES_POST_SEND_FULFILLED': {
const id = action.meta.dialogueId;
return {
...state,
[id]: '',
};
}
case 'DIALOGUES_POST_SEND_FULFILLED': {
const { placeholderId } = action.meta;
return {
..._.omit(state, placeholderId),
[action.payload.mdoc._id]: '',
};
}
case 'DIALOGUES_MESSAGE_PUSH': {
const { type, data } = action.payload;
const id = data._id;
if (type === 'new') {
const { udoc } = action.payload;
if (!state[udoc._id]) {
return {
...state,
[id]: '',
[udoc._id]: '',
};
}
return state;
+13 -39
View File
@@ -8,66 +8,40 @@ export default function reducer(state = {}, action) {
return _.fromPairs(_.map(dialogues, (d) => [d._id, false]));
}
case 'DIALOGUES_CREATE': {
const { id } = action.payload;
const { user } = action.payload;
return {
...state,
[id]: false,
[user._id]: false,
};
}
case 'DIALOGUES_POST_REPLY_PENDING': {
case 'DIALOGUES_POST_SEND_PENDING': {
const id = action.meta.dialogueId;
return {
...state,
[id]: true,
};
}
case 'DIALOGUES_POST_REPLY_REJECTED': {
Notification.error(action.payload.message);
const id = action.meta.dialogueId;
return {
...state,
[id]: false,
};
}
case 'DIALOGUES_POST_REPLY_FULFILLED': {
const id = action.meta.dialogueId;
return {
...state,
[id]: false,
};
}
case 'DIALOGUES_POST_SEND_PENDING': {
const { placeholderId } = action.meta;
return {
...state,
[placeholderId]: true,
};
}
case 'DIALOGUES_POST_SEND_REJECTED': {
Notification.error(action.payload.message);
const { placeholderId } = action.meta;
const id = action.meta.dialogueId;
return {
...state,
[placeholderId]: false,
[id]: false,
};
}
case 'DIALOGUES_POST_SEND_FULFILLED': {
const { placeholderId } = action.meta;
const id = action.meta.dialogueId;
return {
..._.omit(state, placeholderId),
[action.payload.mdoc._id]: false,
...state,
[id]: false,
};
}
case 'DIALOGUES_MESSAGE_PUSH': {
const { type, data } = action.payload;
const id = data._id;
if (type === 'new') {
return {
...state,
[id]: false,
};
}
return state;
const { udoc } = action.payload;
return {
...state,
[udoc._id]: false,
};
}
default:
return state;