diff --git a/native/navigation/invite-link-modal.react.js b/native/navigation/invite-link-modal.react.js
new file mode 100644
--- /dev/null
+++ b/native/navigation/invite-link-modal.react.js
@@ -0,0 +1,174 @@
+// @flow
+
+import * as React from 'react';
+import { View, Text } from 'react-native';
+
+import type { InviteLinkVerificationResponse } from 'lib/types/link-types.js';
+
+import type { RootNavigationProp } from './root-navigator.react.js';
+import type { NavigationRoute } from './route-names.js';
+import Button from '../components/button.react.js';
+import Modal from '../components/modal.react.js';
+import { useStyles } from '../themes/colors.js';
+
+export type InviteLinkModalParams = {
+ +invitationDetails: InviteLinkVerificationResponse,
+ +secret: string,
+};
+
+type Props = {
+ +navigation: RootNavigationProp<'InviteLinkModal'>,
+ +route: NavigationRoute<'InviteLinkModal'>,
+};
+
+function InviteLinkModal(props: Props): React.Node {
+ const styles = useStyles(unboundStyles);
+ const { invitationDetails } = props.route.params;
+
+ React.useEffect(() => {
+ if (invitationDetails.status === 'already_joined') {
+ props.navigation.goBack();
+ }
+ }, [invitationDetails.status, props.navigation]);
+
+ const header = React.useMemo(() => {
+ if (invitationDetails.status === 'valid') {
+ return (
+ <>
+ You have been invited to join
+
+ {invitationDetails.community.name}
+
+ >
+ );
+ }
+ return (
+ <>
+ Invite invalid
+
+ This invite link may be expired, please try again with another invite
+ link
+
+ >
+ );
+ }, [
+ invitationDetails,
+ styles.communityName,
+ styles.invalidInviteExplanation,
+ styles.invalidInviteTitle,
+ styles.invitation,
+ ]);
+
+ const buttons = React.useMemo(() => {
+ if (invitationDetails.status === 'valid') {
+ return (
+ <>
+
+
+ >
+ );
+ }
+ return (
+
+ );
+ }, [
+ invitationDetails.status,
+ props.navigation.goBack,
+ styles.button,
+ styles.buttonPrimary,
+ styles.buttonSecondary,
+ styles.buttonText,
+ styles.gap,
+ ]);
+
+ return (
+
+ {header}
+
+ {buttons}
+
+ );
+}
+
+const unboundStyles = {
+ modal: {
+ backgroundColor: 'modalForeground',
+ paddingVertical: 24,
+ paddingHorizontal: 16,
+ flex: 0,
+ },
+ invitation: {
+ color: 'whiteText',
+ textAlign: 'center',
+ fontSize: 14,
+ fontWeight: '400',
+ lineHeight: 22,
+ marginBottom: 24,
+ },
+ communityName: {
+ color: 'whiteText',
+ textAlign: 'center',
+ fontSize: 18,
+ fontWeight: '500',
+ lineHeight: 24,
+ },
+ invalidInviteTitle: {
+ color: 'whiteText',
+ textAlign: 'center',
+ fontSize: 22,
+ fontWeight: '500',
+ lineHeight: 28,
+ marginBottom: 24,
+ },
+ invalidInviteExplanation: {
+ color: 'whiteText',
+ textAlign: 'center',
+ fontSize: 14,
+ fontWeight: '400',
+ lineHeight: 22,
+ },
+ separator: {
+ height: 1,
+ backgroundColor: 'modalSeparator',
+ marginVertical: 24,
+ },
+ gap: {
+ marginBottom: 16,
+ },
+ button: {
+ borderRadius: 8,
+ paddingVertical: 12,
+ paddingHorizontal: 24,
+ },
+ buttonPrimary: {
+ backgroundColor: 'purpleButton',
+ },
+ buttonSecondary: {
+ borderColor: 'secondaryButtonBorder',
+ borderWidth: 1,
+ },
+ buttonText: {
+ color: 'whiteText',
+ textAlign: 'center',
+ fontSize: 16,
+ fontWeight: '500',
+ lineHeight: 24,
+ },
+};
+
+export default InviteLinkModal;
diff --git a/native/navigation/root-navigator.react.js b/native/navigation/root-navigator.react.js
--- a/native/navigation/root-navigator.react.js
+++ b/native/navigation/root-navigator.react.js
@@ -18,6 +18,7 @@
import { enableScreens } from 'react-native-screens';
import AppNavigator from './app-navigator.react.js';
+import InviteLinkModal from './invite-link-modal.react.js';
import { defaultStackScreenOptions } from './options.js';
import { RootNavigatorContext } from './root-navigator-context.js';
import RootRouter, {
@@ -39,6 +40,7 @@
type RootParamList,
TermsAndPrivacyRouteName,
RegistrationRouteName,
+ InviteLinkModalRouteName,
} from './route-names.js';
import LoggedOutModal from '../account/logged-out-modal.react.js';
import RegistrationNavigator from '../account/registration/registration-navigator.react.js';
@@ -203,6 +205,11 @@
component={TermsAndPrivacyModal}
options={termsAndPrivacyModalScreenOptions}
/>
+
;
@@ -180,6 +182,8 @@
typeaheadTooltipBorder: '#404040',
typeaheadTooltipText: 'white',
messageLabel: '#CCCCCC',
+ modalSeparator: '#404040',
+ secondaryButtonBorder: '#FFFFFF',
});
const colors = { light, dark };