diff --git a/demo/src/containers/chat/Chat.js b/demo/src/containers/chat/Chat.js index 3fef04c5..3f3d7ac5 100755 --- a/demo/src/containers/chat/Chat.js +++ b/demo/src/containers/chat/Chat.js @@ -75,6 +75,26 @@ class Chat extends React.Component { } } + // todo: 1. bindthis需要重构 2. 这个文件都不是纯函数,最好优化一下 + snapshotListener = (e) => { + const { selectItem, selectTab } = this.state; + if (e.clipboardData && e.clipboardData.types) { + if (e.clipboardData.items.length > 0) { + if (/^image\/\w+$/.test(e.clipboardData.items[0].type)) { + const blob = e.clipboardData.items[0].getAsFile(); + const url = window.URL.createObjectURL(blob); + const file = { data: blob, url: url }; + this.props.sendSnapshotMessage(chatType[selectTab], selectItem, file) + } + } + } + } + + componentWillMount() { + // IE unsupport + document.addEventListener('paste', this.snapshotListener) + } + pictureChange(e) { const { match } = this.props const { selectItem, selectTab } = match.params @@ -117,7 +137,7 @@ class Chat extends React.Component { }) } - handleEmojiSelect(v) { + handleEmojiSelect(v) { this.setState({ value: (this.state.value || "") + v.key }, () => { @@ -209,18 +229,18 @@ class Chat extends React.Component { let tabs = null if (selectTab == "contact") { tabs = [ - [ "0", `${I18n.t("block")}`, "iconfont icon-circle-minus" ], - [ "1", `${I18n.t("delAFriend")}`, "iconfont icon-trash" ] + ["0", `${I18n.t("block")}`, "iconfont icon-circle-minus"], + ["1", `${I18n.t("delAFriend")}`, "iconfont icon-trash"] ] } else { // stranger tabs = [ - [ "2", `${I18n.t("addFriend")}`, "anticon anticon-user-add" ], - [ "3", `${I18n.t("delete")}`, "iconfont icon-trash" ] + ["2", `${I18n.t("addFriend")}`, "anticon anticon-user-add"], + ["3", `${I18n.t("delete")}`, "iconfont icon-trash"] ] } - const tabsItem = tabs.map(([ key, name, icon ]) => + const tabsItem = tabs.map(([key, name, icon]) => @@ -244,31 +264,31 @@ class Chat extends React.Component { const { selectItem, selectTab } = match.params const search = history.location.search switch (key) { - case "0": - // block a friend - this.props.doAddBlacklist(selectItem) - history.push("/contact" + search) - break - case "1": - // delete a friend - this.props.removeContact(selectItem) - break - case "2": - // add a friend - this.props.addContact(selectItem) - message.success(`${I18n.t("addFriendMessage")}`) - break - case "3": - // delete - this.props.deleteStranger(selectItem) - history.push("/stranger" + search) - break - default: + case "0": + // block a friend + this.props.doAddBlacklist(selectItem) + history.push("/contact" + search) + break + case "1": + // delete a friend + this.props.removeContact(selectItem) + break + case "2": + // add a friend + this.props.addContact(selectItem) + message.success(`${I18n.t("addFriendMessage")}`) + break + case "3": + // delete + this.props.deleteStranger(selectItem) + history.push("/stranger" + search) + break + default: } } onClearMessage = () => { - const { selectItem, selectTab } = _.get(this.props, [ "match", "params" ], {}) + const { selectItem, selectTab } = _.get(this.props, ["match", "params"], {}) const chatTypes = { "contact": "chat", "group": "groupchat", "chatroom": "chatroom", "stranger": "stranger" } const chatType = chatTypes[selectTab] this.props.clearMessage(chatType, selectItem) @@ -299,10 +319,11 @@ class Chat extends React.Component { componentWillUnmount() { if (this.timer) clearTimeout(this.timer) + document.removeEventListener("paste", this.snapshotListener) } callVideo = () => { - const { selectItem, selectTab } = _.get(this.props, [ "match", "params" ], {}) + const { selectItem, selectTab } = _.get(this.props, ["match", "params"], {}) const { confrModal, avModal } = this.props if (selectTab === "contact") { this.setState({ @@ -332,7 +353,7 @@ class Chat extends React.Component { callVoice = () => { - const { selectItem, selectTab } = _.get(this.props, [ "match", "params" ], {}) + const { selectItem, selectTab } = _.get(this.props, ["match", "params"], {}) console.log("sendWrapper::callVoice", WebIM.conn.context.userId/*当前登录用户*/, selectItem/*聊天对象*/, selectTab/*当前标签*/) this.setState({ @@ -348,7 +369,7 @@ class Chat extends React.Component { // TODO: optimization needed setTimeout(function () { const offset = _this.props.messageList ? _this.props.messageList.length : 0 - const { selectItem, selectTab } = _.get(_this.props, [ "match", "params" ], {}) + const { selectItem, selectTab } = _.get(_this.props, ["match", "params"], {}) const chatTypes = { "contact": "chat", "group": "groupchat", "chatroom": "chatroom", "stranger": "stranger" } const chatType = chatTypes[selectTab] @@ -370,7 +391,7 @@ class Chat extends React.Component { } } ok = (id) => { - this.props.deleteMessage(id,true) + this.props.deleteMessage(id, true) } render() { this.logger.info("chat component render") @@ -386,7 +407,7 @@ class Chat extends React.Component { const { selectItem, selectTab } = match.params const back = () => { - const redirectPath = "/" + [ selectTab ].join("/") + location.search + const redirectPath = "/" + [selectTab].join("/") + location.search history.push(redirectPath) } @@ -437,7 +458,7 @@ class Chat extends React.Component { ? @@ -448,15 +469,15 @@ class Chat extends React.Component {
{/* fixed bug of messageList.map(...) */} {this.state.isLoaded &&
{I18n.t("noMoreMessage")}
} - {_.map(messageList, (message,i) =>{ - if(i > 0){ - if(message.id != messageList[i-1].id){ - return + {_.map(messageList, (message, i) => { + if (i > 0) { + if (message.id != messageList[i - 1].id) { + return } - }else{ - return + } else { + return } - } )} + })}
@@ -541,6 +562,7 @@ export default connect( dispatch => ({ switchRightSider: ({ rightSiderOffset }) => dispatch(GroupActions.switchRightSider({ rightSiderOffset })), sendTxtMessage: (chatType, id, message) => dispatch(MessageActions.sendTxtMessage(chatType, id, message)), + sendSnapshotMessage: (chatType, id, message) => dispatch(MessageActions.sendSnapshotMessage(chatType, id, message)), deleteMessage: (id) => dispatch(MessageActions.deleteMessage(id)), sendImgMessage: (chatType, id, message, source) => dispatch(MessageActions.sendImgMessage(chatType, id, message, source)), sendFileMessage: (chatType, id, message, source) => dispatch(MessageActions.sendFileMessage(chatType, id, message, source)), diff --git a/demo/src/redux/MessageRedux.js b/demo/src/redux/MessageRedux.js index 7c786fdc..7bfc7bdd 100644 --- a/demo/src/redux/MessageRedux.js +++ b/demo/src/redux/MessageRedux.js @@ -114,62 +114,62 @@ export const parseFromServer = (message = {}, bodyType) => { // body.ext could save any customize info of message, like image size, width, height etc let body = copy(message, msgTpl[bodyType]) switch (bodyType) { - case "txt": - return { - ...obj, - status: "sent", - body: { - ...body, - ...ext, - msg: message.data, - type: "txt" + case "txt": + return { + ...obj, + status: "sent", + body: { + ...body, + ...ext, + msg: message.data, + type: "txt" + } } - } - break - case "img": - return { - ...obj, - status: "sent", - body: { - ...body, - ...ext, - type: "img" + break + case "img": + return { + ...obj, + status: "sent", + body: { + ...body, + ...ext, + type: "img" + } } - } - break - case "file": - return { - ...obj, - status: "sent", - body: { - ...body, - ...ext, - type: "file" + break + case "file": + return { + ...obj, + status: "sent", + body: { + ...body, + ...ext, + type: "file" + } } - } - break - case "audio": - return { - ...obj, - status: "sent", - body: { - ...body, - ...ext, - type: "audio" + break + case "audio": + return { + ...obj, + status: "sent", + body: { + ...body, + ...ext, + type: "audio" + } } - } - break - case "video": - return { - ...obj, - status: "sent", - body: { - ...body, - ...ext, - type: "video" + break + case "video": + return { + ...obj, + status: "sent", + body: { + ...body, + ...ext, + type: "video" + } } - } - break + break } } @@ -182,12 +182,12 @@ function copy(message, tpl) { } const { Types, Creators } = createActions({ - addMessage: [ "message", "bodyType" ], - deleteMessage: [ "id"], - updateMessageStatus: [ "message", "status" ], - updateMessageMid: [ "id", "mid" ], - muteMessage: [ "mid" ], - demo: [ "chatType" ], + addMessage: ["message", "bodyType"], + deleteMessage: ["id"], + updateMessageStatus: ["message", "status"], + updateMessageMid: ["id", "mid"], + muteMessage: ["mid"], + demo: ["chatType"], //clearMessage: [ "chatType", "id" ], // clearUnread: [ "chatType", "id" ], // ---------------async------------------ @@ -222,8 +222,44 @@ const { Types, Creators } = createActions({ dispatch(Creators.addMessage(pMessage, type)) } }, - sendImgMessage: (chatType, chatId, message = {}, source = {}, callback = () => { - }) => { + sendSnapshotMessage: (chatType, chatId, source) => { + console.log('sendSnapshotMessage', chatType, chatId, source) + return (dispatch, getState) => { + let pMessage = null; + var id = WebIM.conn.getUniqueId(); // 生成本地消息id + var msgObj = new WebIM.message('img', id); // 创建图片消息 + const type = "img"; + msgObj.set({ + apiUrl: WebIM.config.apiURL, + file: source, + to: chatId, // 接收消息对象 + roomType: chatType == "chatroom", + onFileUploadError: function (error) { + // console.log('Error'); + }, + onFileUploadComplete: function (data) { + // console.log('Complete'); + }, + success: function (id) { + // console.log('Success'); + } + }); + if (chatType === "groupchat" || chatType === "chatroom") { + msgObj.setGroup("groupchat") + } + + WebIM.conn.send(msgObj.body) + pMessage = parseFromLocal(chatType, chatId, msgObj.body, "img") + // NOTE: parseFromLocal will overwrite original id of msgObj + // Recover it here. + pMessage.id = id + // url at local only + pMessage.body.url = source.url + // console.log('pMessage', pMessage, pMessage.body.uri) + dispatch(Creators.addMessage(pMessage, type)) + } + }, + sendImgMessage: (chatType, chatId, message = {}, source = {}, callback = () => { }) => { return (dispatch, getState) => { let pMessage = null const id = WebIM.conn.getUniqueId() @@ -414,14 +450,14 @@ const { Types, Creators } = createActions({ clearUnread: (chatType, id) => { return (dispatch) => { dispatch({ "type": "CLEAR_UNREAD", "chatType": chatType, "id": id }) - AppDB.readMessage(chatType, id).then(res => {}) + AppDB.readMessage(chatType, id).then(res => { }) } }, clearMessage: (chatType, id) => { return (dispatch) => { dispatch({ "type": "CLEAR_MESSAGE", "chatType": chatType, "id": id }) - AppDB.clearMessage(chatType, id).then(res => {}) + AppDB.clearMessage(chatType, id).then(res => { }) } }, @@ -429,7 +465,7 @@ const { Types, Creators } = createActions({ return (dispatch) => { const msgObj = new WebIM.message("read", WebIM.conn.getUniqueId()) msgObj.set({ id: msg.id, to: msg.from, ext: { logo: "easemob" } }) - WebIM.conn.send(msgObj.body) + WebIM.conn.send(msgObj.body) } } }) @@ -475,7 +511,7 @@ export const addMessage = (state, { message, bodyType = "txt" }) => { // root id: when sent by current user or in group chat, is id of receiver. Otherwise is id of sender let chatId = bySelf || type !== "chat" ? to : from // chatId = type === "stranger" ? from - if(type === "stranger"){ + if (type === "stranger") { chatId = from } @@ -486,7 +522,7 @@ export const addMessage = (state, { message, bodyType = "txt" }) => { // } // update message array - const chatData = state.getIn([ type, chatId ], Immutable([])).asMutable() + const chatData = state.getIn([type, chatId], Immutable([])).asMutable() const _message = { ...message, bySelf, @@ -496,7 +532,7 @@ export const addMessage = (state, { message, bodyType = "txt" }) => { // the pushed message maybe have exsited in state, ignore if (_message.type === "chatroom" && bySelf) { - const oid = state.getIn([ "byMid", _message.id, "id" ]) + const oid = state.getIn(["byMid", _message.id, "id"]) if (oid) { _message.id = oid } @@ -514,42 +550,42 @@ export const addMessage = (state, { message, bodyType = "txt" }) => { // add a message to db, if by myselt, isUnread equals 0 !isPushed && AppDB.addMessage(_message, !bySelf ? 1 : 0) - const maxCacheSize = _.includes([ "group", "chatroom" ], type) ? WebIM.config.groupMessageCacheSize : WebIM.config.p2pMessageCacheSize + const maxCacheSize = _.includes(["group", "chatroom"], type) ? WebIM.config.groupMessageCacheSize : WebIM.config.p2pMessageCacheSize if (chatData.length > maxCacheSize) { const deletedChats = chatData.splice(0, chatData.length - maxCacheSize) - let byId = state.getIn([ "byId" ]) + let byId = state.getIn(["byId"]) byId = _.omit(byId, _.map(deletedChats, "id")) - state = state.setIn([ "byId" ], byId) + state = state.setIn(["byId"], byId) } - state = state.setIn([ type, chatId ], chatData) + state = state.setIn([type, chatId], chatData) // unread - const activeContact = _.get(rootState,[ "common", "activeContact" ]) + const activeContact = _.get(rootState, ["common", "activeContact"]) if (!bySelf && !isPushed && message.from !== activeContact) { - let count = state.getIn([ "unread", type, chatId ], 0) - state = state.setIn([ "unread", type, chatId ], ++count) + let count = state.getIn(["unread", type, chatId], 0) + state = state.setIn(["unread", type, chatId], ++count) } - state = state.setIn([ "byId", id ], { type, chatId }) + state = state.setIn(["byId", id], { type, chatId }) return state } -export const deleteMessage = (state,{id, isSelf}) => { +export const deleteMessage = (state, { id, isSelf }) => { id = id.mid || id - const byId = state.getIn([ "byId", id ]) - if(byId){ + const byId = state.getIn(["byId", id]) + if (byId) { const { type, chatId } = byId - let messages = state.getIn([ type, chatId ]).asMutable() + let messages = state.getIn([type, chatId]).asMutable() let found = _.find(messages, { id: id }) const index = messages.indexOf(found) - if(found.getIn([ "body", 'type' ]) != 'txt'){ + if (found.getIn(["body", 'type']) != 'txt') { messages.splice(index, 1) - messages.splice(index,0,{ + messages.splice(index, 0, { body: { type: 'txt', - msg: isSelf?'消息已撤回':found.getIn(['from'])+'撤回了一条消息' + msg: isSelf ? '消息已撤回' : found.getIn(['from']) + '撤回了一条消息' }, time: found.getIn(['time']), from: found.getIn(['from']), @@ -561,14 +597,14 @@ export const deleteMessage = (state,{id, isSelf}) => { toJid: "", type: "chat" }) - }else{ - let message = found.setIn([ "body", 'msg' ], found.from+'撤回了一条消息') - // message = found.setIn([ "status",], 'read') + } else { + let message = found.setIn(["body", 'msg'], found.from + '撤回了一条消息') + // message = found.setIn([ "status",], 'read') //console.log('删除了这条消息',message) messages.splice(messages.indexOf(found), 1, message) } - state = state.setIn([ type, chatId ], messages) + state = state.setIn([type, chatId], messages) AppDB.deleteMessage(id) } return state @@ -583,68 +619,68 @@ export const deleteMessage = (state,{id, isSelf}) => { */ export const updateMessageStatus = (state, { message, status = "" }) => { let { id } = message - if (!id) id = state.getIn([ "byMid", message.mid, "id" ]) //消息体里根本没有mid ... 也不可能没有id ... - let mids = state.getIn([ "byMid" ])||{} + if (!id) id = state.getIn(["byMid", message.mid, "id"]) //消息体里根本没有mid ... 也不可能没有id ... + let mids = state.getIn(["byMid"]) || {} let mid - for( var i in mids){ - console.log('ii',i) - if(mids[i].id == id){ + for (var i in mids) { + console.log('ii', i) + if (mids[i].id == id) { mid = i } } - const byId = state.getIn([ "byId", id ]) + const byId = state.getIn(["byId", id]) if (!_.isEmpty(byId)) { const { type, chatId } = byId - let messages = state.getIn([ type, chatId ]).asMutable() + let messages = state.getIn([type, chatId]).asMutable() let found = _.find(messages, { id: parseInt(id) }) - let msg = found.setIn([ "status" ], status) - msg = found.setIn([ "toJid" ], mid) + let msg = found.setIn(["status"], status) + msg = found.setIn(["toJid"], mid) messages.splice(messages.indexOf(found), 1, msg) - AppDB.updateMessageStatus(id, status).then(res => {}) - state = state.setIn([ type, chatId ], messages) + AppDB.updateMessageStatus(id, status).then(res => { }) + state = state.setIn([type, chatId], messages) } return state } export const clearMessage = (state, { chatType, id }) => { - return chatType ? state.setIn([ chatType, id ], []) : state + return chatType ? state.setIn([chatType, id], []) : state } export const clearUnread = (state, { chatType, id }) => { let data = state["unread"][chatType].asMutable() delete data[id] - return state.setIn([ "unread", chatType ], data) + return state.setIn(["unread", chatType], data) } export const updateMessageMid = (state, { id, mid }) => { AppDB.updateMessageMid(mid, Number(id)) - return state.setIn([ "byMid", mid ], { id }) + return state.setIn(["byMid", mid], { id }) } export const muteMessage = (state, { mid }) => { - const { id } = state.getIn([ "byMid", mid ], "") - const { type, chatId } = state.getIn([ "byId", id ], {}) + const { id } = state.getIn(["byMid", mid], "") + const { type, chatId } = state.getIn(["byId", id], {}) if (type && chatId) { - const messages = state.getIn([ type, chatId ]).asMutable() + const messages = state.getIn([type, chatId]).asMutable() const found = _.find(messages, { id: parseInt(id) }) - const msg = found.setIn([ "status" ], "muted") + const msg = found.setIn(["status"], "muted") messages.splice(messages.indexOf(found), 1, msg) - state = state.setIn([ type, chatId ], messages) + state = state.setIn([type, chatId], messages) } return state } export const initUnread = (state, { unreadList }) => { - let data = state.getIn([ "unread" ]) - data = data.merge(unreadList).setIn([ "chatroom" ], {}) - return state.setIn([ "unread" ], data) + let data = state.getIn(["unread"]) + data = data.merge(unreadList).setIn(["chatroom"], {}) + return state.setIn(["unread"], data) } export const fetchMessage = (state, { id, chatType, messages, offset }) => { let data = state[chatType] && state[chatType][id] ? state[chatType][id].asMutable() : [] data = messages.concat(data) //----------------------- - return state.setIn([ chatType, id ], data) + return state.setIn([chatType, id], data) } /* ------------- Hookup Reducers To Types ------------- */