diff --git a/package.json b/package.json index e97df417..dd8ed106 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "emoji-picker-element": "^1.10.1", "formik": "./git-deps/formik/packages/formik", "git-rev-sync": "^3.0.2", - "golos-lib-js": "^0.9.69", + "golos-lib-js": "^0.9.74", "history": "4.10.1", "immutable": "^4.0.0", "koa": "^2.13.4", diff --git a/src/components/pages/Messages.jsx b/src/components/pages/Messages.jsx index 6fea9ee9..cee1aac7 100644 --- a/src/components/pages/Messages.jsx +++ b/src/components/pages/Messages.jsx @@ -278,7 +278,7 @@ class Messages extends React.Component { } } - async componentDidUpdate(prevProps) { + componentDidUpdate(prevProps) { if (this.props.username !== prevProps.username && this.props.username) { this.props.fetchState(this.props.to); this.setCallback(this.props.username); @@ -294,35 +294,38 @@ class Messages extends React.Component { || this.props.contacts.size !== prevProps.contacts.size || this.props.memo_private !== prevProps.memo_private) { const { contacts, messages, accounts, currentUser } = this.props; + const anotherChat = this.props.to !== this.state.to; + this.setState({ + to: this.props.to, // protects from infinity loop + }); if (!this.props.checkMemo(currentUser)) { - this.setState({ - to: this.props.to, // protects from infinity loop - }); return; } - const anotherChat = this.props.to !== this.state.to; const anotherKey = this.props.memo_private !== prevProps.memo_private; const added = this.props.messages.size > this.state.messagesCount; let focusTimeout = prevProps.messages.size ? 100 : 1000; - const newContacts = contacts.size ? - normalizeContacts(contacts, accounts, currentUser, this.preDecoded, this.cachedProfileImages) : - this.state.contacts - const decoded = await normalizeMessages(messages, accounts, currentUser, prevProps.to, this.preDecoded) - this.setState({ - to: this.props.to, - contacts: newContacts, - messages: decoded, - messagesCount: messages.size, - }, () => { - hideSplash() - if (added) - this.markMessages2(); - setTimeout(() => { - if (anotherChat || anotherKey) { - this.focusInput(); - } - }, focusTimeout); - }) + + const updateData = async () => { + const newContacts = contacts.size ? + await normalizeContacts(contacts, accounts, currentUser, this.preDecoded, this.cachedProfileImages) : + this.state.contacts + const decoded = await normalizeMessages(messages, accounts, currentUser, prevProps.to, this.preDecoded) + this.setState({ + contacts: newContacts, + messages: decoded, + messagesCount: messages.size, + }, () => { + hideSplash() + if (added) + this.markMessages2(); + setTimeout(() => { + if (anotherChat || anotherKey) { + this.focusInput(); + } + }, focusTimeout); + }) + } + updateData() } } @@ -416,7 +419,7 @@ class Messages extends React.Component { this.props.sendMessage({ senderAcc: account, memoKey: private_key, toAcc: accounts[to], - group: the_group, + group: this.isGroup() && the_group, body: message, editInfo, type: 'text', replyingMessage: this.state.replyingMessage, notifyAbort: this.notifyAbort }) @@ -619,8 +622,8 @@ class Messages extends React.Component { const to = this.getToAcc() const private_key = currentUser.getIn(['private_keys', 'memo_private']); this.props.sendMessage({ - senderAcc: account, memoKey: private_key, toAcc: (!group) && accounts[to], - group: the_group, + senderAcc: account, memoKey: private_key, toAcc: accounts[to], + group: this.isGroup() && the_group, body: url, type: 'image', meta: {width, height}, replyingMessage: this.state.replyingMessage, notifyAbort: this.notifyAbort }); @@ -852,11 +855,16 @@ class Messages extends React.Component { ); }; + isGroup = () => { + const { to } = this.props + return to && !to.startsWith('@') + } + _renderMessages = (messagesStub, { }) => { const { to, the_group, accounts } = this.props if (to) { - const isGroup = !to.startsWith('@') + const isGroup = this.isGroup() if (isGroup) { const noGroup = the_group === null const groupError = noGroup || (the_group && the_group.error) @@ -1101,28 +1109,30 @@ export default withRouter(connect( message = {...message, ...replyingMessage}; } - let data - if (group) { - if (group.is_encrypted) { - alert('enc') - } - data = await golos.messages.encodeMsg({ group, message }) - } else { - data = golos.messages.encode(memoKey, toAcc.memo_key, message, editInfo ? editInfo.nonce : undefined); + let data = null + try { + data = await golos.messages.encodeMsg({ group, + private_memo: !group && memoKey, + to_public_memo: !group && toAcc.memo_key, + msg: message, + nonce: editInfo ? editInfo.nonce : undefined, + }) + } catch (err) { + console.error(err) + this.showError((err && err.message) || err.toString(), 10000) } - const emptyPubKey = 'GLS1111111111111111111111111111111114T1Anm' - const opData = { from: senderAcc.name, to: toAcc ? toAcc.name : '', - nonce: editInfo ? editInfo.nonce : data.nonce, - from_memo_key: group ? emptyPubKey : senderAcc.memo_key, - to_memo_key: group ? emptyPubKey : toAcc.memo_key, + nonce: /*editInfo ? editInfo.nonce : */data.nonce, + from_memo_key: data.from_memo_key, + to_memo_key: data.to_memo_key, checksum: data.checksum, update: editInfo ? true : false, encrypted_message: data.encrypted_message, - }; + } + alert(JSON.stringify(data.encrypted_message)) if (group) { opData.extensions = [[0, { @@ -1153,7 +1163,7 @@ export default withRouter(connect( if (err.message.includes('blocked by')) { this.showError(tt( 'messages.blocked_BY', { - BY: toAcc.name + BY: toAcc ? toAcc.name : '' } ), 10000) return @@ -1161,7 +1171,7 @@ export default withRouter(connect( if (err.message.includes('do not bother')) { this.showError(tt( 'messages.do_not_bother_BY', { - BY: toAcc.name + BY: toAcc ? toAcc.name : '' } ), 10000) return diff --git a/src/utils/Normalizators.js b/src/utils/Normalizators.js index 118cd7e1..560fb088 100644 --- a/src/utils/Normalizators.js +++ b/src/utils/Normalizators.js @@ -16,7 +16,39 @@ function getProfileImageLazy(account, cachedProfileImages) { return image; } -export function normalizeContacts(contacts, accounts, currentUser, preDecoded, cachedProfileImages) { +const cacheKey = (msg) => { + let key = [msg.nonce] + if (msg.group) { + key.push(msg.group) + key.push(msg.receive_date) + key.push(msg.from) + key.push(msg.to) + } else { + key.push(msg.receive_date) + } + key = key.join('|') + return key +} + +const saveToCache = (preDecoded, msg) => { + if (!msg.message) return false + if (msg.group && msg.decrypt_date !== msg.receive_date) return false + let key = cacheKey(msg) + preDecoded[key] = { message: msg.message } + return true +} + +const loadFromCache = (preDecoded, msg) => { + let key = cacheKey(msg) + let pd = preDecoded[key]; + if (pd) { + msg.message = pd.message + return true + } + return false +} + +export async function normalizeContacts(contacts, accounts, currentUser, preDecoded, cachedProfileImages) { if (!currentUser || !accounts) return []; @@ -24,11 +56,13 @@ export function normalizeContacts(contacts, accounts, currentUser, preDecoded, c if (!currentAcc) return []; - const private_key = currentUser.getIn(['private_keys', 'memo_private']); + const posting = currentUser.getIn(['private_keys', 'posting_private']) + const private_memo = currentUser.getIn(['private_keys', 'memo_private']); const tt_invalid_message = tt('messages.invalid_message'); let contactsCopy = contacts ? [...contacts.toJS()] : []; + let messages = [] for (let contact of contactsCopy) { let account = accounts && accounts[contact.contact]; contact.avatar = getProfileImageLazy(account, cachedProfileImages); @@ -38,41 +72,46 @@ export function normalizeContacts(contacts, accounts, currentUser, preDecoded, c continue; } - let public_key; - if (currentAcc.memo_key === contact.last_message.to_memo_key) { - public_key = contact.last_message.from_memo_key; - } else { - public_key = contact.last_message.to_memo_key; - } + messages.push(contact.last_message) + } - try { - golos.messages.decode(private_key, public_key, [contact.last_message], - (msg, idx, results) => { + try { + await decodeMsgs({ msgs: messages, private_memo, + login: { + account: currentAcc.name, keys: { posting }, + }, + before_decode: (msg, idx, results) => { + if (!msg.isGroup) { if (msg.read_date.startsWith('19') && msg.from === currentAcc.name) { msg.unread = true; } - let pd = preDecoded[msg.nonce + '' + msg.receive_date]; - if (pd) { - msg.message = pd; - return true; - } - return false; - }, (msg) => { - preDecoded[msg.nonce + '' + msg.receive_date] = msg.message; - }, (msg, idx, exception) => { - msg.message = { body: tt_invalid_message, invalid: true, }; - }, 0, 1); - } catch (ex) { - console.log(ex); - } + } + + if (loadFromCache(preDecoded, msg)) { + return true + } + return false; + }, + for_each: (msg) => { + saveToCache(preDecoded, msg) + }, + on_error: (msg, idx, exception) => { + msg.message = { body: tt_invalid_message, invalid: true, }; + }, + begin_idx: 0, + end_idx: messages.length, + }) + } catch (ex) { + console.log(ex); } + return contactsCopy } export async function normalizeMessages(messages, accounts, currentUser, to, preDecoded) { - let isGroup = true + let isGroup = false if (to) { - if (to[0] === '@') isGroup = false + if (to[0] !== '@') isGroup = true to = to.replace('@', '') } @@ -88,62 +127,51 @@ export async function normalizeMessages(messages, accounts, currentUser, to, pre const tt_invalid_message = tt('messages.invalid_message'); - if (isGroup) { - const decoded = await decodeMsgs({ messages: messagesCopy, - for_each: (msg, i) => { - msg.id = ++id; - msg.author = msg.from; - msg.date = new Date(msg.create_date + 'Z'); - }, - on_error: (msg, i, err) => { - console.log(err) - msg.message = {body: tt_invalid_message, invalid: true} - msg.id = ++id; - msg.author = msg.from; - msg.date = new Date(msg.create_date + 'Z'); - }, - begin_idx: messagesCopy.length - 1, - end_idx: -1, - }) - return decoded - } - + const posting = currentUser.getIn(['private_keys', 'posting_private']) const privateMemo = currentUser.getIn(['private_keys', 'memo_private']); - let messagesCopy2 = golos.messages.decode(privateMemo, accounts[to].memo_key, messagesCopy, - (msg, i, results) => { + console.time('dddm') + const decoded = await decodeMsgs({ msgs: messagesCopy, + private_memo: !isGroup && privateMemo, + login: { + account: currentAcc.name, keys: { posting }, + }, + before_decode: (msg, i, results) => { msg.id = ++id; msg.author = msg.from; msg.date = new Date(msg.create_date + 'Z'); - if (msg.to === currentAcc.name) { - if (msg.read_date.startsWith('19')) { - msg.toMark = true; - } - } else { - if (msg.read_date.startsWith('19')) { - msg.unread = true; + if (!isGroup) { + if (msg.to === currentAcc.name) { + if (msg.read_date.startsWith('19')) { + msg.toMark = true; + } + } else { + if (msg.read_date.startsWith('19')) { + msg.unread = true; + } } } + msg.decrypt_date = null - let pd = preDecoded[msg.nonce + '' + msg.receive_date]; - if (pd) { - msg.message = pd; - results.push(msg); - return true; + if (loadFromCache(preDecoded, msg)) { + results.push(msg) + return true } return false; }, - (msg) => { - preDecoded[msg.nonce + '' + msg.receive_date] = msg.message; + for_each: (msg, i) => { + saveToCache(preDecoded, msg) }, - (msg, i, err) => { - console.log(err); - msg.message = {body: tt_invalid_message, invalid: true}; + on_error: (msg, i, err) => { + console.error(err) + msg.message = {body: tt_invalid_message, invalid: true} }, - messagesCopy.length - 1, -1); - - return messagesCopy2; + begin_idx: messagesCopy.length - 1, + end_idx: -1, + }) + console.timeEnd('dddm') + return decoded } catch (ex) { console.log(ex); return []; diff --git a/yarn.lock b/yarn.lock index 88a3bfee..981c20f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5256,10 +5256,10 @@ globby@^11.0.1, globby@^11.0.4: "gls-messenger-native-core@file:native_core": version "1.0.0" -golos-lib-js@^0.9.69: - version "0.9.69" - resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.69.tgz#d7b9d17fab1d0967b2e99923ef7c85740da7a157" - integrity sha512-6kxDJUiSj8itwMAEP8klnJSijxyZ1Xz2RVmGGh7BAMTb1WT+YDUoZJFHFv4Cldt7usHs7OAIFKv4bQQzJCpM1w== +golos-lib-js@^0.9.74: + version "0.9.74" + resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.74.tgz#2be3851f9168bf846453486455e30e882d1f11dd" + integrity sha512-+Wa0FULKmOE+OIiQO9bQis4q9ytgl//8F2GzSqzGfxon0IVDGA1Ij7QIkc1nrL36nN0yFTu5tAnXpue7cafECg== dependencies: abort-controller "^3.0.0" assert "^2.0.0"