Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
PLT-3640 Add mobile landing pages (#3674)
Browse files Browse the repository at this point in the history
* PLT-3640 Moved all clientside user agent snooping into a single file

* PLT-3640 Added mobile landing pages on login to iOS and Android web apps

* PLT-3640 Moved landing page to appear before first login

* PLT-3640 Fixed detection of Chrome on Android

* PLT-3640 Disabled mobile landing pages when their respective URLs are set to blank
  • Loading branch information
hmhealey authored and coreyhulen committed Aug 3, 2016
1 parent 0955fa3 commit 43b7c29
Show file tree
Hide file tree
Showing 19 changed files with 398 additions and 82 deletions.
5 changes: 3 additions & 2 deletions components/file_upload.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Client from 'client/web_client.jsx';
import Constants from 'utils/constants.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';

import {intlShape, injectIntl, defineMessages} from 'react-intl';
Expand Down Expand Up @@ -311,13 +312,13 @@ class FileUpload extends React.Component {

render() {
let multiple = true;
if (Utils.isMobileApp()) {
if (UserAgent.isMobileApp()) {
// iOS WebViews don't upload videos properly in multiple mode
multiple = false;
}

let accept = '';
if (Utils.isIosChrome()) {
if (UserAgent.isIosChrome()) {
// iOS Chrome can't upload videos at all
accept = 'image/*';
}
Expand Down
75 changes: 75 additions & 0 deletions components/get_android_app/get_android_app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import React from 'react';

import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router';

import MattermostIcon from 'images/favicon/android-chrome-192x192.png';
import Nexus6Mockup from 'images/nexus-6p-mockup.png';

export default class GetAndroidApp extends React.Component {
render() {
return (
<div className='get-app get-android-app'>
<h1 className='get-app__header'>
<FormattedMessage
id='get_app.androidHeader'
defaultMessage='Mattermost works best if you switch to our Android app'
/>
</h1>
<hr/>
<div>
<img
className='get-android-app__icon'
src={MattermostIcon}
/>
<div className='get-android-app__app-info'>
<span className='get-android-app__app-name'>
<FormattedMessage
id='get_app.androidAppName'
defaultMessage='Mattermost for Android'
/>
</span>
<span className='get-android-app__app-creator'>
<FormattedMessage
id='get_app.mattermostInc'
defaultMessage='Mattermost, Inc'
/>
</span>
</div>
</div>
<a
className='btn btn-primary get-android-app__continue'
href={global.window.mm_config.AndroidAppDownloadLink}
>
<FormattedMessage
id='get_app.continue'
defaultMessage='Continue'
/>
</a>
<img
className='get-app__screenshot'
src={Nexus6Mockup}
/>
<span className='get-app__continue-with-browser'>
<FormattedMessage
id='get_app.continueWithBrowser'
defaultMessage='Or {link}'
values={{
link: (
<Link to='/switch_team'>
<FormattedMessage
id='get_app.continueWithBrowserLink'
defaultMessage='continue with browser'
/>
</Link>
)
}}
/>
</span>
</div>
);
}
}
68 changes: 68 additions & 0 deletions components/get_ios_app/get_ios_app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import React from 'react';

import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router';

import AppStoreButton from 'images/app-store-button.png';
import IPhone6Mockup from 'images/iphone-6-mockup.png';

export default class GetIosApp extends React.Component {
render() {
return (
<div className='get-app get-ios-app'>
<h1 className='get-app__header'>
<FormattedMessage
id='get_app.iosHeader'
defaultMessage='Mattermost works best if you switch to our iPhone app'
/>
</h1>
<hr/>
<a
className='get-ios-app__app-store-link'
href={global.window.mm_config.IosAppDownloadLink}
rel='noopener noreferrer'
>
<img src={AppStoreButton}/>
</a>
<img
className='get-app__screenshot'
src={IPhone6Mockup}
/>
<h2 className='get-ios-app__already-have-it'>
<FormattedMessage
id='get_app.alreadyHaveIt'
defaultMessage='Already have it?'
/>
</h2>
<a
className='btn btn-primary get-ios-app__open-mattermost'
href='mattermost:https://'
>
<FormattedMessage
id='get_app.openMattermost'
defaultMessage='Open Mattermost'
/>
</a>
<span className='get-app__continue-with-browser'>
<FormattedMessage
id='get_app.continueWithBrowser'
defaultMessage='Or {link}'
values={{
link: (
<Link to='/switch_team'>
<FormattedMessage
id='get_app.continueWithBrowserLink'
defaultMessage='continue with browser'
/>
</Link>
)
}}
/>
</span>
</div>
);
}
}
5 changes: 4 additions & 1 deletion components/new_channel_modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';

import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';

Expand Down Expand Up @@ -53,9 +54,11 @@ class NewChannelModal extends React.Component {
}

componentDidMount() {
if (Utils.isBrowserIE()) {
// ???
if (UserAgent.isInternetExplorer()) {
$('body').addClass('browser--ie');
}

PreferenceStore.addChangeListener(this.onPreferenceChange);
}

Expand Down
3 changes: 2 additions & 1 deletion components/post_view/components/post_list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as GlobalActions from 'actions/global_actions.jsx';

import {createChannelIntroMessage} from 'utils/channel_intro_messages.jsx';

import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
Expand Down Expand Up @@ -336,7 +337,7 @@ export default class PostList extends React.Component {

// Temporary fix to solve ie11 rendering issue
let newSeparatorId = '';
if (!Utils.isBrowserIE()) {
if (!UserAgent.isInternetExplorer()) {
newSeparatorId = 'new_message_' + post.id;
}
postCtls.push(
Expand Down
3 changes: 2 additions & 1 deletion components/select_team/select_team.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import UserStore from 'stores/user_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import ErrorBar from 'components/error_bar.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
Expand Down Expand Up @@ -176,7 +177,7 @@ export default class SelectTeam extends React.Component {
}

let teamSignUp;
if (isSystemAdmin || (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp())) {
if (isSystemAdmin || (global.window.mm_config.EnableTeamCreation === 'true' && !UserAgent.isMobileApp())) {
teamSignUp = (
<div className='margin--extra'>
<Link
Expand Down
4 changes: 2 additions & 2 deletions components/settings_sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// See License.txt for license information.

import $ from 'jquery';
import * as Utils from 'utils/utils.jsx';
import * as UserAgent from 'utils/user_agent.jsx';

import React from 'react';

Expand All @@ -18,7 +18,7 @@ export default class SettingsSidebar extends React.Component {
$(e.target).closest('.settings-modal').addClass('display--content');
}
componentDidMount() {
if (Utils.isBrowserFirefox()) {
if (UserAgent.isFirefox()) {
$('.settings-modal .settings-table .nav').addClass('position--top');
}
}
Expand Down
3 changes: 2 additions & 1 deletion components/sidebar_right_menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import TeamStore from 'stores/team_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';

import * as GlobalActions from 'actions/global_actions.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';

Expand Down Expand Up @@ -296,7 +297,7 @@ export default class SidebarRightMenu extends React.Component {
}

let nativeAppLink = null;
if (global.window.mm_config.AppDownloadLink && !Utils.isMobileApp()) {
if (global.window.mm_config.AppDownloadLink && !UserAgent.isMobileApp()) {
nativeAppLink = (
<li>
<Link
Expand Down
3 changes: 2 additions & 1 deletion components/user_settings/user_settings_notifications.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import UserStore from 'stores/user_store.jsx';

import Client from 'client/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';

import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';

function getNotificationsStateFromStores() {
var user = UserStore.getCurrentUser();
var soundNeeded = !Utils.isBrowserFirefox();
var soundNeeded = !UserAgent.isFirefox();

var sound = 'true';
if (user.notify_props && user.notify_props.desktop_sound) {
Expand Down
9 changes: 9 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,15 @@
"general_tab.teamNameInfo": "Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.",
"general_tab.title": "General Settings",
"general_tab.yes": "Yes",
"get_app.alreadyHaveIt": "Already have it?",
"get_app.androidAppName": "Mattermost for Android",
"get_app.androidHeader": "Mattermost works best if you switch to our Android app",
"get_app.continue": "continue",
"get_app.continueWithBrowser": "Or {link}",
"get_app.continueWithBrowserLink": "continue with browser",
"get_app.iosHeader": "Mattermost works best if you switch to our iPhone app",
"get_app.mattermostInc": "Mattermost, Inc",
"get_app.openMattermost": "Open Mattermost",
"get_link.clipboard": " Link copied to clipboard.",
"get_link.close": "Close",
"get_link.copy": "Copy Link",
Expand Down
Binary file added images/app-store-button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/iphone-6-mockup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/nexus-6p-mockup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions routes/route_root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,23 @@ import claimAccountRoute from 'routes/route_claim.jsx';
import createTeamRoute from 'routes/route_create_team.jsx';
import teamRoute from 'routes/route_team.jsx';

import BrowserStore from 'stores/browser_store.jsx';
import ErrorStore from 'stores/error_store.jsx';
import * as UserAgent from 'utils/user_agent.jsx';

function preLogin(nextState, replace, callback) {
// redirect to the mobile landing page if the user hasn't seen it before
if (window.mm_config.IosAppDownloadLink && UserAgent.isIosWeb() && !BrowserStore.hasSeenLandingPage()) {
replace('/get_ios_app');
BrowserStore.setLandingPageSeen(true);
} else if (window.mm_config.AndroidAppDownloadLink && UserAgent.isAndroidWeb() && !BrowserStore.hasSeenLandingPage()) {
replace('/get_android_app');
BrowserStore.setLandingPageSeen(true);
}

callback();
}

function preLoggedIn(nextState, replace, callback) {
ErrorStore.clearLastError();
callback();
Expand All @@ -28,6 +44,7 @@ export default {
[
{
path: 'login',
onEnter: preLogin,
getComponents: (location, callback) => {
System.import('components/login/login_controller.jsx').then(RouteUtils.importComponentSuccess(callback));
}
Expand Down Expand Up @@ -66,6 +83,18 @@ export default {
]
)
},
{
path: 'get_ios_app',
getComponents: (location, callback) => {
System.import('components/get_ios_app/get_ios_app.jsx').then(RouteUtils.importComponentSuccess(callback));
}
},
{
path: 'get_android_app',
getComponents: (location, callback) => {
System.import('components/get_android_app/get_android_app.jsx').then(RouteUtils.importComponentSuccess(callback));
}
},
{
path: 'error',
getComponents: (location, callback) => {
Expand Down
Loading

0 comments on commit 43b7c29

Please sign in to comment.