forked from Ivasoft/mattermost-mobile
[Gekidou] Show login screen when selecting a previous server (#6190)
* Show login screen when selecting a previous server * Update app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx Co-authored-by: Jason Frerich <jason.frerich@mattermost.com> Co-authored-by: Jason Frerich <jason.frerich@mattermost.com>
This commit is contained in:
@@ -9,7 +9,9 @@ import Swipeable from 'react-native-gesture-handler/Swipeable';
|
||||
|
||||
import {storeMultiServerTutorial} from '@actions/app/global';
|
||||
import {appEntry} from '@actions/remote/entry';
|
||||
import {doPing} from '@actions/remote/general';
|
||||
import {logout} from '@actions/remote/session';
|
||||
import {fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import Loading from '@components/loading';
|
||||
import ServerIcon from '@components/server_icon';
|
||||
@@ -21,7 +23,7 @@ import DatabaseManager from '@database/manager';
|
||||
import {subscribeServerUnreadAndMentions} from '@database/subscription/unreads';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import {addNewServer, alertServerLogout, alertServerRemove, editServer} from '@utils/server';
|
||||
import {alertServerError, alertServerLogout, alertServerRemove, editServer, loginToServer} from '@utils/server';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {removeProtocol, stripTrailingSlashes} from '@utils/url';
|
||||
@@ -195,8 +197,22 @@ const ServerItem = ({highlight, isActive, server, tutorialWatched}: Props) => {
|
||||
return style;
|
||||
}, [server.lastActiveAt]);
|
||||
|
||||
const handleLogin = useCallback(() => {
|
||||
addNewServer(theme, server.url, displayName);
|
||||
const handleLogin = useCallback(async () => {
|
||||
swipeable.current?.close();
|
||||
setSwitching(true);
|
||||
const result = await doPing(server.url);
|
||||
if (result.error) {
|
||||
alertServerError(intl, result.error as ClientErrorProps);
|
||||
setSwitching(false);
|
||||
return;
|
||||
}
|
||||
const data = await fetchConfigAndLicense(server.url, true);
|
||||
if (data.error) {
|
||||
alertServerError(intl, data.error as ClientErrorProps);
|
||||
setSwitching(false);
|
||||
return;
|
||||
}
|
||||
loginToServer(theme, server.url, displayName, data.config!, data.license!);
|
||||
}, [server, theme, intl]);
|
||||
|
||||
const handleDismissTutorial = useCallback(() => {
|
||||
|
||||
@@ -11,8 +11,9 @@ import {SafeAreaView} from 'react-native-safe-area-context';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {Screens} from '@constants';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import Background from '@screens/background';
|
||||
import {goToScreen, loginAnimationOptions} from '@screens/navigation';
|
||||
import {dismissModal, goToScreen, loginAnimationOptions} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
@@ -24,6 +25,8 @@ import SsoOptions from './sso_options';
|
||||
import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
export interface LoginOptionsProps extends LaunchProps {
|
||||
closeButtonId?: string;
|
||||
componentId: string;
|
||||
config: ClientConfig;
|
||||
hasLoginForm: boolean;
|
||||
license: ClientLicense;
|
||||
@@ -68,7 +71,11 @@ const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
|
||||
const AnimatedSafeArea = Animated.createAnimatedComponent(SafeAreaView);
|
||||
|
||||
const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, license, serverDisplayName, serverUrl, ssoOptions, theme}: LoginOptionsProps) => {
|
||||
const LoginOptions = ({
|
||||
closeButtonId, componentId, config, extra,
|
||||
hasLoginForm, launchType, launchError, license,
|
||||
serverDisplayName, serverUrl, ssoOptions, theme,
|
||||
}: LoginOptionsProps) => {
|
||||
const styles = getStyles(theme);
|
||||
const keyboardAwareRef = useRef<KeyboardAwareScrollView>();
|
||||
const dimensions = useWindowDimensions();
|
||||
@@ -125,6 +132,17 @@ const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, lic
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const navigationEvents = Navigation.events().registerNavigationButtonPressedListener(({buttonId}) => {
|
||||
if (closeButtonId && buttonId === closeButtonId) {
|
||||
NetworkManager.invalidateClient(serverUrl);
|
||||
dismissModal({componentId});
|
||||
}
|
||||
});
|
||||
|
||||
return () => navigationEvents.remove();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = {
|
||||
componentDidAppear: () => {
|
||||
|
||||
@@ -184,6 +184,8 @@ export function resetToHome(passProps: LaunchProps = {launchType: LaunchType.Nor
|
||||
|
||||
if (passProps.launchType === LaunchType.AddServer) {
|
||||
dismissModal({componentId: Screens.SERVER});
|
||||
dismissModal({componentId: Screens.LOGIN});
|
||||
dismissModal({componentId: Screens.SSO});
|
||||
dismissModal({componentId: Screens.BOTTOM_SHEET});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
import LocalConfig from '@assets/config.json';
|
||||
import ClientError from '@client/rest/error';
|
||||
import AppVersion from '@components/app_version';
|
||||
import {Screens, Sso} from '@constants';
|
||||
import {Screens} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {t} from '@i18n';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
@@ -24,6 +24,7 @@ import Background from '@screens/background';
|
||||
import {dismissModal, goToScreen, loginAnimationOptions} from '@screens/navigation';
|
||||
import {DeepLinkWithData, LaunchProps, LaunchType} from '@typings/launch';
|
||||
import {getErrorMessage} from '@utils/client_error';
|
||||
import {loginOptions} from '@utils/server';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {getServerUrlAfterRedirect, isValidUrl, sanitizeUrl} from '@utils/url';
|
||||
|
||||
@@ -136,6 +137,7 @@ const Server = ({
|
||||
useEffect(() => {
|
||||
const navigationEvents = Navigation.events().registerNavigationButtonPressedListener(({buttonId}) => {
|
||||
if (closeButtonId && buttonId === closeButtonId) {
|
||||
NetworkManager.invalidateClient(url);
|
||||
dismissModal({componentId});
|
||||
}
|
||||
});
|
||||
@@ -144,24 +146,7 @@ const Server = ({
|
||||
}, []);
|
||||
|
||||
const displayLogin = (serverUrl: string, config: ClientConfig, license: ClientLicense) => {
|
||||
const isLicensed = license.IsLicensed === 'true';
|
||||
const samlEnabled = config.EnableSaml === 'true' && isLicensed && license.SAML === 'true';
|
||||
const gitlabEnabled = config.EnableSignUpWithGitLab === 'true';
|
||||
const googleEnabled = config.EnableSignUpWithGoogle === 'true' && isLicensed;
|
||||
const o365Enabled = config.EnableSignUpWithOffice365 === 'true' && isLicensed && license.Office365OAuth === 'true';
|
||||
const openIdEnabled = config.EnableSignUpWithOpenId === 'true' && isLicensed;
|
||||
const ldapEnabled = isLicensed && config.EnableLdap === 'true' && license.LDAP === 'true';
|
||||
const hasLoginForm = config.EnableSignInWithEmail === 'true' || config.EnableSignInWithUsername === 'true' || ldapEnabled;
|
||||
const ssoOptions: Record<string, boolean> = {
|
||||
[Sso.SAML]: samlEnabled,
|
||||
[Sso.GITLAB]: gitlabEnabled,
|
||||
[Sso.GOOGLE]: googleEnabled,
|
||||
[Sso.OFFICE365]: o365Enabled,
|
||||
[Sso.OPENID]: openIdEnabled,
|
||||
};
|
||||
const enabledSSOs = Object.keys(ssoOptions).filter((key) => ssoOptions[key]);
|
||||
const numberSSOs = enabledSSOs.length;
|
||||
|
||||
const {enabledSSOs, hasLoginForm, numberSSOs, ssoOptions} = loginOptions(config, license);
|
||||
const passProps = {
|
||||
config,
|
||||
extra,
|
||||
|
||||
@@ -11,8 +11,9 @@ import {SafeAreaView} from 'react-native-safe-area-context';
|
||||
import {ssoLogin} from '@actions/remote/session';
|
||||
import ClientError from '@client/rest/error';
|
||||
import {Screens, Sso} from '@constants';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import Background from '@screens/background';
|
||||
import {resetToHome} from '@screens/navigation';
|
||||
import {dismissModal, resetToHome} from '@screens/navigation';
|
||||
|
||||
import SSOWithRedirectURL from './sso_with_redirect_url';
|
||||
import SSOWithWebView from './sso_with_webview';
|
||||
@@ -20,11 +21,13 @@ import SSOWithWebView from './sso_with_webview';
|
||||
import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
interface SSOProps extends LaunchProps {
|
||||
config: Partial<ClientConfig>;
|
||||
license: Partial<ClientLicense>;
|
||||
ssoType: string;
|
||||
serverDisplayName: string;
|
||||
theme: Theme;
|
||||
closeButtonId?: string;
|
||||
componentId: string;
|
||||
config: Partial<ClientConfig>;
|
||||
license: Partial<ClientLicense>;
|
||||
ssoType: string;
|
||||
serverDisplayName: string;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const AnimatedSafeArea = Animated.createAnimatedComponent(SafeAreaView);
|
||||
@@ -35,7 +38,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
const SSO = ({config, extra, launchError, launchType, serverDisplayName, serverUrl, ssoType, theme}: SSOProps) => {
|
||||
const SSO = ({
|
||||
closeButtonId, componentId, config, extra,
|
||||
launchError, launchType, serverDisplayName,
|
||||
serverUrl, ssoType, theme,
|
||||
}: SSOProps) => {
|
||||
const managedConfig = useManagedConfig<ManagedConfig>();
|
||||
const inAppSessionAuth = managedConfig?.inAppSessionAuth === 'true';
|
||||
const dimensions = useWindowDimensions();
|
||||
@@ -128,6 +135,19 @@ const SSO = ({config, extra, launchError, launchType, serverDisplayName, serverU
|
||||
return () => unsubscribe.remove();
|
||||
}, [dimensions]);
|
||||
|
||||
useEffect(() => {
|
||||
const navigationEvents = Navigation.events().registerNavigationButtonPressedListener(({buttonId}) => {
|
||||
if (closeButtonId && buttonId === closeButtonId) {
|
||||
if (serverUrl) {
|
||||
NetworkManager.invalidateClient(serverUrl);
|
||||
}
|
||||
dismissModal({componentId});
|
||||
}
|
||||
});
|
||||
|
||||
return () => navigationEvents.remove();
|
||||
}, []);
|
||||
|
||||
const props = {
|
||||
doSSOLogin,
|
||||
loginError,
|
||||
|
||||
@@ -23,6 +23,7 @@ jest.mock('@utils/url', () => {
|
||||
|
||||
describe('SSO', () => {
|
||||
const baseProps = {
|
||||
componentId: 'SSO',
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
},
|
||||
|
||||
@@ -5,9 +5,10 @@ import {IntlShape} from 'react-intl';
|
||||
import {Alert, AlertButton} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {Screens, SupportedServer} from '@constants';
|
||||
import {Screens, Sso, SupportedServer} from '@constants';
|
||||
import {dismissBottomSheet, showModal} from '@screens/navigation';
|
||||
import {LaunchType} from '@typings/launch';
|
||||
import {getErrorMessage} from '@utils/client_error';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
import {tryOpenURL} from '@utils/url';
|
||||
|
||||
@@ -49,6 +50,61 @@ export async function addNewServer(theme: Theme, serverUrl?: string, displayName
|
||||
showModal(Screens.SERVER, '', props, options);
|
||||
}
|
||||
|
||||
export function loginOptions(config: ClientConfig, license: ClientLicense) {
|
||||
const isLicensed = license.IsLicensed === 'true';
|
||||
const samlEnabled = config.EnableSaml === 'true' && isLicensed && license.SAML === 'true';
|
||||
const gitlabEnabled = config.EnableSignUpWithGitLab === 'true';
|
||||
const googleEnabled = config.EnableSignUpWithGoogle === 'true' && isLicensed;
|
||||
const o365Enabled = config.EnableSignUpWithOffice365 === 'true' && isLicensed && license.Office365OAuth === 'true';
|
||||
const openIdEnabled = config.EnableSignUpWithOpenId === 'true' && isLicensed;
|
||||
const ldapEnabled = isLicensed && config.EnableLdap === 'true' && license.LDAP === 'true';
|
||||
const hasLoginForm = config.EnableSignInWithEmail === 'true' || config.EnableSignInWithUsername === 'true' || ldapEnabled;
|
||||
const ssoOptions: Record<string, boolean> = {
|
||||
[Sso.SAML]: samlEnabled,
|
||||
[Sso.GITLAB]: gitlabEnabled,
|
||||
[Sso.GOOGLE]: googleEnabled,
|
||||
[Sso.OFFICE365]: o365Enabled,
|
||||
[Sso.OPENID]: openIdEnabled,
|
||||
};
|
||||
const enabledSSOs = Object.keys(ssoOptions).filter((key) => ssoOptions[key]);
|
||||
const numberSSOs = enabledSSOs.length;
|
||||
|
||||
return {
|
||||
enabledSSOs,
|
||||
hasLoginForm,
|
||||
numberSSOs,
|
||||
ssoOptions,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loginToServer(theme: Theme, serverUrl: string, displayName: string, config: ClientConfig, license: ClientLicense) {
|
||||
await dismissBottomSheet();
|
||||
const closeButtonId = 'close-server';
|
||||
const {enabledSSOs, hasLoginForm, numberSSOs, ssoOptions} = loginOptions(config, license);
|
||||
const props = {
|
||||
closeButtonId,
|
||||
config,
|
||||
hasLoginForm,
|
||||
launchType: LaunchType.AddServer,
|
||||
license,
|
||||
serverDisplayName: displayName,
|
||||
serverUrl,
|
||||
ssoOptions,
|
||||
theme,
|
||||
};
|
||||
|
||||
const redirectSSO = !hasLoginForm && numberSSOs === 1;
|
||||
const screen = redirectSSO ? Screens.SSO : Screens.LOGIN;
|
||||
if (redirectSSO) {
|
||||
// @ts-expect-error ssoType not in definition
|
||||
props.ssoType = enabledSSOs[0];
|
||||
}
|
||||
|
||||
const options = buildServerModalOptions(theme, closeButtonId);
|
||||
|
||||
showModal(screen, '', props, options);
|
||||
}
|
||||
|
||||
export async function editServer(theme: Theme, server: ServersModel) {
|
||||
const closeButtonId = 'close-server-edit';
|
||||
const props = {
|
||||
@@ -103,6 +159,17 @@ export async function alertServerRemove(displayName: string, onPress: () => void
|
||||
);
|
||||
}
|
||||
|
||||
export function alertServerError(intl: IntlShape, error: ClientErrorProps) {
|
||||
const message = getErrorMessage(error, intl);
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'server.websocket.unreachable',
|
||||
defaultMessage: 'Server is unreachable.',
|
||||
}),
|
||||
message,
|
||||
);
|
||||
}
|
||||
|
||||
function unsupportedServerAdminAlert(intl: IntlShape) {
|
||||
const title = intl.formatMessage({id: 'mobile.server_upgrade.title', defaultMessage: 'Server upgrade required'});
|
||||
|
||||
|
||||
@@ -270,6 +270,9 @@ jest.mock('react-native-navigation', () => {
|
||||
bindComponent: jest.fn(() => {
|
||||
return {remove: jest.fn()};
|
||||
}),
|
||||
registerNavigationButtonPressedListener: jest.fn(() => {
|
||||
return {buttonId: 'buttonId'};
|
||||
}),
|
||||
}),
|
||||
setRoot: jest.fn(),
|
||||
pop: jest.fn(),
|
||||
|
||||
Reference in New Issue
Block a user