diff --git a/package.json b/package.json
index a1d54ed3..347170cb 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.75",
+ "golos-lib-js": "^0.9.76",
"history": "4.10.1",
"immutable": "^4.0.0",
"koa": "^2.13.4",
@@ -73,7 +73,7 @@
"scripts": {
"preinstall": "node git-install.js",
"cordova": "cordova",
- "dev": "cross-env HTTPS=true react-app-rewired start",
+ "dev": "cross-env react-app-rewired start",
"dev:server": "nodemon server",
"build": "react-app-rewired build",
"prod": "NODE_ENV=production node server/index.js",
diff --git a/src/components/elements/messages/ConversationListItem/ConversationListItem.css b/src/components/elements/messages/ConversationListItem/ConversationListItem.css
index 20e7c582..11346a85 100644
--- a/src/components/elements/messages/ConversationListItem/ConversationListItem.css
+++ b/src/components/elements/messages/ConversationListItem/ConversationListItem.css
@@ -54,6 +54,11 @@
text-align: center;
}
+.conversation-unread.mention:not(.mine) {
+ background-color: #007aff;
+ margin-left: 8px;
+}
+
.conversation-unread.mine {
float: right;
color: #007aff;
diff --git a/src/components/elements/messages/ConversationListItem/index.jsx b/src/components/elements/messages/ConversationListItem/index.jsx
index fe2b33ec..39145242 100644
--- a/src/components/elements/messages/ConversationListItem/index.jsx
+++ b/src/components/elements/messages/ConversationListItem/index.jsx
@@ -64,7 +64,7 @@ export default class ConversationListItem extends React.Component {
render() {
const { selected } = this.props;
- const { avatar, isSystemMessage, contact, last_message, size, unread_donate } = this.props.data;
+ const { avatar, isSystemMessage, contact, last_message, size, unread_donate, kind } = this.props.data;
const link = this.makeLink();
@@ -84,12 +84,31 @@ export default class ConversationListItem extends React.Component {
unread = (
●
);
}
- const unreadMessages = size && size.unread_inbox_messages;
+ let title = ''
+
+ const unreadMessages = size && size.unread_inbox_messages
+ const unreadMentions = size && size.unread_mentions
if (!unread && unreadMessages) {
unread = (
{unreadMessages}
)
+ if (kind === 'group') {
+ title += tt('plurals.reply_count', { count: unreadMessages })
+ }
+ }
+
+ if (unreadMentions) {
+ unread =
+
+ {unreadMentions}
+
+ {unread}
+
+ if (kind === 'group') {
+ if (title) title += ', '
+ title += tt('plurals.mention_count', { count: unreadMentions })
+ }
}
let checkmark
@@ -100,7 +119,7 @@ export default class ConversationListItem extends React.Component {
}
return (
-
+
{this._renderAvatar()}
{contact}{checkmark}
diff --git a/src/components/elements/messages/Message/Message.css b/src/components/elements/messages/Message/Message.css
index a874a7d8..c758dcc2 100644
--- a/src/components/elements/messages/Message/Message.css
+++ b/src/components/elements/messages/Message/Message.css
@@ -44,6 +44,9 @@
text-decoration: underline;
font-weight: bold;
}
+.msgs-message .bubble-container a.mention {
+ text-decoration: none;
+}
.msgs-message.mine .bubble-container {
justify-content: flex-end;
diff --git a/src/components/elements/messages/Message/index.jsx b/src/components/elements/messages/Message/index.jsx
index 692b7bf3..c20463ac 100644
--- a/src/components/elements/messages/Message/index.jsx
+++ b/src/components/elements/messages/Message/index.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import {connect} from 'react-redux'
import { Fade } from 'react-foundation-components/lib/global/fade'
import { LinkWithDropdown } from 'react-foundation-components/lib/global/dropdown'
+import { Link } from 'react-router-dom'
import tt from 'counterpart';
import cn from 'classnames'
import { Asset } from 'golos-lib-js/lib/utils'
@@ -9,6 +10,8 @@ import { Asset } from 'golos-lib-js/lib/utils'
import AuthorDropdown from 'app/components/elements/messages/AuthorDropdown'
import Donating from 'app/components/elements/messages/Donating'
import Userpic from 'app/components/elements/Userpic'
+import { session } from 'app/redux/UserSaga'
+import { accountNameRegEx } from 'app/utils/mentions'
import { displayQuoteMsg } from 'app/utils/MessageUtils';
import { proxifyImageUrl } from 'app/utils/ProxifyUrl';
import './Message.css';
@@ -26,6 +29,8 @@ class Message extends React.Component {
};
render() {
+ let username
+
const {
idx,
data,
@@ -72,6 +77,11 @@ class Message extends React.Component {
} else if (word.length <= 2 && /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/.test(word)) {
spans.push({word});
spans.push(' ');
+ } else if (word.length > 3 && accountNameRegEx.test(word)) {
+ const sess = session.load()
+ if (sess && !username) username = sess[0]
+ spans.push({word})
+ spans.push(' ')
} else {
spans.push(word + ' ');
}
diff --git a/src/components/pages/Messages.jsx b/src/components/pages/Messages.jsx
index 31bd07ee..d6e635e5 100644
--- a/src/components/pages/Messages.jsx
+++ b/src/components/pages/Messages.jsx
@@ -106,6 +106,7 @@ class Messages extends React.Component {
op.extensions = [[0, {
group: to,
requester: account.name,
+ mentions: [account.name],
}]]
}
const json = JSON.stringify(['private_mark_message', op])
@@ -252,7 +253,7 @@ class Messages extends React.Component {
if (op.update) {
this.props.messageEdited(op, timestamp, updateMessage, isMine);
} else {
- this.props.messaged(op, timestamp, updateMessage, isMine);
+ this.props.messaged(op, timestamp, updateMessage, isMine, username)
if (this.nonce !== op.nonce) {
this.nonce = op.nonce
if (!isMine && !this.windowFocused) {
@@ -1285,10 +1286,14 @@ export default withRouter(connect(
successCallback: null,
errorCallback: (err, errStr) => {
if (err && err.message) {
- if (err.message.includes('blocked by')) {
+ const bm = 'blocked by user (@'
+ const bmIdx = err.message.indexOf(bm)
+ if (bmIdx > -1) {
+ const msg = err.message.substring(bmIdx + bm.length)
+ const blocker = msg.substring(0, msg.indexOf(')'))
this.showError(tt(
'messages.blocked_BY', {
- BY: toAcc ? toAcc.name : ''
+ BY: blocker
}
), 10000)
return
@@ -1307,8 +1312,8 @@ export default withRouter(connect(
},
}));
},
- messaged: (message, timestamp, updateMessage, isMine) => {
- dispatch(g.actions.messaged({message, timestamp, updateMessage, isMine}));
+ messaged: (message, timestamp, updateMessage, isMine, username) => {
+ dispatch(g.actions.messaged({message, timestamp, updateMessage, isMine, username}));
},
messageEdited: (message, timestamp, updateMessage, isMine) => {
dispatch(g.actions.messageEdited({message, timestamp, updateMessage, isMine}));
diff --git a/src/locales/en.json b/src/locales/en.json
index b8faead7..55d6d22e 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -339,10 +339,20 @@
"one": "1 member",
"other": "%(count)s members"
},
+ "mention_count": {
+ "zero": "0 mentions",
+ "one": "1 mention",
+ "other": "%(count)s mentions"
+ },
"message_count": {
"zero": "0 messages",
"one": "1 message",
"other": "%(count)s messages"
+ },
+ "reply_count": {
+ "zero": "0 replies",
+ "one": "1 reply",
+ "other": "%(count)s replies"
}
},
"g": {
diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json
index 384b1269..950087b8 100644
--- a/src/locales/ru-RU.json
+++ b/src/locales/ru-RU.json
@@ -355,10 +355,20 @@
"one": "1 участник",
"other": "%(count)s участник(-ов)"
},
+ "mention_count": {
+ "zero": "0 упоминания",
+ "one": "1 упоминание",
+ "other": "%(count)s упоминания(-й)"
+ },
"message_count": {
"zero": "0 сообщений",
"one": "1 сообщение",
"other": "%(count)s сообщения(-й)"
+ },
+ "reply_count": {
+ "zero": "0 ответов",
+ "one": "1 ответ",
+ "other": "%(count)s ответа(-ов)"
}
},
"g": {
diff --git a/src/redux/GlobalReducer.js b/src/redux/GlobalReducer.js
index 57f975f3..5a86167f 100644
--- a/src/redux/GlobalReducer.js
+++ b/src/redux/GlobalReducer.js
@@ -84,7 +84,7 @@ export default createModule({
action: 'MESSAGED',
reducer: (
state,
- { payload: { message, timestamp, updateMessage, isMine } }
+ { payload: { message, timestamp, updateMessage, isMine, username } }
) => {
message.create_date = timestamp;
message.receive_date = timestamp;
@@ -96,6 +96,7 @@ export default createModule({
const { group, mentions } = opGroup(message)
message.group = group
message.mentions = mentions
+ message.read_date = (group && !message.to) ? timestamp : '1970-01-01T00:00:00';
let new_state = state;
let messages_update = message.nonce;
@@ -138,9 +139,15 @@ export default createModule({
contacts = contacts.update(idx, contact => {
contact = contact.set('last_message', fromJS(message));
if (!isMine && !updateMessage) {
- let msgs = contact.getIn(['size', 'unread_inbox_messages']);
- contact = contact.setIn(['size', 'unread_inbox_messages'],
- msgs + 1);
+ if (!group || message.to === username) {
+ let msgs = contact.getIn(['size', 'unread_inbox_messages']);
+ contact = contact.setIn(['size', 'unread_inbox_messages'],
+ msgs + 1);
+ }
+ if (group && message.mentions && message.mentions.includes(username)) {
+ contact = contact.updateIn(['size', 'unread_mentions'],
+ msgs => msgs + 1)
+ }
}
return contact
});
@@ -200,7 +207,7 @@ export default createModule({
) => {
let new_state = state;
let messages_update = message.nonce || Math.random();
- const { requester } = opGroup(message)
+ const { group, requester } = opGroup(message)
if (updateMessage) {
new_state = new_state.updateIn(['messages'],
List(),
@@ -215,7 +222,7 @@ export default createModule({
List(),
contacts => {
let idx = contacts.findIndex(i =>
- i.get('contact') === (isMine ? message.to : message.from));
+ i.get('contact') === (group || (isMine ? message.to : message.from)))
if (idx !== -1) {
contacts = contacts.update(idx, contact => {
// to update read_date (need for isMine case), and more actualize text
@@ -229,6 +236,9 @@ export default createModule({
// currently used only !isMine case
const msgsKey = isMine ? 'unread_outbox_messages' : 'unread_inbox_messages';
contact = contact.setIn(['size', msgsKey], 0);
+ if (!isMine) {
+ contact = contact.setIn(['size', 'unread_mentions'], 0)
+ }
return contact;
});
}
diff --git a/src/utils/mentions.js b/src/utils/mentions.js
index 916cd594..a4232b6b 100644
--- a/src/utils/mentions.js
+++ b/src/utils/mentions.js
@@ -1,5 +1,5 @@
-const accountNameRegEx = /^@[a-z0-9.-]+$/
+export const accountNameRegEx = /^@[a-z0-9.-]+$/
// TODO: can be renderMsg which also supports links, and rendering
export function parseMentions(message) {
diff --git a/yarn.lock b/yarn.lock
index 1b7a63bd..84575d8e 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.75:
- version "0.9.75"
- resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.75.tgz#51b2f05f6c536776d5a9681f2871d4c9d3449e37"
- integrity sha512-0upRVfRnCJ+MD9cMCtVCA85eWpXKTF/zg8mjhQEpfuUELFRmNQ1maDuIY/meM3VSIVgkXaoqw1ci0CHjjfP55w==
+golos-lib-js@^0.9.76:
+ version "0.9.76"
+ resolved "https://registry.yarnpkg.com/golos-lib-js/-/golos-lib-js-0.9.76.tgz#c589b26a8f77916529f2fb6e1020bb87f4cb0a7f"
+ integrity sha512-E9A9BnVoOoPjklxGJVxB3xKgLbLSCaXfW0lN4pipAKuokGEVFy8DPEwlUsFgmY9Jf9JFcwl5h6q2c1dzEuBGkQ==
dependencies:
abort-controller "^3.0.0"
assert "^2.0.0"