mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
messagepad (react is great!)
This commit is contained in:
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import _ from 'lodash';
|
||||
import { NamedPage } from 'vj/misc/PageLoader';
|
||||
import request from 'vj/utils/request';
|
||||
import loadReactRedux from 'vj/utils/loadReactRedux';
|
||||
@@ -11,17 +10,15 @@ const page = new NamedPage('home_messages', () => {
|
||||
let reduxStore;
|
||||
|
||||
function createDialog(user) {
|
||||
const id = _.uniqueId('PLACEHOLDER_');
|
||||
reduxStore.dispatch({
|
||||
type: 'DIALOGUES_CREATE',
|
||||
payload: {
|
||||
id,
|
||||
user,
|
||||
},
|
||||
});
|
||||
reduxStore.dispatch({
|
||||
type: 'DIALOGUES_SWITCH_TO',
|
||||
payload: id,
|
||||
payload: user._id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,11 +79,11 @@ const page = new NamedPage('home_messages', () => {
|
||||
*/
|
||||
async function loadSendTarget() {
|
||||
const queryString = parseQueryString();
|
||||
if (!queryString.target_uid) {
|
||||
if (!queryString.target) {
|
||||
return;
|
||||
}
|
||||
const user = await request.get('/user/search', {
|
||||
q: queryString.target_uid,
|
||||
q: queryString.target,
|
||||
exact_match: true,
|
||||
});
|
||||
if (!user || user.length === 0) {
|
||||
|
||||
Reference in new issue
Block a user