diff --git a/server/index.js b/server/index.js index 3fbf53473..b7b0aef0c 100644 --- a/server/index.js +++ b/server/index.js @@ -7,6 +7,7 @@ const cors = require('@koa/cors') const coBody = require('co-body') const config = require('config') const git = require('git-rev-sync') +const fs = require('fs') const path = require('path') const { convertEntriesToArrays, } = require('./utils/misc') @@ -70,14 +71,22 @@ if (env === 'production') { } if (env === 'production') { + const buildPath = path.join(__dirname, '../build') app.use(async (ctx, next) => { - if (ctx.path.startsWith('/@')) { - ctx.url = '/' + const parts = ctx.path.split('/') + // / + // /@user + // /group + if (parts.length === 2 && parts[1] !== 'api') { + const filePath = path.join(buildPath, parts[1]) + if (!fs.existsSync(filePath)) { + ctx.url = '/' + } } await next() }) const cacheOpts = { maxage: 0, gzip: true } - app.use(static(path.join(__dirname, '../build'), cacheOpts)) + app.use(static(buildPath, cacheOpts)) } app.use(router.routes()) diff --git a/src/assets/icons/golos.svg b/src/assets/icons/golos.svg new file mode 100644 index 000000000..cf13e03be --- /dev/null +++ b/src/assets/icons/golos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/elements/Icon.jsx b/src/components/elements/Icon.jsx index 80fe69eef..dcf3f562e 100644 --- a/src/components/elements/Icon.jsx +++ b/src/components/elements/Icon.jsx @@ -24,7 +24,7 @@ const icons = new Map([ // ['clock', require('app/assets/icons/clock.svg')], // ['copy', require('app/assets/icons/copy.svg')], // ['extlink', require('app/assets/icons/extlink.svg')], - // ['golos', require('app/assets/icons/golos.svg')], + ['golos', require('app/assets/icons/golos.svg')], ['dropdown-arrow', require('app/assets/icons/dropdown-arrow.svg')], // ['printer', require('app/assets/icons/printer.svg')], // ['search', require('app/assets/icons/search.svg')], diff --git a/src/components/elements/messages/ChatError/ChatError.scss b/src/components/elements/messages/ChatError/ChatError.scss new file mode 100644 index 000000000..6b0ee4537 --- /dev/null +++ b/src/components/elements/messages/ChatError/ChatError.scss @@ -0,0 +1,6 @@ +.ChatError { + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%); + left: 50%; +} diff --git a/src/components/elements/messages/ChatError/index.jsx b/src/components/elements/messages/ChatError/index.jsx new file mode 100644 index 000000000..3a22ff33a --- /dev/null +++ b/src/components/elements/messages/ChatError/index.jsx @@ -0,0 +1,18 @@ +import React from 'react' +import tt from 'counterpart' + +import Icon from 'app/components/elements/Icon' +import './ChatError.scss' + +class ChatError extends React.Component { + render() { + const { isGroup } = this.props + return
+
+
{isGroup ? tt('msgs_chat_error.404_group') : tt('msgs_chat_error.404_acc')}
+
{tt('msgs_chat_error.404_but')}
+
+ } +} + +export default ChatError diff --git a/src/components/modules/messages/MessageList/index.js b/src/components/modules/messages/MessageList/index.js index 3af4c9f4d..ffeb5c170 100644 --- a/src/components/modules/messages/MessageList/index.js +++ b/src/components/modules/messages/MessageList/index.js @@ -79,7 +79,15 @@ export default class MessageList extends React.Component { } renderMessages = () => { - const { to, renderEmpty, messages, selectedMessages, onMessageSelect } = this.props; + const { to, renderEmpty, renderMessages, messages, selectedMessages, onMessageSelect } = this.props; + + let renderRes = false + if (renderMessages) { + renderRes = renderMessages({}) + } + if (renderRes !== false) { + return renderRes + } if (!to && renderEmpty) { return renderEmpty() diff --git a/src/components/modules/messages/Messenger/index.js b/src/components/modules/messages/Messenger/index.js index 2f04d2255..93b2a3d44 100644 --- a/src/components/modules/messages/Messenger/index.js +++ b/src/components/modules/messages/Messenger/index.js @@ -38,10 +38,11 @@ export default class Messages extends React.Component { } render() { - const { account, to, + const { account, to, toNew, contacts, conversationTopLeft, conversationTopRight, conversationLinkPattern, onConversationSearch, onConversationSelect, - messagesTopLeft, messagesTopCenter, messagesTopRight, messages, replyingMessage, onCancelReply, onSendMessage, + messagesTopLeft, messagesTopCenter, messagesTopRight, messages, renderMessages, + replyingMessage, onCancelReply, onSendMessage, onButtonImageClicked, onImagePasted, selectedMessages, onMessageSelect, onPanelDeleteClick, onPanelReplyClick, onPanelEditClick, onPanelCloseClick, composeRef @@ -86,6 +87,7 @@ export default class Messages extends React.Component { if ((localStorage.getItem('msgr_auth') && !account) || process.env.MOBILE_APP) return null return }} + renderMessages={renderMessages} messages={messages} replyingMessage={replyingMessage} onCancelReply={onCancelReply} diff --git a/src/components/pages/Messages.jsx b/src/components/pages/Messages.jsx index 6e394dd1f..d16c2f0a1 100644 --- a/src/components/pages/Messages.jsx +++ b/src/components/pages/Messages.jsx @@ -17,6 +17,7 @@ import MarkNotificationRead from 'app/components/elements/MarkNotificationRead' import NotifiCounter from 'app/components/elements/NotifiCounter' import DialogManager from 'app/components/elements/common/DialogManager' import AddImageDialog from 'app/components/dialogs/AddImageDialog' +import ChatError from 'app/components/elements/messages/ChatError' import PageFocus from 'app/components/elements/messages/PageFocus' import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper' import Userpic from 'app/components/elements/Userpic' @@ -56,11 +57,18 @@ class Messages extends React.Component { this.composeRef = React.createRef() } + getToAcc = () => { + let { to } = this.props + if (to) to = to.replace('@', '') + return to + } + markMessages() { const { messages } = this.state; if (!messages.length) return; - const { account, accounts, to } = this.props; + const { account, accounts, } = this.props; + const to = this.getToAcc() let OPERATIONS = golos.messages.makeDatedGroups(messages, (message_object, idx) => { return message_object.toMark && !message_object._offchain; @@ -390,7 +398,8 @@ class Messages extends React.Component { onSendMessage = (message, event) => { if (!message.length) return; - const { to, account, accounts, currentUser, messages } = this.props; + const { account, accounts, currentUser, messages } = this.props; + const to = this.getToAcc() const private_key = currentUser.getIn(['private_keys', 'memo_private']); let editInfo; @@ -480,7 +489,8 @@ class Messages extends React.Component { onPanelDeleteClick = (event) => { const { messages } = this.state; - const { account, accounts, to } = this.props; + const { account, accounts, } = this.props; + const to = this.getToAcc() // TODO: works wrong if few messages have same create_time /*let OPERATIONS = golos.messages.makeDatedGroups(messages, (message_object, idx) => { @@ -597,7 +607,8 @@ class Messages extends React.Component { if (!url) return; - const { to, account, accounts, currentUser, messages } = this.props; + const { account, accounts, currentUser, messages } = this.props; + const to = this.getToAcc() const private_key = currentUser.getIn(['private_keys', 'memo_private']); this.props.sendMessage({ senderAcc: account, memoKey: private_key, toAcc: accounts[to], @@ -746,7 +757,8 @@ class Messages extends React.Component { _renderMessagesTopCenter = ({ isSmall }) => { let messagesTopCenter = []; - const { to, accounts } = this.props; + const { accounts } = this.props; + const to = this.getToAcc() if (accounts[to]) { let checkmark if (to === 'notify') { @@ -862,6 +874,21 @@ class Messages extends React.Component { ); }; + _renderMessages = ({ }) => { + const { to, the_group, accounts } = this.props + + if (to) { + const isGroup = !to.startsWith('@') + if (isGroup && the_group === null) { + return + } else if (!isGroup && !accounts[this.getToAcc()]) { + return + } + } + + return false + } + handleFocusChange = isFocused => { this.windowFocused = isFocused; if (!isFocused) { @@ -915,7 +942,7 @@ class Messages extends React.Component { } render() { - const { contacts, account, to, nodeError } = this.props; + const { contacts, account, to, the_group, nodeError } = this.props; let bbc, auc if (process.env.MOBILE_APP) { bbc = @@ -934,6 +961,8 @@ class Messages extends React.Component { conversationTopLeft={this._renderConversationTopLeft} /> ); + const toAcc = this.getToAcc() + return (
{bbc} @@ -946,7 +975,7 @@ class Messages extends React.Component { {Messenger ? ( dispatch(user.actions.showMyGroups()), fetchState: (to) => { - const pathname = '/' + (to ? ('@' + to) : ''); + const pathname = '/' + (to || '') dispatch({type: 'FETCH_STATE', payload: { location: { pathname diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index ca295b03d..4afe4d32f 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -109,6 +109,11 @@ "start_chat": "Начать чат", "create_group": "Создать группу" }, + "msgs_chat_error": { + "404_group": "Такой группы у нас нет", + "404_acc": "Такого пользователя нет", + "404_but": "Но у нас есть много интересного..." + }, "create_group_jsx": { "title": "Название", "name": "Ссылка chat.golos.app/", diff --git a/src/redux/FetchDataSaga.js b/src/redux/FetchDataSaga.js index e19cbd27b..232521e39 100644 --- a/src/redux/FetchDataSaga.js +++ b/src/redux/FetchDataSaga.js @@ -61,15 +61,33 @@ export function* fetchState(location_change_action) { state.contacts = yield callSafe(state, [], 'getContactsAsync', [api, api.getContactsAsync], account, 'unknown', 100, 0) if (hasErr) return - if (parts[1]) { - const to = parts[1].replace('@', ''); - accounts.add(to); - - state.messages = yield callSafe(state, [], 'getThreadAsync', [api, api.getThreadAsync], account, to, {}); - if (hasErr) return - - if (state.messages.length) { - state.messages_update = state.messages[state.messages.length - 1].nonce; + const path = parts[1] + if (path) { + if (path.startsWith('@')) { + const to = path.replace('@', ''); + accounts.add(to); + + state.messages = yield callSafe(state, [], 'getThreadAsync', [api, api.getThreadAsync], account, to, {}); + if (hasErr) return + + if (state.messages.length) { + state.messages_update = state.messages[state.messages.length - 1].nonce; + } + } else { + let the_group = yield callSafe(state, [], 'getGroupsAsync', [api, api.getGroupsAsync], { + start_group: path, + limit: 1, + with_members: { + accounts: [account] + } + }) + if (hasErr) return + if (the_group[0] && the_group[0].name === path) { + the_group = the_group[0] + } else { + the_group = null + } + state.the_group = the_group } } for (let contact of state.contacts) { diff --git a/src/utils/Normalizators.js b/src/utils/Normalizators.js index 9200d4da5..844a63486 100644 --- a/src/utils/Normalizators.js +++ b/src/utils/Normalizators.js @@ -68,6 +68,8 @@ export function normalizeContacts(contacts, accounts, currentUser, preDecoded, c } export function normalizeMessages(messages, accounts, currentUser, to, preDecoded) { + if (to) to = to.replace('@', '') + if (!to || !accounts[to]) { return []; }