diff --git a/app/MainApp.js b/app/MainApp.js index 88ab8e230..c541122a4 100644 --- a/app/MainApp.js +++ b/app/MainApp.js @@ -50,18 +50,20 @@ async function initState() { } } + let splashTimeout = setTimeout(() => { + closeSplash() + showNodeError() + }, 30000) + try { $STM_Config.add_notify_site = await checkUpdates() } catch (err) { console.error('Cannot check updates', err) + clearTimeout(splashTimeout) + closeSplash() alert('Cannot check updates' + err) } - let splashTimeout = setTimeout(() => { - closeSplash() - showNodeError() - }, 30000) - let onchain let nodeError = null try { diff --git a/app/assets/images/app/linux.png b/app/assets/images/app/linux.png new file mode 100644 index 000000000..fc1ffd38d Binary files /dev/null and b/app/assets/images/app/linux.png differ diff --git a/app/assets/images/app/windows.png b/app/assets/images/app/windows.png new file mode 100644 index 000000000..193679ffb Binary files /dev/null and b/app/assets/images/app/windows.png differ diff --git a/app/components/App.jsx b/app/components/App.jsx index 51fa84ded..582272a79 100644 --- a/app/components/App.jsx +++ b/app/components/App.jsx @@ -12,6 +12,7 @@ import { createGlobalStyle } from 'styled-components' import AppPropTypes from 'app/utils/AppPropTypes'; import Header from 'app/components/modules/Header'; import Footer from 'app/components/modules/Footer'; +import AppReminder from 'app/components/elements/app/AppReminder' import URLLoader from 'app/components/elements/app/URLLoader'; import TooltipManager from 'app/components/elements/common/TooltipManager'; import user from 'app/redux/User'; @@ -32,6 +33,8 @@ import { APP_ICON, VEST_TICKER, } from 'app/client_config'; import session from 'app/utils/session' import { loadGrayHideSettings } from 'app/utils/ContentAccess' +const APP_REMINDER_INTERVAL = 30*24*60*60*1000 + const GlobalStyle = createGlobalStyle` body { fill: currentColor; @@ -97,6 +100,16 @@ class App extends React.Component { } } + showAppReminder = () => { + if (process.env.IS_APP || typeof(localStorage) === 'undefined') { + return false + } + const now = Date.now() + let reminded = localStorage.getItem('app_reminder') || 0 + reminded = parseInt(reminded) + return !reminded || (now - reminded > APP_REMINDER_INTERVAL) + } + constructor(props) { super(props) if (process.env.BROWSER) { @@ -367,6 +380,8 @@ class App extends React.Component { const noHeader = isApp const noFooter = isApp || location.pathname.startsWith('/submit') + const reminder = this.showAppReminder() ? : null + return (
{children} {noFooter ? null :
} + {reminder}
diff --git a/app/components/all.scss b/app/components/all.scss index 31428c46c..c898a4044 100644 --- a/app/components/all.scss +++ b/app/components/all.scss @@ -39,6 +39,7 @@ @import "./elements/VerticalMenu"; @import "./elements/VotesAndComments"; @import "./elements/Voting"; +@import "./elements/app/AppReminder"; @import "./elements/common/YoutubePlayer/YoutubePlayer"; @import "./elements/common/TelegramPlayer/TelegramPlayer"; @import "./elements/common/Button/index"; diff --git a/app/components/elements/app/AppReminder.jsx b/app/components/elements/app/AppReminder.jsx new file mode 100644 index 000000000..7043fc288 --- /dev/null +++ b/app/components/elements/app/AppReminder.jsx @@ -0,0 +1,53 @@ +import React from 'react' +import tt from 'counterpart' +import { connect } from 'react-redux' + +import CloseButton from 'react-foundation-components/lib/global/close-button' + +import user from 'app/redux/User' + +class AppReminder extends React.Component { + state = { + hidden: false + } + + hideMe = () => { + const now = Date.now() + localStorage.setItem('app_reminder', now) + this.setState({ + hidden: true + }) + } + + showModal = (e) => { + e.preventDefault() + this.props.showModal() + this.hideMe() + } + + render() { + if (this.state.hidden) { + return null + } + return + { + e.stopPropagation() + this.hideMe() + }} + /> + {tt('app_reminder.text')} + + } +} + +export default connect( + state => { + return {} + }, + dispatch => ({ + showModal: () => { + dispatch(user.actions.showAppDownload()) + } + }) +)(AppReminder) diff --git a/app/components/elements/app/AppReminder.scss b/app/components/elements/app/AppReminder.scss new file mode 100644 index 000000000..a704683d9 --- /dev/null +++ b/app/components/elements/app/AppReminder.scss @@ -0,0 +1,14 @@ +.AppReminder { + background-color: #d0edff !important; + color: #333; + border-radius: 5px; + position: fixed; + left: 20px; + bottom: 1px; + padding-right: 5rem; + cursor: pointer; + + .close-button { + margin-top: 4px; + } +} diff --git a/app/components/modules/Modals.jsx b/app/components/modules/Modals.jsx index 781ad920a..de745b5b9 100644 --- a/app/components/modules/Modals.jsx +++ b/app/components/modules/Modals.jsx @@ -10,6 +10,7 @@ import Donate from 'app/components/modules/Donate' import SignUp from 'app/components/modules/SignUp' import ChangeAccount from 'app/components/modules/ChangeAccount' import AddAccount from 'app/components/modules/AddAccount' +import AppDownload from 'app/components/modules/app/AppDownload' import user from 'app/redux/User'; import tr from 'app/redux/Transaction'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; @@ -25,11 +26,13 @@ class Modals extends React.Component { show_promote_post_modal: PropTypes.bool, show_change_account_modal: PropTypes.bool, show_add_account_modal: PropTypes.bool, + show_app_download_modal: PropTypes.bool, hideLogin: PropTypes.func.isRequired, hideConfirm: PropTypes.func.isRequired, hideSignUp: PropTypes.func.isRequired, hideDonate: PropTypes.func.isRequired, hidePromotePost: PropTypes.func.isRequired, + hideAppDownload: PropTypes.func.isRequired, notifications: PropTypes.object, removeNotification: PropTypes.func, }; @@ -53,12 +56,14 @@ class Modals extends React.Component { show_signup_modal, show_change_account_modal, show_add_account_modal, + show_app_download_modal, hideLogin, hideDonate, hideConfirm, hideSignUp, hideChangeAccount, hideAddAccount, + hideAppDownload, notifications, removeNotification, } = this.props; @@ -96,6 +101,10 @@ class Modals extends React.Component { } + {show_app_download_modal && + + + } ); } @@ -114,6 +123,7 @@ export default connect( show_signup_modal: state.user.get('show_signup_modal'), show_change_account_modal: state.user.get('show_change_account_modal'), show_add_account_modal: state.user.get('show_add_account_modal'), + show_app_download_modal: state.user.get('show_app_download_modal'), notifications: state.app.get('notifications'), } }, @@ -146,6 +156,10 @@ export default connect( if (e) e.preventDefault(); dispatch(user.actions.hideAddAccount()) }, + hideAppDownload: e => { + if (e) e.preventDefault() + dispatch(user.actions.hideAppDownload()) + }, // example: addNotification: ({key, message}) => dispatch({type: 'ADD_NOTIFICATION', payload: {key, message}}), removeNotification: (key) => dispatch({type: 'REMOVE_NOTIFICATION', payload: {key}}), diff --git a/app/components/modules/app/AppDownload.jsx b/app/components/modules/app/AppDownload.jsx new file mode 100644 index 000000000..850c3edae --- /dev/null +++ b/app/components/modules/app/AppDownload.jsx @@ -0,0 +1,26 @@ +import React from 'react' +import tt from 'counterpart' + +class AppDownload extends React.Component { + componentDidMount() { + } + + render() { + const updaterHost = 'https://files.golos.app' + const winUrl = new URL('/api/exe/desktop/windows/latest', updaterHost) + const linuxUrl = new URL('/api/exe/desktop/linux/latest', updaterHost) + return
+

{tt('app_download.title')}

+ + + Windows +
+ + + Linux (deb) + +
+ } +} + +export default AppDownload diff --git a/app/locales/en.json b/app/locales/en.json index 08223f114..35ef47ad9 100644 --- a/app/locales/en.json +++ b/app/locales/en.json @@ -1056,6 +1056,12 @@ "mention_comment": " mentioned you in their comment", "message": " sent you a private message" }, + "app_download": { + "title": "Download Golos Desktop" + }, + "app_reminder": { + "text": "Install desktop application for Windows or Linux to receive data directly from blockchain" + }, "app_goto_url": { "goto": "Goto", "wrong_domain_DOMAINS": "This address is not belong to GOLOS Blogs.\nGOLOS Blogs domains are: %(DOMAINS)s\nThis address will be opened in external browser." diff --git a/app/locales/ru-RU.json b/app/locales/ru-RU.json index 4a86d6f3d..d0fa3383b 100644 --- a/app/locales/ru-RU.json +++ b/app/locales/ru-RU.json @@ -1123,6 +1123,13 @@ "mention_comment": " упомянул вас в комментарии", "message": " написал вам сообщение" }, + "app_download": { + "title": "Скачать Golos Desktop", + "download_for": "Скачать для" + }, + "app_reminder": { + "text": "Установите десктоп-приложение для Windows или Linux и получайте информацию напрямую с блокчейна" + }, "app_goto_url": { "goto": "Перейти", "wrong_domain_DOMAINS": "Похоже, эта ссылка не с GOLOS Блогов.\nДомены GOLOS Блогов: %(DOMAINS)s\nЭта ссылка будет открыта во внешнем браузере." diff --git a/app/redux/FetchDataSaga.js b/app/redux/FetchDataSaga.js index 5b41f058a..5b51a4aaa 100644 --- a/app/redux/FetchDataSaga.js +++ b/app/redux/FetchDataSaga.js @@ -540,6 +540,7 @@ export function* fetchData(action) { args[0].select_authors = [accountname]; } else if (order === 'by_author') { call_name = 'getDiscussionsByBlogAsync'; + args[0].filter_tags = args[0].filter_tags.filter(tag => tag !== 'onlyblog') delete args[0].select_tags; delete args[0].select_categories; delete args[0].prefs diff --git a/app/redux/User.js b/app/redux/User.js index e93493851..24bf2fa97 100644 --- a/app/redux/User.js +++ b/app/redux/User.js @@ -14,6 +14,7 @@ const defaultState = fromJS({ show_open_orders_modal: false, show_change_account_modal: false, show_add_account_modal: false, + show_app_download_modal: false, pub_keys_used: null, locale: DEFAULT_LANGUAGE, nightmodeEnabled: false, @@ -86,6 +87,8 @@ export default createModule({ { action: 'HIDE_CHANGE_ACCOUNT', reducer: state => state.set('show_change_account_modal', false) }, { action: 'SHOW_ADD_ACCOUNT', reducer: state => state.set('show_add_account_modal', true) }, { action: 'HIDE_ADD_ACCOUNT', reducer: state => state.set('show_add_account_modal', false) }, + { action: 'SHOW_APP_DOWNLOAD', reducer: state => state.set('show_app_download_modal', true) }, + { action: 'HIDE_APP_DOWNLOAD', reducer: state => state.set('show_app_download_modal', false) }, { action: 'USERNAME_PASSWORD_LOGIN', reducer: state => state, // saga