diff --git a/config/mobile.json b/config/mobile.json index 332dc1e3e..6168e18d2 100644 --- a/config/mobile.json +++ b/config/mobile.json @@ -17,7 +17,8 @@ "custom_client": "blogs" }, "notify_service": { - "host": "https://notify.golos.app" + "host": "https://notify.golos.app", + "host_ws": "wss://notify.golos.app/ws" }, "blogs_service": { "host": "https://golos.id" diff --git a/src/components/elements/VerticalMenu.scss b/src/components/elements/VerticalMenu.scss index 9bdf1c161..d3aba6adc 100644 --- a/src/components/elements/VerticalMenu.scss +++ b/src/components/elements/VerticalMenu.scss @@ -55,6 +55,10 @@ padding: 0 1rem 0 3.8rem; line-height: 50px; + @media screen and (max-width: 39.9375em) { + padding: 0 20px; + } + .Icon { @include themify($themes) { fill: themed('textColorPrimary'); diff --git a/src/components/elements/groups/GroupMember.jsx b/src/components/elements/groups/GroupMember.jsx index f43591870..30a7855d6 100644 --- a/src/components/elements/groups/GroupMember.jsx +++ b/src/components/elements/groups/GroupMember.jsx @@ -6,6 +6,7 @@ import Icon from 'app/components/elements/Icon' import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper' import Userpic from 'app/components/elements/Userpic' import { getRoleInGroup } from 'app/utils/groups' +import isScreenSmall from 'app/utils/isScreenSmall' class GroupMember extends React.Component { // shouldComponentUpdate(nextProps) { @@ -96,6 +97,8 @@ class GroupMember extends React.Component { onClick={e => this.groupMember(e, member, 'retired')} /> } + const isSmall = isScreenSmall() + return @@ -106,7 +109,7 @@ class GroupMember extends React.Component { - {!creatingNew && } + {!isSmall && !creatingNew && } {isOwner && { return { - name: tt('create_group_jsx.step_name'), - logo: tt('create_group_jsx.step_logo'), - members: tt('create_group_jsx.step_members'), - final: tt('create_group_jsx.step_create') -} } +const STEPS = () => { + const isSmall = isScreenSmall() + return { + name: tt('create_group_jsx.step_name'), + logo: isSmall ? tt('create_group_jsx.step_logo_mobile') : tt('create_group_jsx.step_logo'), + members: isSmall ? tt('create_group_jsx.step_members_mobile') : tt('create_group_jsx.step_members'), + final: isSmall ? tt('create_group_jsx.step_create_mobile') : tt('create_group_jsx.step_create') + } +} class ActionOnUnmount extends React.Component { componentWillUnmount() { diff --git a/src/components/modules/MessagesTopCenter.jsx b/src/components/modules/MessagesTopCenter.jsx index 0726b4d02..a4744ff6d 100644 --- a/src/components/modules/MessagesTopCenter.jsx +++ b/src/components/modules/MessagesTopCenter.jsx @@ -229,6 +229,31 @@ class MessagesTopCenter extends React.Component { } + showErrorLogs = (e) => { + e.preventDefault() + e.stopPropagation() + try { + const { errorLogs } = this.props + let msg = '' + for (const err of errorLogs) { + msg += (err.err ? err.err.toString() : '') + '\n' + JSON.stringify(err.details) + '\n\n' + } + alert(msg) + } catch (err) { + alert('Cannot display error logs, due to: ' + (err && err.toString())) + } + } + + refreshSync = e => { + this.setState({ refreshing: true }) + e.preventDefault() + e.stopPropagation() + this.props.fetchState(this.props.to) + setTimeout(() => { + this.setState({ refreshing: false }) + }, 500) + } + render() { let avatar = [] let items = [] @@ -257,7 +282,7 @@ class MessagesTopCenter extends React.Component { closeOnClickOutside dropdownClassName="GroupDropdown" dropdownPosition="bottom" - dropdownAlignment="center" + dropdownAlignment="right" dropdownContent={this._renderGroupDropdown()} transition={Fade} > @@ -282,12 +307,13 @@ class MessagesTopCenter extends React.Component { } if (notifyErrors >= 30) { + const { refreshing } = this.state items.push(
{isSmall ? - + {tt('messages.sync_error_short')} - { e.preventDefault(); this.props.fetchState(this.props.to) }}> - {tt('g.refresh').toLowerCase()}. + + {refreshing ? '...' : tt('g.refresh')}. : {tt('messages.sync_error')} diff --git a/src/components/modules/groups/MyGroups.jsx b/src/components/modules/groups/MyGroups.jsx index f818bf340..9de07e978 100644 --- a/src/components/modules/groups/MyGroups.jsx +++ b/src/components/modules/groups/MyGroups.jsx @@ -4,6 +4,7 @@ import { Link } from 'react-router-dom' import { Map } from 'immutable' import { api, formatter } from 'golos-lib-js' import tt from 'counterpart' +import cn from 'classnames' import DialogManager from 'app/components/elements/common/DialogManager' import g from 'app/redux/GlobalReducer' @@ -15,6 +16,7 @@ import LoadingIndicator from 'app/components/elements/LoadingIndicator' import MarkNotificationRead from 'app/components/elements/MarkNotificationRead' import { showLoginDialog } from 'app/components/dialogs/LoginDialog' import { getGroupLogo, getGroupMeta, getRoleInGroup } from 'app/utils/groups' +import isScreenSmall from 'app/utils/isScreenSmall' class MyGroups extends React.Component { constructor(props) { @@ -126,10 +128,13 @@ class MyGroups extends React.Component { const meta = getGroupMeta(json_metadata) + const isSmall = isScreenSmall() + + const maxLength = isSmall ? 15 : 20 let title = meta.title || name let titleShr = title - if (titleShr.length > 20) { - titleShr = titleShr.substring(0, 17) + '...' + if (titleShr.length > maxLength) { + titleShr = titleShr.substring(0, maxLength - 3) + '...' } const kebabItems = [] @@ -161,7 +166,9 @@ class MyGroups extends React.Component { e.preventDefault() e.stopPropagation() }}> - {amPending ? : null} - diff --git a/src/components/modules/groups/MyGroups.scss b/src/components/modules/groups/MyGroups.scss index b7634153f..d0cfc5749 100644 --- a/src/components/modules/groups/MyGroups.scss +++ b/src/components/modules/groups/MyGroups.scss @@ -39,4 +39,9 @@ margin-left: 5px; vertical-align: middle; } + .button.icon-only { + .btn-title { + display: none; + } + } } diff --git a/src/components/pages/Messages.jsx b/src/components/pages/Messages.jsx index aa6608a67..6b0c40241 100644 --- a/src/components/pages/Messages.jsx +++ b/src/components/pages/Messages.jsx @@ -34,7 +34,7 @@ import { getProfileImage, } from 'app/utils/NormalizeProfile'; import { normalizeContacts, normalizeMessages, cacheMyOwnMsg } from 'app/utils/Normalizators'; import { fitToPreview } from 'app/utils/ImageUtils'; import { notificationSubscribe, notificationSubscribeWs, notifyWsPing, - notificationShallowUnsubscribe, notificationTake, queueWatch, sendOffchainMessage } from 'app/utils/NotifyApiClient'; + notificationShallowUnsubscribe, notificationTake, queueWatch, sendOffchainMessage, notifyWsHost, notifyUrl } from 'app/utils/NotifyApiClient'; import { flash, unflash } from 'app/components/elements/messages/FlashTitle'; import { addShortcut } from 'app/utils/app/ShortcutUtils' import { hideSplash } from 'app/utils/app/SplashUtils' @@ -44,6 +44,7 @@ import { proxifyImageUrl } from 'app/utils/ProxifyUrl' class Messages extends React.Component { constructor(props) { super(props); + window.errorLogs = window.errorLogs || [] this.state = { contacts: [], messages: [], @@ -78,7 +79,6 @@ class Messages extends React.Component { //alert('scrollToMention ' + messages.length) let nonce for (const msg of messages) { - console.log(msg.read_date) if (msg.to === username && msg.read_date && msg.read_date.startsWith('1970')) { nonce = msg.nonce break @@ -182,16 +182,21 @@ class Messages extends React.Component { } notifyErrorsClear = () => { - if (this.state.notifyErrors) + if (this.state.notifyErrors) { + window.errorLogs = [] this.setState({ notifyErrors: 0, }); + } }; - notifyErrorsInc = (score) => { + notifyErrorsInc = (score, err, errDetails) => { this.setState({ notifyErrors: this.state.notifyErrors + score, }); + if (err) { + window.errorLogs.push({ err, details: errDetails }) + } }; checkLoggedOut = (username) => { @@ -256,7 +261,7 @@ class Messages extends React.Component { return true } catch (err) { console.error('watchGroup - ', to, err) - this.notifyErrorsInc(30) + this.notifyErrorsInc(30, err, {watchGroup: notifyUrl()}) } return false } @@ -309,7 +314,7 @@ class Messages extends React.Component { console.log('WSS:', subscribed) } catch (ex) { console.error('notificationSubscribe', ex) - this.notifyErrorsInc(15) + this.notifyErrorsInc(15, ex, {subscribeWs: notifyWsHost()}) setTimeout(() => { this.setCallback(username) }, 5000) @@ -330,12 +335,11 @@ class Messages extends React.Component { this.notifyErrorsClear() } catch (err) { console.error('Notify ping failed', err) - this.notifyErrorsInc(10) + this.notifyErrorsInc(10, err, {wsPing: notifyWsHost()}) } } setTimeout(ping, 10000) } - ping(true) this.watchGroup(this.props.to) } @@ -373,6 +377,9 @@ class Messages extends React.Component { if (this.props.to !== prevProps.to || (this.isChat() && loggedNow)) { this.checkUserAuth() } + if (prevProps.username && !this.props.username) { + this.checkUserAuth(true) + } if (loggedNow) { this.props.fetchState(this.props.to); this.setCallback(this.props.username) @@ -896,15 +903,17 @@ class Messages extends React.Component { }; _renderMessagesTopCenter = ({ isSmall }) => { - const { to } = this.props + const { fetchState, to } = this.props const toAcc = this.getToAcc() - const { notifyErrors } = this.state + const { notifyErrors, } = this.state return }; @@ -966,7 +975,7 @@ class Messages extends React.Component { return (} >
diff --git a/src/components/pages/app/AppSettings.jsx b/src/components/pages/app/AppSettings.jsx index 6080b5ea2..dfb8ce402 100644 --- a/src/components/pages/app/AppSettings.jsx +++ b/src/components/pages/app/AppSettings.jsx @@ -14,6 +14,7 @@ class AppSettings extends React.Component { use_img_proxy: $GLS_Config.images.use_img_proxy, auth_service: $GLS_Config.auth_service.host, notify_service: $GLS_Config.notify_service.host, + notify_service_ws: $GLS_Config.notify_service.host_ws, blogs_service: $GLS_Config.blogs_service.host, } this.initialValues = initialValues @@ -78,6 +79,7 @@ class AppSettings extends React.Component { cfg.images.use_img_proxy = data.use_img_proxy cfg.auth_service.host = data.auth_service cfg.notify_service.host = data.notify_service + cfg.notify_service.host_ws = data.notify_service_ws cfg.blogs_service.host = data.blogs_service cfg = JSON.stringify(cfg) localStorage.setItem('app_settings', cfg) @@ -164,6 +166,17 @@ class AppSettings extends React.Component {
+
+
+ {tt('app_settings.notify_service_ws')} +
+ +
+
+
{tt('app_settings.blogs_service')} diff --git a/src/locales/en.json b/src/locales/en.json index 55d6d22e2..3f0dbb4e7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -107,7 +107,7 @@ "new_message5": " notifications", "invalid_message": "(This message could not be displayed in Golos Messenger)", "sync_error": "Sync error. To receive new messages please refresh the page.", - "sync_error_short": "Sync error. To receive new messages touch ", + "sync_error_short": "Sync error. ", "blocked_BY": "You are blocked by @%(BY)s.", "do_not_bother_BY": "@%(BY)s wants to not be bothered by low-reputation users." }, @@ -156,8 +156,11 @@ "submit": "Create", "step_name": "Name", "step_logo": "Logo", + "step_logo_mobile": "Logo", "step_members": "Members", + "step_members_mobile": "Membs", "step_create": "Create!", + "step_create_mobile": "Go!", "group_already_exists": "Group already exists.", "group_min_length": "Min is 3 symbols.", "golos_power_too_low": "To create group you should have Golos Power at least ", @@ -313,7 +316,8 @@ "current_node": "GOLOS node URL", "img_proxy_prefix": "Use Proxy For Images", "auth_service": "Golos Auth & Registration Service (for messenger)", - "notify_service": "Golos Notify Service (for messenger)", + "notify_service": "Golos Notify Service (for instant messages)", + "notify_service_ws": "Golos Notify Service WebSocket (for instant messages)", "blogs_service": "Blogs", "save": "Save", "node_error_NODE": "Cannot connect to %(NODE)s. It may be internet failure. Try ", @@ -374,6 +378,7 @@ "name": "Name", "night_mode": "Night Mode", "ok": "OK", + "or": "or", "refresh": "Refresh", "required": "Required", "replies": "Replies", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index 950087b87..910190bc4 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -108,7 +108,7 @@ "new_message5": " новых сообщений", "invalid_message": "(Это сообщение не может быть отображено в Golos Messenger)", "sync_error": "Ошибка синхронизации. Для получения новых сообщений обновляйте страницу.", - "sync_error_short": "Ошибка синхронизации. Для получения новых сообщений нажимайте ", + "sync_error_short": "Ошибка синхронизации. ", "blocked_BY": "Вы заблокированы пользователем @%(BY)s.", "do_not_bother_BY": "@%(BY)s просит пользователей с низкой репутацией не беспокоить.", "too_low_gp": "Не хватает Силы Голоса. Для участия в группах нужно не менее ", @@ -162,8 +162,11 @@ "submit": "Создать", "step_name": "Имя", "step_logo": "Логотип", + "step_logo_mobile": "Лого", "step_members": "Участники", + "step_members_mobile": "Люди", "step_create": "Создать!", + "step_create_mobile": "Go!", "group_already_exists": "Такая группа уже существует.", "validating": "Проверка существования группы...", "group_min_length": "Минимум 3 символа.", @@ -330,6 +333,7 @@ "img_proxy_prefix": "Использовать прокси для изображений", "auth_service": "Golos Auth & Registration Service (для мгновенных сообщений)", "notify_service": "Golos Notify Service (для мгновенных сообщений)", + "notify_service_ws": "Golos Notify Service WebSocket (для мгновенных сообщений)", "blogs_service": "Блоги", "save": "Сохранить", "node_error_NODE": "Не удалось подключиться к ноде %(NODE)s. Возможно, это проблемы с интернетом. Попробуйте ", diff --git a/src/redux/TransactionSaga.js b/src/redux/TransactionSaga.js index 7a8410b48..5c4ccc8ac 100644 --- a/src/redux/TransactionSaga.js +++ b/src/redux/TransactionSaga.js @@ -57,6 +57,7 @@ function* preBroadcast_custom_json({operation}) { } const newMsg = messageOpToObject(json[1], group, mentions) msgs = msgs.insert(0, fromJS(newMsg)) + messages_update = json[1].nonce; } else { messages_update = json[1].nonce; msgs = msgs.update(idx, msg => { diff --git a/src/redux/UserSaga.js b/src/redux/UserSaga.js index 3b57ef347..b2fa49b8f 100644 --- a/src/redux/UserSaga.js +++ b/src/redux/UserSaga.js @@ -134,10 +134,15 @@ function* usernamePasswordLogin(action) { yield put(user.actions.setUser({ username, private_keys, })) } + const { errorLogs } = window + if (postingWif) { let alreadyAuthorized = false; try { const res = yield notifyApiLogin(username, localStorage.getItem('X-Auth-Session')); + + errorLogs.push({ details: { notifyApiLogin1: res } }) + alreadyAuthorized = (res.status === 'ok'); } catch(error) { // Does not need to be fatal @@ -148,6 +153,9 @@ function* usernamePasswordLogin(action) { let authorized = false; try { const res = yield authApiLogin(username, null); + + errorLogs.push({ details: { authApiLogin1: res } }) + if (!res.already_authorized) { console.log('login_challenge', res.login_challenge); @@ -156,6 +164,9 @@ function* usernamePasswordLogin(action) { posting: postingWif, }); const res2 = yield authApiLogin(username, signatures); + + errorLogs.push({ details: { authApiLogin2: res2 } }) + if (res2.guid) { localStorage.setItem('guid', res2.guid) } @@ -174,6 +185,8 @@ function* usernamePasswordLogin(action) { try { const res = yield notifyApiLogin(username, localStorage.getItem('X-Auth-Session')); + errorLogs.push({ details: { notifyApiLogin2: res } }) + if (res.status !== 'ok') { throw new Error(res); } diff --git a/src/utils/NotifyApiClient.js b/src/utils/NotifyApiClient.js index 4df4ebca5..602892e90 100644 --- a/src/utils/NotifyApiClient.js +++ b/src/utils/NotifyApiClient.js @@ -14,13 +14,13 @@ const notifyAvailable = () => { && $GLS_Config.notify_service && $GLS_Config.notify_service.host; }; -const notifyWsAvailable = () => { +export function notifyWsHost() { return notifyAvailable() && $GLS_Config.notify_service.host_ws } -const notifyUrl = (pathname) => { +export function notifyUrl(pathname = '') { return new URL(pathname, window.$GLS_Config.notify_service.host).toString(); -}; +} function notifySession() { return localStorage.getItem('X-Session') @@ -50,7 +50,7 @@ async function connectNotifyWs() { window.notifyWs.close() } await new Promise((resolve, reject) => { - const notifyWs = new WebSocket($GLS_Config.notify_service.host_ws) + const notifyWs = new WebSocket(notifyWsHost()) window.notifyWs = notifyWs const timeout = setTimeout(() => { @@ -211,7 +211,7 @@ export async function notificationSubscribe(account, scopes = 'message,donate_ms } export async function notificationSubscribeWs(account, callback, scopes = 'message,donate_msgs', sidKey = '__subscriber_id') { - if (!notifyWsAvailable()) return null + if (!notifyWsHost()) throw new Error('No notify_service host_ws in config') const xSession = notifySession() return await new Promise(async (resolve, reject) => { await notifyWsSend('queues/subscribe', { @@ -327,7 +327,7 @@ export async function queueWatch(account, group, sidKey = '__subscriber_id') { } export async function queueWatchWs(account, group, sidKey = '__subscriber_id') { - if (!notifyWsAvailable()) return null + if (!notifyWsHost()) return null const xSession = notifySession() return await new Promise(async (resolve, reject) => { await notifyWsSend('queues/subscribe', { diff --git a/src/utils/app/SplashUtils.js b/src/utils/app/SplashUtils.js index fdd8b6658..1e0c4f305 100644 --- a/src/utils/app/SplashUtils.js +++ b/src/utils/app/SplashUtils.js @@ -1,10 +1,13 @@ export function hideSplash() { - if (process.env.MOBILE_APP) { - try { + try { + if (process.env.MOBILE_APP) { navigator.splashscreen.hide() - } catch (err) { - console.error('hideSplash', err) + } else if (process.env.DESKTOP_APP) { + if (window.appSplash) + window.appSplash.contentLoaded() } + } catch (err) { + console.error('hideSplash', err) } } diff --git a/src/utils/initConfig.js b/src/utils/initConfig.js index 290276805..ab252c8dc 100644 --- a/src/utils/initConfig.js +++ b/src/utils/initConfig.js @@ -23,7 +23,10 @@ const loadMobileConfig = async () => { if (cfg) { try { cfg = JSON.parse(cfg) - // Add here migrations in future, if need + // Add here migrations + if (cfg.notify_service && !cfg.notify_service.host_ws) { + delete cfg.notify_service + } cfg = { ...defaultCfg, ...cfg } } catch (err) { console.error('Cannot parse app_settings', err)