diff --git a/src/nav/AppNavigator.js b/src/nav/AppNavigator.js index 442e5f775b1..8238ab9e770 100644 --- a/src/nav/AppNavigator.js +++ b/src/nav/AppNavigator.js @@ -184,7 +184,6 @@ export default function AppNavigator(props: Props): Node { name="notif-troubleshooting" component={useHaveServerDataGate(NotifTroubleshootingScreen)} /> - @@ -205,6 +204,7 @@ export default function AppNavigator(props: Props): Node { } /> + ); diff --git a/src/settings/LegalScreen.js b/src/settings/LegalScreen.js index 8cd7ef98735..0269bc58f07 100644 --- a/src/settings/LegalScreen.js +++ b/src/settings/LegalScreen.js @@ -3,56 +3,133 @@ import React, { useCallback } from 'react'; import type { Node } from 'react'; +import { createSelector } from 'reselect'; import type { RouteProp } from '../react-navigation'; import type { AppNavigationProp } from '../nav/AppNavigator'; -import { useGlobalSelector, useSelector } from '../react-redux'; +import { useGlobalSelector } from '../react-redux'; import Screen from '../common/Screen'; import NavRow from '../common/NavRow'; import ZulipText from '../common/ZulipText'; import { openLinkWithUserPreference } from '../utils/openLink'; -import { getRealmUrl, getRealmName, getGlobalSettings } from '../selectors'; +import { getRealmName, getGlobalSettings } from '../selectors'; +import { getAccounts } from '../directSelectors'; +import type { GlobalSelector } from '../reduxTypes'; +import { getAccount, tryGetActiveAccountState } from '../account/accountsSelectors'; +import { identityOfAccount, keyOfIdentity } from '../account/accountMisc'; +import { getHaveServerData } from '../haveServerDataSelectors'; + +/** + * Data for all realms represented in `state.accounts`, logged-in or not, + * unique by URL. + * + * The realm name will be missing when we don't have server data for any + * account on the realm. + */ +type ViewModel = $ReadOnlyArray<{| + +realm: URL, + +name: string | null, + +policiesUrl: URL, +|}>; + +const getViewModel: GlobalSelector = createSelector( + getAccounts, + tryGetActiveAccountState, + (accounts, activeAccountState) => { + const result = new Map(accounts.map(a => [a.realm.toString(), null])); + + accounts.forEach(account => { + const realmStr = account.realm.toString(); + + if (result.get(realmStr) != null) { + return; + } + + // TODO(#5006): Add realm name for any account we have server data for, + // not just the active account. + if ( + activeAccountState + && keyOfIdentity(identityOfAccount(getAccount(activeAccountState))) + === keyOfIdentity(identityOfAccount(account)) + && getHaveServerData(activeAccountState) + ) { + result.set(realmStr, getRealmName(activeAccountState)); + } + }); + + return [...result.entries()].map(([realmStr, name]) => { + const realm = new URL(realmStr); + return { + realm, + name, + policiesUrl: new URL('/policies/?nav=no', realm), + }; + }); + }, +); type Props = $ReadOnly<{| navigation: AppNavigationProp<'legal'>, route: RouteProp<'legal', void>, |}>; -/** (NB this is a per-account screen: it leads to this realm's policies.) */ +const zulipPoliciesUrl = new URL('https://zulip.com/policies/?nav=no'); + +/** + * A global, all-accounts screen linking to terms for all realms we know about. + */ export default function LegalScreen(props: Props): Node { - const realm = useSelector(getRealmUrl); - const realmName = useSelector(getRealmName); + const viewModel = useGlobalSelector(getViewModel); const globalSettings = useGlobalSelector(getGlobalSettings); const openZulipPolicies = useCallback(() => { - openLinkWithUserPreference(new URL('https://zulip.com/policies/?nav=no'), globalSettings); + openLinkWithUserPreference(zulipPoliciesUrl, globalSettings); }, [globalSettings]); - const openRealmPolicies = useCallback(() => { - openLinkWithUserPreference(new URL('/policies/?nav=no', realm), globalSettings); - }, [realm, globalSettings]); - return ( - }, - }} - onPress={openRealmPolicies} + title="Zulip terms" + subtitle={{ text: '{_}', values: { _: zulipPoliciesUrl.toString() } }} + onPress={openZulipPolicies} type="external" /> + {viewModel.map(({ realm, name, policiesUrl }) => ( + + ), + }, + }} + subtitle={ + // It's nice to be explicit about where the policies live, + // though the "?nav=no" is a bit annoying. But also, this line + // disambiguates multiple realms with the same name; the name is + // shown (when we have it) in `title`. + { text: '{_}', values: { _: policiesUrl.toString() } } + } + onPress={() => { + openLinkWithUserPreference(policiesUrl, globalSettings); + }} + type="external" + /> + ))} ); }