From d2544a79c8dacc47c58c46362ebb320bae47c2a3 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Thu, 23 Sep 2021 11:17:53 -0300 Subject: [PATCH] [IMPROVE] Onboarding changes (#3387) - Change the first screen of the app - Minor changes on NewServerView and make it the first screen of the app - Add "Create workspace" to ServerDropdown Co-authored-by: Diego Mello --- .../__snapshots__/Storyshots.test.js.snap | 3 + app/AppContainer.tsx | 6 +- app/actions/app.js | 1 - app/containers/Button/index.tsx | 6 +- app/reducers/server.js | 3 - app/sagas/deepLinking.js | 4 +- app/sagas/login.js | 24 +--- app/stacks/OutsideStack.js | 12 +- app/utils/log/events.js | 6 +- app/utils/scaling.js | 11 +- app/views/NewServerView/ServerInput/index.js | 4 +- app/views/NewServerView/index.js | 135 +++++++++++++----- app/views/OnboardingView/index.js | 87 ----------- app/views/OnboardingView/styles.js | 41 ------ app/views/RoomsListView/ServerDropdown.js | 31 +++- app/views/RoomsListView/index.js | 19 ++- app/views/RoomsListView/styles.js | 5 + app/views/WorkspaceView/index.js | 1 - e2e/helpers/app.js | 14 +- e2e/tests/assorted/07-changeserver.spec.js | 14 +- e2e/tests/assorted/12-i18n.spec.js | 13 +- e2e/tests/onboarding/01-onboarding.spec.js | 25 +--- .../onboarding/07-server-history.spec.js | 1 - 23 files changed, 187 insertions(+), 279 deletions(-) delete mode 100644 app/views/OnboardingView/index.js delete mode 100644 app/views/OnboardingView/styles.js diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 022ded85c..0ad82c0d0 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -1444,6 +1444,7 @@ Array [ Object { "fontSize": 12, }, + undefined, ] } > @@ -1605,6 +1606,7 @@ Array [ Object { "fontSize": 12, }, + undefined, ] } > @@ -41153,6 +41155,7 @@ exports[`Storyshots Message Show a button as attachment 1`] = ` "color": "#ffffff", }, undefined, + undefined, ] } > diff --git a/app/AppContainer.tsx b/app/AppContainer.tsx index 00d851010..f7f08bb27 100644 --- a/app/AppContainer.tsx +++ b/app/AppContainer.tsx @@ -5,7 +5,7 @@ import { connect } from 'react-redux'; import Navigation from './lib/Navigation'; import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation'; -import { ROOT_INSIDE, ROOT_LOADING, ROOT_NEW_SERVER, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app'; +import { ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app'; // Stacks import AuthLoadingView from './views/AuthLoadingView'; // SetUsername Stack @@ -56,9 +56,7 @@ const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail <> {root === ROOT_LOADING ? : null} - {root === ROOT_OUTSIDE || root === ROOT_NEW_SERVER ? ( - - ) : null} + {root === ROOT_OUTSIDE ? : null} {root === ROOT_INSIDE && isMasterDetail ? ( ) : null} diff --git a/app/actions/app.js b/app/actions/app.js index 9ced0c6c2..fe6981d67 100644 --- a/app/actions/app.js +++ b/app/actions/app.js @@ -3,7 +3,6 @@ import { APP } from './actionsTypes'; export const ROOT_OUTSIDE = 'outside'; export const ROOT_INSIDE = 'inside'; export const ROOT_LOADING = 'loading'; -export const ROOT_NEW_SERVER = 'newServer'; export const ROOT_SET_USERNAME = 'setUsername'; export function appStart({ root, ...args }) { diff --git a/app/containers/Button/index.tsx b/app/containers/Button/index.tsx index 4f57776d1..9e475a679 100644 --- a/app/containers/Button/index.tsx +++ b/app/containers/Button/index.tsx @@ -17,6 +17,7 @@ interface IButtonProps { color: string; fontSize: any; style: any; + styleText?: any; testID: string; } @@ -48,7 +49,8 @@ export default class Button extends React.PureComponent, a }; render() { - const { title, type, onPress, disabled, backgroundColor, color, loading, style, theme, fontSize, ...otherProps } = this.props; + const { title, type, onPress, disabled, backgroundColor, color, loading, style, theme, fontSize, styleText, ...otherProps } = + this.props; const isPrimary = type === 'primary'; let textColor = isPrimary ? themes[theme!].buttonText : themes[theme!].bodyText; @@ -72,7 +74,7 @@ export default class Button extends React.PureComponent, a {loading ? ( ) : ( - + {title} )} diff --git a/app/reducers/server.js b/app/reducers/server.js index f3934a68d..14c7bbfdf 100644 --- a/app/reducers/server.js +++ b/app/reducers/server.js @@ -7,7 +7,6 @@ const initialState = { server: '', version: null, loading: true, - adding: false, previousServer: null, changingServer: false }; @@ -58,13 +57,11 @@ export default function server(state = initialState, action) { case SERVER.INIT_ADD: return { ...state, - adding: true, previousServer: action.previousServer }; case SERVER.FINISH_ADD: return { ...state, - adding: false, previousServer: null }; default: diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 959a0802d..efad7afae 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -8,7 +8,7 @@ import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks' import database from '../lib/database'; import RocketChat from '../lib/rocketchat'; import EventEmitter from '../utils/events'; -import { ROOT_INSIDE, ROOT_NEW_SERVER, appInit, appStart } from '../actions/app'; +import { ROOT_INSIDE, ROOT_OUTSIDE, appInit, appStart } from '../actions/app'; import { localAuthenticate } from '../utils/localAuthentication'; import { goRoom } from '../utils/goRoom'; import { loginRequest } from '../actions/login'; @@ -180,7 +180,7 @@ const handleOpen = function* handleOpen({ params }) { yield fallbackNavigation(); return; } - yield put(appStart({ root: ROOT_NEW_SERVER })); + yield put(appStart({ root: ROOT_OUTSIDE })); yield put(serverInitAdd(server)); yield delay(1000); EventEmitter.emit('NewServer', { server: host }); diff --git a/app/sagas/login.js b/app/sagas/login.js index 516599c3d..c3f987054 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -118,8 +118,6 @@ const fetchRooms = function* fetchRooms() { const handleLoginSuccess = function* handleLoginSuccess({ user }) { try { - const adding = yield select(state => state.server.adding); - RocketChat.getUserPresence(user.id); const server = yield select(getServer); @@ -170,24 +168,10 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) { yield put(setUser(user)); EventEmitter.emit('connected'); - let currentRoot; - if (adding) { - yield put(serverFinishAdd()); - yield put(appStart({ root: ROOT_INSIDE })); - } else { - currentRoot = yield select(state => state.app.root); - if (currentRoot !== ROOT_INSIDE) { - yield put(appStart({ root: ROOT_INSIDE })); - } - } - - // after a successful login, check if it's been invited via invite link - currentRoot = yield select(state => state.app.root); - if (currentRoot === ROOT_INSIDE) { - const inviteLinkToken = yield select(state => state.inviteLinks.token); - if (inviteLinkToken) { - yield put(inviteLinksRequest(inviteLinkToken)); - } + yield put(appStart({ root: ROOT_INSIDE })); + const inviteLinkToken = yield select(state => state.inviteLinks.token); + if (inviteLinkToken) { + yield put(inviteLinksRequest(inviteLinkToken)); } } catch (e) { log(e); diff --git a/app/stacks/OutsideStack.js b/app/stacks/OutsideStack.js index 0e8a8f62e..f23e65c3d 100644 --- a/app/stacks/OutsideStack.js +++ b/app/stacks/OutsideStack.js @@ -1,13 +1,11 @@ import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; import { ThemeContext } from '../theme'; import { ModalAnimation, StackAnimation, defaultHeader, themedHeader } from '../utils/navigation'; // Outside Stack -import OnboardingView from '../views/OnboardingView'; import NewServerView from '../views/NewServerView'; import WorkspaceView from '../views/WorkspaceView'; import LoginView from '../views/LoginView'; @@ -15,18 +13,14 @@ import ForgotPasswordView from '../views/ForgotPasswordView'; import RegisterView from '../views/RegisterView'; import LegalView from '../views/LegalView'; import AuthenticationWebView from '../views/AuthenticationWebView'; -import { ROOT_OUTSIDE } from '../actions/app'; // Outside const Outside = createStackNavigator(); -const _OutsideStack = ({ root }) => { +const _OutsideStack = () => { const { theme } = React.useContext(ThemeContext); return ( - {root === ROOT_OUTSIDE ? ( - - ) : null} @@ -41,10 +35,6 @@ const mapStateToProps = state => ({ root: state.app.root }); -_OutsideStack.propTypes = { - root: PropTypes.string -}; - const OutsideStack = connect(mapStateToProps)(_OutsideStack); // OutsideStackModal diff --git a/app/utils/log/events.js b/app/utils/log/events.js index c39ccc424..4a3ec38df 100644 --- a/app/utils/log/events.js +++ b/app/utils/log/events.js @@ -1,9 +1,4 @@ export default { - // ONBOARDING VIEW - ONBOARD_JOIN_A_WORKSPACE: 'onboard_join_a_workspace', - ONBOARD_CREATE_NEW_WORKSPACE: 'onboard_create_new_workspace', - ONBOARD_CREATE_NEW_WORKSPACE_F: 'onboard_create_new_workspace_f', - // NEW SERVER VIEW NS_CONNECT_TO_WORKSPACE: 'ns_connect_to_workspace', NS_JOIN_OPEN_WORKSPACE: 'ns_join_open_workspace', @@ -78,6 +73,7 @@ export default { RL_GROUP_CHANNELS_BY_TYPE: 'rl_group_channels_by_type', RL_GROUP_CHANNELS_BY_FAVORITE: 'rl_group_channels_by_favorite', RL_GROUP_CHANNELS_BY_UNREAD: 'rl_group_channels_by_unread', + RL_CREATE_NEW_WORKSPACE: 'rl_create_new_workspace', // QUEUE LIST VIEW QL_GO_ROOM: 'ql_go_room', diff --git a/app/utils/scaling.js b/app/utils/scaling.js index f154a50a9..7cf33f1fc 100644 --- a/app/utils/scaling.js +++ b/app/utils/scaling.js @@ -1,14 +1,11 @@ -import { Dimensions } from 'react-native'; - import { isTablet } from './deviceInfo'; -const { width, height } = Dimensions.get('window'); - const guidelineBaseWidth = isTablet ? 600 : 375; const guidelineBaseHeight = isTablet ? 800 : 667; -const scale = size => (width / guidelineBaseWidth) * size; -const verticalScale = size => (height / guidelineBaseHeight) * size; -const moderateScale = (size, factor = 0.5) => size + (scale(size) - size) * factor; +// TODO: we need to refactor this +const scale = (size, width) => (width / guidelineBaseWidth) * size; +const verticalScale = (size, height) => (height / guidelineBaseHeight) * size; +const moderateScale = (size, factor = 0.5, width) => size + (scale(size, width) - size) * factor; export { scale, verticalScale, moderateScale }; diff --git a/app/views/NewServerView/ServerInput/index.js b/app/views/NewServerView/ServerInput/index.js index 16d8159d1..ef80c4685 100644 --- a/app/views/NewServerView/ServerInput/index.js +++ b/app/views/NewServerView/ServerInput/index.js @@ -10,9 +10,7 @@ import Item from './Item'; const styles = StyleSheet.create({ container: { - zIndex: 1, - marginTop: 24, - marginBottom: 32 + zIndex: 1 }, inputContainer: { marginTop: 0, diff --git a/app/views/NewServerView/index.js b/app/views/NewServerView/index.js index e7bffce67..8eaba349e 100644 --- a/app/views/NewServerView/index.js +++ b/app/views/NewServerView/index.js @@ -1,15 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { BackHandler, Keyboard, StyleSheet, Text, View } from 'react-native'; +import { Text, Keyboard, StyleSheet, View, BackHandler, Image } from 'react-native'; import { connect } from 'react-redux'; import { Base64 } from 'js-base64'; import parse from 'url-parse'; import { Q } from '@nozbe/watermelondb'; import { TouchableOpacity } from 'react-native-gesture-handler'; +import Orientation from 'react-native-orientation-locker'; import UserPreferences from '../../lib/userPreferences'; import EventEmitter from '../../utils/events'; -import { selectServerRequest, serverRequest } from '../../actions/server'; +import { selectServerRequest, serverRequest, serverFinishAdd as serverFinishAddAction } from '../../actions/server'; import { inviteLinksClear as inviteLinksClearAction } from '../../actions/inviteLinks'; import sharedStyles from '../Styles'; import Button from '../../containers/Button'; @@ -27,31 +28,38 @@ import database from '../../lib/database'; import { sanitizeLikeString } from '../../lib/database/utils'; import SSLPinning from '../../utils/sslPinning'; import RocketChat from '../../lib/rocketchat'; +import { isTablet } from '../../utils/deviceInfo'; +import { verticalScale, moderateScale } from '../../utils/scaling'; +import { withDimensions } from '../../dimensions'; import ServerInput from './ServerInput'; const styles = StyleSheet.create({ + onboardingImage: { + alignSelf: 'center', + resizeMode: 'contain' + }, title: { ...sharedStyles.textBold, - fontSize: 22 + letterSpacing: 0, + alignSelf: 'center' + }, + subtitle: { + ...sharedStyles.textRegular, + alignSelf: 'center' }, certificatePicker: { - marginBottom: 32, alignItems: 'center', justifyContent: 'flex-end' }, chooseCertificateTitle: { - fontSize: 13, ...sharedStyles.textRegular }, chooseCertificate: { - fontSize: 13, ...sharedStyles.textSemibold }, description: { ...sharedStyles.textRegular, - fontSize: 14, - textAlign: 'left', - marginBottom: 24 + textAlign: 'center' }, connectButton: { marginBottom: 0 @@ -59,23 +67,22 @@ const styles = StyleSheet.create({ }); class NewServerView extends React.Component { - static navigationOptions = () => ({ - title: I18n.t('Workspaces') - }); - static propTypes = { navigation: PropTypes.object, theme: PropTypes.string, connecting: PropTypes.bool.isRequired, connectServer: PropTypes.func.isRequired, selectServer: PropTypes.func.isRequired, - adding: PropTypes.bool, previousServer: PropTypes.string, - inviteLinksClear: PropTypes.func + inviteLinksClear: PropTypes.func, + serverFinishAdd: PropTypes.func }; constructor(props) { super(props); + if (!isTablet) { + Orientation.lockToPortrait(); + } this.setHeader(); this.state = { @@ -92,25 +99,27 @@ class NewServerView extends React.Component { this.queryServerHistory(); } - componentDidUpdate(prevProps) { - const { adding } = this.props; - if (prevProps.adding !== adding) { - this.setHeader(); - } - } - componentWillUnmount() { EventEmitter.removeListener('NewServer', this.handleNewServerEvent); BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress); + const { previousServer, serverFinishAdd } = this.props; + if (previousServer) { + serverFinishAdd(); + } } setHeader = () => { - const { adding, navigation } = this.props; - if (adding) { - navigation.setOptions({ + const { previousServer, navigation } = this.props; + if (previousServer) { + return navigation.setOptions({ + headerTitle: I18n.t('Workspaces'), headerLeft: () => }); } + + return navigation.setOptions({ + headerShown: false + }); }; handleBackPress = () => { @@ -273,16 +282,26 @@ class NewServerView extends React.Component { renderCertificatePicker = () => { const { certificate } = this.state; - const { theme } = this.props; + const { theme, width, height, previousServer } = this.props; return ( - - + + {certificate ? I18n.t('Your_certificate') : I18n.t('Do_you_have_a_certificate')} - + {certificate ?? I18n.t('Apply_Your_Certificate')} @@ -291,12 +310,48 @@ class NewServerView extends React.Component { }; render() { - const { connecting, theme } = this.props; + const { connecting, theme, previousServer, width, height } = this.props; const { text, connectingOpen, serversHistory } = this.state; + const marginTop = previousServer ? 0 : 35; + return ( - {I18n.t('Join_your_workspace')} + + + Rocket.Chat + + + {I18n.t('Onboarding_subtitle')} + - + {I18n.t('Onboarding_join_open_description')}