diff --git a/native/account/registration/avatar-selection.react.js b/native/account/registration/avatar-selection.react.js
index f68612821..fb2b13121 100644
--- a/native/account/registration/avatar-selection.react.js
+++ b/native/account/registration/avatar-selection.react.js
@@ -1,205 +1,217 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import { Text, View } from 'react-native';
import {
EditUserAvatarContext,
type UserAvatarSelection,
} from 'lib/components/edit-user-avatar-provider.react.js';
import RegistrationButtonContainer from './registration-button-container.react.js';
import RegistrationButton from './registration-button.react.js';
import RegistrationContainer from './registration-container.react.js';
import RegistrationContentContainer from './registration-content-container.react.js';
import { RegistrationContext } from './registration-context.js';
import type { RegistrationNavigationProp } from './registration-navigator.react.js';
import {
type CoolOrNerdMode,
type AccountSelection,
type AvatarData,
ensAvatarSelection,
} from './registration-types.js';
+import { enableSIWEBackupCreation } from './registration-types.js';
import EditUserAvatar from '../../avatars/edit-user-avatar.react.js';
import { useCurrentLeafRouteName } from '../../navigation/nav-selectors.js';
import {
type NavigationRoute,
RegistrationTermsRouteName,
+ CreateSIWEBackupMessageRouteName,
AvatarSelectionRouteName,
EmojiAvatarSelectionRouteName,
RegistrationUserAvatarCameraModalRouteName,
} from '../../navigation/route-names.js';
import { useStyles } from '../../themes/colors.js';
export type AvatarSelectionParams = {
+userSelections: {
+coolOrNerdMode: CoolOrNerdMode,
+keyserverURL: string,
+accountSelection: AccountSelection,
+farcasterID: ?string,
},
};
type Props = {
+navigation: RegistrationNavigationProp<'AvatarSelection'>,
+route: NavigationRoute<'AvatarSelection'>,
};
function AvatarSelection(props: Props): React.Node {
const { userSelections } = props.route.params;
const { accountSelection } = userSelections;
const usernameOrETHAddress =
accountSelection.accountType === 'username'
? accountSelection.username
: accountSelection.address;
const registrationContext = React.useContext(RegistrationContext);
invariant(registrationContext, 'registrationContext should be set');
const { cachedSelections, setCachedSelections } = registrationContext;
const editUserAvatarContext = React.useContext(EditUserAvatarContext);
invariant(editUserAvatarContext, 'editUserAvatarContext should be set');
const { setRegistrationMode } = editUserAvatarContext;
const prefetchedAvatarURI =
accountSelection.accountType === 'ethereum'
? accountSelection.avatarURI
: undefined;
let initialAvatarData = cachedSelections.avatarData;
if (!initialAvatarData && prefetchedAvatarURI) {
initialAvatarData = ensAvatarSelection;
}
const [avatarData, setAvatarData] =
React.useState(initialAvatarData);
const setClientAvatarFromSelection = React.useCallback(
(selection: UserAvatarSelection) => {
if (selection.needsUpload) {
const newAvatarData = {
...selection,
clientAvatar: {
type: 'image',
uri: selection.mediaSelection.uri,
},
};
setAvatarData(newAvatarData);
setCachedSelections(oldUserSelections => ({
...oldUserSelections,
avatarData: newAvatarData,
}));
} else if (selection.updateUserAvatarRequest.type !== 'remove') {
const clientRequest = selection.updateUserAvatarRequest;
invariant(
clientRequest.type !== 'image' &&
clientRequest.type !== 'encrypted_image',
'image avatars need to be uploaded',
);
const newAvatarData = {
...selection,
clientAvatar: clientRequest,
};
setAvatarData(newAvatarData);
setCachedSelections(oldUserSelections => ({
...oldUserSelections,
avatarData: newAvatarData,
}));
} else {
setAvatarData(undefined);
setCachedSelections(oldUserSelections => ({
...oldUserSelections,
avatarData: undefined,
}));
}
},
[setCachedSelections],
);
const currentRouteName = useCurrentLeafRouteName();
const avatarSelectionHappening =
currentRouteName === AvatarSelectionRouteName ||
currentRouteName === EmojiAvatarSelectionRouteName ||
currentRouteName === RegistrationUserAvatarCameraModalRouteName;
React.useEffect(() => {
if (!avatarSelectionHappening) {
return undefined;
}
setRegistrationMode({
registrationMode: 'on',
successCallback: setClientAvatarFromSelection,
});
return () => {
setRegistrationMode({ registrationMode: 'off' });
};
}, [
avatarSelectionHappening,
setRegistrationMode,
setClientAvatarFromSelection,
]);
const { navigate } = props.navigation;
const onProceed = React.useCallback(async () => {
const newUserSelections = {
...userSelections,
avatarData,
};
+ if (
+ userSelections.accountSelection.accountType === 'ethereum' &&
+ enableSIWEBackupCreation
+ ) {
+ navigate<'CreateSIWEBackupMessage'>({
+ name: CreateSIWEBackupMessageRouteName,
+ params: { userSelections: newUserSelections },
+ });
+ return;
+ }
navigate<'RegistrationTerms'>({
name: RegistrationTermsRouteName,
params: { userSelections: newUserSelections },
});
}, [userSelections, avatarData, navigate]);
const clientAvatar = avatarData?.clientAvatar;
const userInfoOverride = React.useMemo(
() => ({
username: usernameOrETHAddress,
avatar: clientAvatar,
}),
[usernameOrETHAddress, clientAvatar],
);
const styles = useStyles(unboundStyles);
return (
Pick an avatar
);
}
const unboundStyles = {
scrollViewContentContainer: {
paddingHorizontal: 0,
},
header: {
fontSize: 24,
color: 'panelForegroundLabel',
paddingBottom: 16,
paddingHorizontal: 16,
},
stagedAvatarSection: {
marginTop: 16,
backgroundColor: 'panelForeground',
paddingVertical: 24,
alignItems: 'center',
},
editUserAvatar: {
alignItems: 'center',
justifyContent: 'center',
},
};
export default AvatarSelection;
diff --git a/native/account/registration/registration-navigator.react.js b/native/account/registration/registration-navigator.react.js
index 6b439b578..ec0517db2 100644
--- a/native/account/registration/registration-navigator.react.js
+++ b/native/account/registration/registration-navigator.react.js
@@ -1,125 +1,131 @@
// @flow
import type {
StackNavigationProp,
StackNavigationHelpers,
} from '@react-navigation/core';
import { createStackNavigator } from '@react-navigation/stack';
import * as React from 'react';
import AccountDoesNotExist from './account-does-not-exist.react.js';
import AvatarSelection from './avatar-selection.react.js';
import ConnectEthereum from './connect-ethereum.react.js';
import ConnectFarcaster from './connect-farcaster.react.js';
import CoolOrNerdModeSelection from './cool-or-nerd-mode-selection.react.js';
import EmojiAvatarSelection from './emoji-avatar-selection.react.js';
import ExistingEthereumAccount from './existing-ethereum-account.react.js';
import KeyserverSelection from './keyserver-selection.react.js';
import PasswordSelection from './password-selection.react.js';
import RegistrationTerms from './registration-terms.react.js';
+import CreateSIWEBackupMessage from './siwe-backup-message-creation.react.js';
import UsernameSelection from './username-selection.react.js';
import RegistrationUserAvatarCameraModal from '../../media/registration-user-avatar-camera-modal.react.js';
import type { RootNavigationProp } from '../../navigation/root-navigator.react.js';
import {
KeyserverSelectionRouteName,
CoolOrNerdModeSelectionRouteName,
ConnectEthereumRouteName,
+ CreateSIWEBackupMessageRouteName,
ExistingEthereumAccountRouteName,
UsernameSelectionRouteName,
ConnectFarcasterRouteName,
PasswordSelectionRouteName,
AvatarSelectionRouteName,
EmojiAvatarSelectionRouteName,
RegistrationUserAvatarCameraModalRouteName,
RegistrationTermsRouteName,
AccountDoesNotExistRouteName,
type ScreenParamList,
type RegistrationParamList,
} from '../../navigation/route-names.js';
export type RegistrationNavigationProp<
RouteName: $Keys = $Keys,
> = StackNavigationProp;
const Registration = createStackNavigator<
ScreenParamList,
RegistrationParamList,
StackNavigationHelpers,
>();
const screenOptions = {
headerTransparent: true,
headerBackTitleVisible: false,
headerTitle: '',
headerTintColor: 'white',
headerLeftContainerStyle: {
paddingLeft: 12,
},
};
const cameraScreenOptions = {
headerShown: false,
};
type Props = {
+navigation: RootNavigationProp<'Registration'>,
...
};
// eslint-disable-next-line no-unused-vars
function RegistrationNavigator(props: Props): React.Node {
return (
+
);
}
export default RegistrationNavigator;
diff --git a/native/account/registration/registration-types.js b/native/account/registration/registration-types.js
index 6e2dc6ad5..22681afa6 100644
--- a/native/account/registration/registration-types.js
+++ b/native/account/registration/registration-types.js
@@ -1,64 +1,67 @@
// @flow
import type {
UpdateUserAvatarRequest,
ClientAvatar,
} from 'lib/types/avatar-types.js';
import type { NativeMediaSelection } from 'lib/types/media-types.js';
-import type { SIWEResult } from 'lib/types/siwe-types.js';
+import type { SIWEResult, SIWEBackupSecrets } from 'lib/types/siwe-types.js';
export type CoolOrNerdMode = 'cool' | 'nerd';
export type EthereumAccountSelection = {
+accountType: 'ethereum',
...SIWEResult,
+avatarURI: ?string,
};
export type UsernameAccountSelection = {
+accountType: 'username',
+username: string,
+password: string,
};
export type AccountSelection =
| EthereumAccountSelection
| UsernameAccountSelection;
export type AvatarData =
| {
+needsUpload: true,
+mediaSelection: NativeMediaSelection,
+clientAvatar: ClientAvatar,
}
| {
+needsUpload: false,
+updateUserAvatarRequest: UpdateUserAvatarRequest,
+clientAvatar: ClientAvatar,
};
export type RegistrationServerCallInput = {
+coolOrNerdMode: CoolOrNerdMode,
+keyserverURL: string,
+farcasterID: ?string,
+accountSelection: AccountSelection,
+avatarData: ?AvatarData,
};
export type CachedUserSelections = {
+coolOrNerdMode?: CoolOrNerdMode,
+keyserverURL?: string,
+username?: string,
+password?: string,
+avatarData?: ?AvatarData,
+ethereumAccount?: EthereumAccountSelection,
+farcasterID?: string,
+ +siweBackupSecrets?: ?SIWEBackupSecrets,
};
export const ensAvatarSelection: AvatarData = {
needsUpload: false,
updateUserAvatarRequest: { type: 'ens' },
clientAvatar: { type: 'ens' },
};
export const enableNewRegistrationMode = __DEV__;
+
+export const enableSIWEBackupCreation = __DEV__;
diff --git a/native/account/registration/siwe-backup-message-creation.react.js b/native/account/registration/siwe-backup-message-creation.react.js
new file mode 100644
index 000000000..0d1abbc70
--- /dev/null
+++ b/native/account/registration/siwe-backup-message-creation.react.js
@@ -0,0 +1,187 @@
+// @flow
+
+import Icon from '@expo/vector-icons/MaterialIcons.js';
+import invariant from 'invariant';
+import * as React from 'react';
+import { View, Text } from 'react-native';
+
+import { type SIWEResult, SIWEMessageTypes } from 'lib/types/siwe-types.js';
+
+import RegistrationButtonContainer from './registration-button-container.react.js';
+import RegistrationButton from './registration-button.react.js';
+import RegistrationContainer from './registration-container.react.js';
+import RegistrationContentContainer from './registration-content-container.react.js';
+import { RegistrationContext } from './registration-context.js';
+import { type RegistrationNavigationProp } from './registration-navigator.react.js';
+import type {
+ CoolOrNerdMode,
+ AccountSelection,
+ AvatarData,
+} from './registration-types.js';
+import {
+ type NavigationRoute,
+ RegistrationTermsRouteName,
+} from '../../navigation/route-names.js';
+import { useStyles } from '../../themes/colors.js';
+import SIWEPanel from '../siwe-panel.react.js';
+
+export type CreateSIWEBackupMessageParams = {
+ +userSelections: {
+ +coolOrNerdMode: CoolOrNerdMode,
+ +keyserverURL: string,
+ +farcasterID: ?string,
+ +accountSelection: AccountSelection,
+ +avatarData: ?AvatarData,
+ },
+};
+
+type PanelState = 'closed' | 'opening' | 'open' | 'closing';
+
+type Props = {
+ +navigation: RegistrationNavigationProp<'CreateSIWEBackupMessage'>,
+ +route: NavigationRoute<'CreateSIWEBackupMessage'>,
+};
+function CreateSIWEBackupMessage(props: Props): React.Node {
+ const { navigate } = props.navigation;
+ const { params } = props.route;
+
+ const styles = useStyles(unboundStyles);
+
+ const [panelState, setPanelState] = React.useState('closed');
+
+ const openPanel = React.useCallback(() => {
+ setPanelState('opening');
+ }, []);
+ const onPanelClosed = React.useCallback(() => {
+ setPanelState('closed');
+ }, []);
+ const onPanelClosing = React.useCallback(() => {
+ setPanelState('closing');
+ }, []);
+
+ const siwePanelSetLoading = React.useCallback(
+ (loading: boolean) => {
+ if (panelState === 'closing' || panelState === 'closed') {
+ return;
+ }
+ setPanelState(loading ? 'opening' : 'open');
+ },
+ [panelState],
+ );
+
+ const registrationContext = React.useContext(RegistrationContext);
+ invariant(registrationContext, 'registrationContext should be set');
+ const { cachedSelections, setCachedSelections } = registrationContext;
+
+ const onSuccessfulWalletSignature = React.useCallback(
+ (result: SIWEResult) => {
+ const { message, signature } = result;
+ setCachedSelections(oldUserSelections => ({
+ ...oldUserSelections,
+ siweBackupSecrets: { message, signature },
+ }));
+ navigate<'RegistrationTerms'>({
+ name: RegistrationTermsRouteName,
+ params,
+ });
+ },
+ [navigate, params, setCachedSelections],
+ );
+
+ const onExistingWalletSignature = React.useCallback(() => {
+ navigate<'RegistrationTerms'>({
+ name: RegistrationTermsRouteName,
+ params,
+ });
+ }, [params, navigate]);
+
+ let siwePanel;
+ if (panelState !== 'closed') {
+ siwePanel = (
+
+ );
+ }
+
+ const { siweBackupSecrets } = cachedSelections;
+
+ const newSignatureButtonText = siweBackupSecrets
+ ? 'Encrypt with new signature'
+ : 'Encrypt with Ethereum signature';
+ const newSignatureButtonVariant = siweBackupSecrets ? 'outline' : 'enabled';
+
+ let useExistingSignatureButton;
+ if (siweBackupSecrets) {
+ useExistingSignatureButton = (
+
+ );
+ }
+
+ const body = (
+
+ Comm encrypts user backups so that our backend is not able to see user
+ data.
+
+ );
+
+ return (
+ <>
+
+
+ Encrypting your Comm Backup
+ {body}
+
+
+
+
+
+ {useExistingSignatureButton}
+
+
+
+ {siwePanel}
+ >
+ );
+}
+
+const unboundStyles = {
+ scrollViewContentContainer: {
+ flexGrow: 1,
+ },
+ header: {
+ fontSize: 24,
+ color: 'panelForegroundLabel',
+ paddingBottom: 16,
+ },
+ body: {
+ fontFamily: 'Arial',
+ fontSize: 15,
+ lineHeight: 20,
+ color: 'panelForegroundSecondaryLabel',
+ paddingBottom: 16,
+ },
+ siweBackupIcon: {
+ color: 'panelForegroundIcon',
+ },
+ siweBackupIconContainer: {
+ flexGrow: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+};
+
+export default CreateSIWEBackupMessage;
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
index b2021d747..24ecc4921 100644
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -1,358 +1,361 @@
// @flow
import type { RouteProp } from '@react-navigation/core';
import type { ActionResultModalParams } from './action-result-modal.react.js';
import type { InviteLinkModalParams } from './invite-link-modal.react';
import type { AvatarSelectionParams } from '../account/registration/avatar-selection.react.js';
import type { ConnectEthereumParams } from '../account/registration/connect-ethereum.react.js';
import type { ConnectFarcasterParams } from '../account/registration/connect-farcaster.react.js';
import type { EmojiAvatarSelectionParams } from '../account/registration/emoji-avatar-selection.react.js';
import type { ExistingEthereumAccountParams } from '../account/registration/existing-ethereum-account.react.js';
import type { KeyserverSelectionParams } from '../account/registration/keyserver-selection.react.js';
import type { PasswordSelectionParams } from '../account/registration/password-selection.react.js';
import type { RegistrationTermsParams } from '../account/registration/registration-terms.react.js';
+import type { CreateSIWEBackupMessageParams } from '../account/registration/siwe-backup-message-creation.react.js';
import type { UsernameSelectionParams } from '../account/registration/username-selection.react.js';
import type { TermsAndPrivacyModalParams } from '../account/terms-and-privacy-modal.react.js';
import type { ThreadPickerModalParams } from '../calendar/thread-picker-modal.react.js';
import type { ComposeSubchannelParams } from '../chat/compose-subchannel.react.js';
import type { FullScreenThreadMediaGalleryParams } from '../chat/fullscreen-thread-media-gallery.react.js';
import type { ImagePasteModalParams } from '../chat/image-paste-modal.react.js';
import type { MessageListParams } from '../chat/message-list-types.js';
import type { MessageReactionsModalParams } from '../chat/message-reactions-modal.react.js';
import type { MultimediaMessageTooltipModalParams } from '../chat/multimedia-message-tooltip-modal.react.js';
import type { PinnedMessagesScreenParams } from '../chat/pinned-messages-screen.react.js';
import type { RobotextMessageTooltipModalParams } from '../chat/robotext-message-tooltip-modal.react.js';
import type { AddUsersModalParams } from '../chat/settings/add-users-modal.react.js';
import type { ColorSelectorModalParams } from '../chat/settings/color-selector-modal.react.js';
import type { ComposeSubchannelModalParams } from '../chat/settings/compose-subchannel-modal.react.js';
import type { DeleteThreadParams } from '../chat/settings/delete-thread.react.js';
import type { EmojiThreadAvatarCreationParams } from '../chat/settings/emoji-thread-avatar-creation.react.js';
import type { ThreadSettingsMemberTooltipModalParams } from '../chat/settings/thread-settings-member-tooltip-modal.react.js';
import type { ThreadSettingsParams } from '../chat/settings/thread-settings.react.js';
import type { SidebarListModalParams } from '../chat/sidebar-list-modal.react.js';
import type { SubchannelListModalParams } from '../chat/subchannels-list-modal.react.js';
import type { TextMessageTooltipModalParams } from '../chat/text-message-tooltip-modal.react.js';
import type { TogglePinModalParams } from '../chat/toggle-pin-modal.react.js';
import type { CommunityCreationMembersScreenParams } from '../community-creation/community-creation-members.react.js';
import type { ManagePublicLinkScreenParams } from '../invite-links/manage-public-link-screen.react.js';
import type { ViewInviteLinksScreenParams } from '../invite-links/view-invite-links-screen.react.js';
import type { ChatCameraModalParams } from '../media/chat-camera-modal.react.js';
import type { ImageModalParams } from '../media/image-modal.react.js';
import type { ThreadAvatarCameraModalParams } from '../media/thread-avatar-camera-modal.react.js';
import type { VideoPlaybackModalParams } from '../media/video-playback-modal.react.js';
import type { CustomServerModalParams } from '../profile/custom-server-modal.react.js';
import type { KeyserverSelectionBottomSheetParams } from '../profile/keyserver-selection-bottom-sheet.react.js';
import type { UserRelationshipTooltipModalParams } from '../profile/user-relationship-tooltip-modal.react.js';
import type { ChangeRolesScreenParams } from '../roles/change-roles-screen.react.js';
import type { CommunityRolesScreenParams } from '../roles/community-roles-screen.react.js';
import type { CreateRolesScreenParams } from '../roles/create-roles-screen.react.js';
import type { MessageSearchParams } from '../search/message-search.react.js';
import type { UserProfileAvatarModalParams } from '../user-profile/user-profile-avatar-modal.react.js';
import type { UserProfileBottomSheetParams } from '../user-profile/user-profile-bottom-sheet.react.js';
export const ActionResultModalRouteName = 'ActionResultModal';
export const AddUsersModalRouteName = 'AddUsersModal';
export const AppearancePreferencesRouteName = 'AppearancePreferences';
export const AppRouteName = 'App';
export const AppsRouteName = 'Apps';
export const BackgroundChatThreadListRouteName = 'BackgroundChatThreadList';
export const BackupMenuRouteName = 'BackupMenu';
export const BlockListRouteName = 'BlockList';
export const BuildInfoRouteName = 'BuildInfo';
export const CalendarRouteName = 'Calendar';
export const ChangeRolesScreenRouteName = 'ChangeRolesScreen';
export const ChatCameraModalRouteName = 'ChatCameraModal';
export const ChatRouteName = 'Chat';
export const ChatThreadListRouteName = 'ChatThreadList';
export const ColorSelectorModalRouteName = 'ColorSelectorModal';
export const ComposeSubchannelModalRouteName = 'ComposeSubchannelModal';
export const ComposeSubchannelRouteName = 'ComposeSubchannel';
export const CommunityDrawerNavigatorRouteName = 'CommunityDrawerNavigator';
export const CustomServerModalRouteName = 'CustomServerModal';
export const DefaultNotificationsPreferencesRouteName = 'DefaultNotifications';
export const DeleteAccountRouteName = 'DeleteAccount';
export const DeleteThreadRouteName = 'DeleteThread';
export const DevToolsRouteName = 'DevTools';
export const EditPasswordRouteName = 'EditPassword';
export const EmojiThreadAvatarCreationRouteName = 'EmojiThreadAvatarCreation';
export const EmojiUserAvatarCreationRouteName = 'EmojiUserAvatarCreation';
export const FriendListRouteName = 'FriendList';
export const FullScreenThreadMediaGalleryRouteName =
'FullScreenThreadMediaGallery';
export const HomeChatThreadListRouteName = 'HomeChatThreadList';
export const ImageModalRouteName = 'ImageModal';
export const ImagePasteModalRouteName = 'ImagePasteModal';
export const InviteLinkModalRouteName = 'InviteLinkModal';
export const InviteLinkNavigatorRouteName = 'InviteLinkNavigator';
export const LinkedDevicesRouteName = 'LinkedDevices';
export const LoggedOutModalRouteName = 'LoggedOutModal';
export const ManagePublicLinkRouteName = 'ManagePublicLink';
export const MessageListRouteName = 'MessageList';
export const MessageReactionsModalRouteName = 'MessageReactionsModal';
export const PinnedMessagesScreenRouteName = 'PinnedMessagesScreen';
export const MultimediaMessageTooltipModalRouteName =
'MultimediaMessageTooltipModal';
export const PrivacyPreferencesRouteName = 'PrivacyPreferences';
export const ProfileRouteName = 'Profile';
export const ProfileScreenRouteName = 'ProfileScreen';
export const UserRelationshipTooltipModalRouteName =
'UserRelationshipTooltipModal';
export const RobotextMessageTooltipModalRouteName =
'RobotextMessageTooltipModal';
export const SecondaryDeviceQRCodeScannerRouteName =
'SecondaryDeviceQRCodeScanner';
export const SidebarListModalRouteName = 'SidebarListModal';
export const SubchannelsListModalRouteName = 'SubchannelsListModal';
export const TabNavigatorRouteName = 'TabNavigator';
export const TextMessageTooltipModalRouteName = 'TextMessageTooltipModal';
export const ThreadAvatarCameraModalRouteName = 'ThreadAvatarCameraModal';
export const ThreadPickerModalRouteName = 'ThreadPickerModal';
export const ThreadSettingsMemberTooltipModalRouteName =
'ThreadSettingsMemberTooltipModal';
export const ThreadSettingsRouteName = 'ThreadSettings';
export const TunnelbrokerMenuRouteName = 'TunnelbrokerMenu';
export const UserAvatarCameraModalRouteName = 'UserAvatarCameraModal';
export const TogglePinModalRouteName = 'TogglePinModal';
export const VideoPlaybackModalRouteName = 'VideoPlaybackModal';
export const ViewInviteLinksRouteName = 'ViewInviteLinks';
export const TermsAndPrivacyRouteName = 'TermsAndPrivacyModal';
export const RegistrationRouteName = 'Registration';
export const KeyserverSelectionRouteName = 'KeyserverSelection';
export const CoolOrNerdModeSelectionRouteName = 'CoolOrNerdModeSelection';
export const ConnectEthereumRouteName = 'ConnectEthereum';
+export const CreateSIWEBackupMessageRouteName = 'CreateSIWEBackupMessage';
export const ExistingEthereumAccountRouteName = 'ExistingEthereumAccount';
export const ConnectFarcasterRouteName = 'ConnectFarcaster';
export const UsernameSelectionRouteName = 'UsernameSelection';
export const CommunityCreationRouteName = 'CommunityCreation';
export const CommunityConfigurationRouteName = 'CommunityConfiguration';
export const CommunityCreationMembersRouteName = 'CommunityCreationMembers';
export const MessageSearchRouteName = 'MessageSearch';
export const PasswordSelectionRouteName = 'PasswordSelection';
export const AvatarSelectionRouteName = 'AvatarSelection';
export const EmojiAvatarSelectionRouteName = 'EmojiAvatarSelection';
export const RegistrationUserAvatarCameraModalRouteName =
'RegistrationUserAvatarCameraModal';
export const RegistrationTermsRouteName = 'RegistrationTerms';
export const RolesNavigatorRouteName = 'RolesNavigator';
export const CommunityRolesScreenRouteName = 'CommunityRolesScreen';
export const CreateRolesScreenRouteName = 'CreateRolesScreen';
export const QRCodeSignInNavigatorRouteName = 'QRCodeSignInNavigator';
export const QRCodeScreenRouteName = 'QRCodeScreen';
export const UserProfileBottomSheetNavigatorRouteName =
'UserProfileBottomSheetNavigator';
export const UserProfileBottomSheetRouteName = 'UserProfileBottomSheet';
export const UserProfileAvatarModalRouteName = 'UserProfileAvatarModal';
export const KeyserverSelectionListRouteName = 'KeyserverSelectionList';
export const AddKeyserverRouteName = 'AddKeyserver';
export const KeyserverSelectionBottomSheetRouteName =
'KeyserverSelectionBottomSheet';
export const AccountDoesNotExistRouteName = 'AccountDoesNotExist';
export const FarcasterAccountSettingsRouteName = 'FarcasterAccountSettings';
export const ConnectFarcasterBottomSheetRouteName =
'ConnectFarcasterBottomSheet';
export const TagFarcasterChannelNavigatorRouteName =
'TagFarcasterChannelNavigator';
export const TagFarcasterChannelRouteName = 'TagFarcasterChannel';
export type RootParamList = {
+LoggedOutModal: void,
+App: void,
+ThreadPickerModal: ThreadPickerModalParams,
+AddUsersModal: AddUsersModalParams,
+CustomServerModal: CustomServerModalParams,
+ColorSelectorModal: ColorSelectorModalParams,
+ComposeSubchannelModal: ComposeSubchannelModalParams,
+SidebarListModal: SidebarListModalParams,
+ImagePasteModal: ImagePasteModalParams,
+TermsAndPrivacyModal: TermsAndPrivacyModalParams,
+SubchannelsListModal: SubchannelListModalParams,
+MessageReactionsModal: MessageReactionsModalParams,
+Registration: void,
+CommunityCreation: void,
+InviteLinkModal: InviteLinkModalParams,
+InviteLinkNavigator: void,
+RolesNavigator: void,
+QRCodeSignInNavigator: void,
+UserProfileBottomSheetNavigator: void,
+TunnelbrokerMenu: void,
+KeyserverSelectionBottomSheet: KeyserverSelectionBottomSheetParams,
+ConnectFarcasterBottomSheet: void,
+TagFarcasterChannelNavigator: void,
};
export type MessageTooltipRouteNames =
| typeof RobotextMessageTooltipModalRouteName
| typeof MultimediaMessageTooltipModalRouteName
| typeof TextMessageTooltipModalRouteName;
export const PinnableMessageTooltipRouteNames = [
TextMessageTooltipModalRouteName,
MultimediaMessageTooltipModalRouteName,
];
export type TooltipModalParamList = {
+MultimediaMessageTooltipModal: MultimediaMessageTooltipModalParams,
+TextMessageTooltipModal: TextMessageTooltipModalParams,
+ThreadSettingsMemberTooltipModal: ThreadSettingsMemberTooltipModalParams,
+UserRelationshipTooltipModal: UserRelationshipTooltipModalParams,
+RobotextMessageTooltipModal: RobotextMessageTooltipModalParams,
};
export type OverlayParamList = {
+CommunityDrawerNavigator: void,
+ImageModal: ImageModalParams,
+ActionResultModal: ActionResultModalParams,
+ChatCameraModal: ChatCameraModalParams,
+UserAvatarCameraModal: void,
+ThreadAvatarCameraModal: ThreadAvatarCameraModalParams,
+VideoPlaybackModal: VideoPlaybackModalParams,
+TogglePinModal: TogglePinModalParams,
...TooltipModalParamList,
};
export type TabParamList = {
+Calendar: void,
+Chat: void,
+Profile: void,
+Apps: void,
};
export type ChatParamList = {
+ChatThreadList: void,
+MessageList: MessageListParams,
+ComposeSubchannel: ComposeSubchannelParams,
+ThreadSettings: ThreadSettingsParams,
+EmojiThreadAvatarCreation: EmojiThreadAvatarCreationParams,
+DeleteThread: DeleteThreadParams,
+FullScreenThreadMediaGallery: FullScreenThreadMediaGalleryParams,
+PinnedMessagesScreen: PinnedMessagesScreenParams,
+MessageSearch: MessageSearchParams,
+ChangeRolesScreen: ChangeRolesScreenParams,
};
export type ChatTopTabsParamList = {
+HomeChatThreadList: void,
+BackgroundChatThreadList: void,
};
export type ProfileParamList = {
+ProfileScreen: void,
+EmojiUserAvatarCreation: void,
+EditPassword: void,
+DeleteAccount: void,
+BuildInfo: void,
+DevTools: void,
+AppearancePreferences: void,
+PrivacyPreferences: void,
+DefaultNotifications: void,
+FriendList: void,
+BlockList: void,
+LinkedDevices: void,
+SecondaryDeviceQRCodeScanner: void,
+BackupMenu: void,
+TunnelbrokerMenu: void,
+KeyserverSelectionList: void,
+AddKeyserver: void,
+FarcasterAccountSettings: void,
};
export type CommunityDrawerParamList = { +TabNavigator: void };
export type RegistrationParamList = {
+CoolOrNerdModeSelection: void,
+KeyserverSelection: KeyserverSelectionParams,
+ConnectEthereum: ConnectEthereumParams,
+ExistingEthereumAccount: ExistingEthereumAccountParams,
+ConnectFarcaster: ConnectFarcasterParams,
+ +CreateSIWEBackupMessage: CreateSIWEBackupMessageParams,
+UsernameSelection: UsernameSelectionParams,
+PasswordSelection: PasswordSelectionParams,
+AvatarSelection: AvatarSelectionParams,
+EmojiAvatarSelection: EmojiAvatarSelectionParams,
+RegistrationUserAvatarCameraModal: void,
+RegistrationTerms: RegistrationTermsParams,
+AccountDoesNotExist: void,
};
export type InviteLinkParamList = {
+ViewInviteLinks: ViewInviteLinksScreenParams,
+ManagePublicLink: ManagePublicLinkScreenParams,
};
export type CommunityCreationParamList = {
+CommunityConfiguration: void,
+CommunityCreationMembers: CommunityCreationMembersScreenParams,
};
export type RolesParamList = {
+CommunityRolesScreen: CommunityRolesScreenParams,
+CreateRolesScreen: CreateRolesScreenParams,
};
export type TagFarcasterChannelParamList = {
+TagFarcasterChannel: void,
};
export type QRCodeSignInParamList = {
+QRCodeScreen: void,
};
export type UserProfileBottomSheetParamList = {
+UserProfileBottomSheet: UserProfileBottomSheetParams,
+UserProfileAvatarModal: UserProfileAvatarModalParams,
+UserRelationshipTooltipModal: UserRelationshipTooltipModalParams,
};
export type ScreenParamList = {
...RootParamList,
...OverlayParamList,
...TabParamList,
...ChatParamList,
...ChatTopTabsParamList,
...ProfileParamList,
...CommunityDrawerParamList,
...RegistrationParamList,
...InviteLinkParamList,
...CommunityCreationParamList,
...RolesParamList,
...QRCodeSignInParamList,
...UserProfileBottomSheetParamList,
...TagFarcasterChannelParamList,
};
export type NavigationRoute> =
RouteProp;
export const accountModals = [
LoggedOutModalRouteName,
RegistrationRouteName,
QRCodeSignInNavigatorRouteName,
];
export const scrollBlockingModals = [
ImageModalRouteName,
MultimediaMessageTooltipModalRouteName,
TextMessageTooltipModalRouteName,
ThreadSettingsMemberTooltipModalRouteName,
UserRelationshipTooltipModalRouteName,
RobotextMessageTooltipModalRouteName,
VideoPlaybackModalRouteName,
];
export const chatRootModals = [
AddUsersModalRouteName,
ColorSelectorModalRouteName,
ComposeSubchannelModalRouteName,
];
export const threadRoutes = [
MessageListRouteName,
ThreadSettingsRouteName,
DeleteThreadRouteName,
ComposeSubchannelRouteName,
FullScreenThreadMediaGalleryRouteName,
PinnedMessagesScreenRouteName,
MessageSearchRouteName,
EmojiThreadAvatarCreationRouteName,
CommunityRolesScreenRouteName,
];
diff --git a/native/themes/colors.js b/native/themes/colors.js
index 4a3485bae..ec9b809a3 100644
--- a/native/themes/colors.js
+++ b/native/themes/colors.js
@@ -1,393 +1,395 @@
// @flow
import * as React from 'react';
import { StyleSheet } from 'react-native';
import { createSelector } from 'reselect';
import type { GlobalTheme } from 'lib/types/theme-types.js';
import { selectBackgroundIsDark } from '../navigation/nav-selectors.js';
import { NavContext } from '../navigation/navigation-context.js';
import { useSelector } from '../redux/redux-utils.js';
import type { AppState } from '../redux/state-types.js';
const designSystemColors = Object.freeze({
shadesWhite100: '#ffffff',
shadesWhite90: '#f5f5f5',
shadesWhite80: '#ebebeb',
shadesWhite70: '#e0e0e0',
shadesWhite60: '#cccccc',
shadesBlack95: '#0a0a0a',
shadesBlack90: '#191919',
shadesBlack85: '#1f1f1f',
shadesBlack75: '#404040',
shadesBlack60: '#666666',
shadesBlack50: '#808080',
violetDark100: '#7e57c2',
violetDark80: '#6d49ab',
violetDark60: '#563894',
violetDark40: '#44297a',
violetDark20: '#331f5c',
violetLight100: '#ae94db',
violetLight80: '#b9a4df',
violetLight60: '#d3c6ec',
violetLight40: '#e8e0f5',
violetLight20: '#f3f0fa',
successLight10: '#d5f6e3',
successLight50: '#6cdf9c',
successPrimary: '#00c853',
successDark50: '#029841',
successDark90: '#034920',
errorLight10: '#feebe6',
errorLight50: '#f9947b',
errorPrimary: '#f53100',
errorDark50: '#b62602',
errorDark90: '#4f1203',
spoilerColor: '#33332c',
});
const light = Object.freeze({
blockQuoteBackground: designSystemColors.shadesWhite70,
blockQuoteBorder: designSystemColors.shadesWhite60,
codeBackground: designSystemColors.shadesWhite70,
disabledButton: designSystemColors.shadesWhite70,
disabledButtonText: designSystemColors.shadesBlack50,
disconnectedBarBackground: designSystemColors.shadesWhite90,
editButton: '#A4A4A2',
floatingButtonBackground: '#999999',
floatingButtonLabel: designSystemColors.shadesWhite80,
headerChevron: designSystemColors.shadesBlack95,
inlineEngagementBackground: designSystemColors.shadesWhite70,
inlineEngagementLabel: designSystemColors.shadesBlack95,
link: designSystemColors.violetDark100,
listBackground: designSystemColors.shadesWhite100,
listBackgroundLabel: designSystemColors.shadesBlack95,
listBackgroundSecondaryLabel: '#444444',
listBackgroundTernaryLabel: '#999999',
listChatBubble: '#F1F0F5',
listForegroundLabel: designSystemColors.shadesBlack95,
listForegroundSecondaryLabel: '#333333',
listForegroundTertiaryLabel: designSystemColors.shadesBlack60,
listInputBackground: designSystemColors.shadesWhite90,
listInputBar: '#E2E2E2',
listInputButton: '#8E8D92',
listIosHighlightUnderlay: '#DDDDDDDD',
listSearchBackground: designSystemColors.shadesWhite90,
listSearchIcon: '#8E8D92',
listSeparatorLabel: designSystemColors.shadesBlack60,
modalBackground: designSystemColors.shadesWhite80,
modalBackgroundLabel: '#333333',
modalBackgroundSecondaryLabel: '#AAAAAA',
modalButton: '#BBBBBB',
modalButtonLabel: designSystemColors.shadesBlack95,
modalContrastBackground: designSystemColors.shadesBlack95,
modalContrastForegroundLabel: designSystemColors.shadesWhite100,
modalContrastOpacity: 0.7,
modalForeground: designSystemColors.shadesWhite100,
modalForegroundBorder: designSystemColors.shadesWhite60,
modalForegroundLabel: designSystemColors.shadesBlack95,
modalForegroundSecondaryLabel: '#888888',
modalForegroundTertiaryLabel: '#AAAAAA',
modalIosHighlightUnderlay: '#CCCCCCDD',
modalSubtext: designSystemColors.shadesWhite60,
modalSubtextLabel: designSystemColors.shadesBlack60,
modalInputBackground: designSystemColors.shadesWhite60,
modalInputForeground: designSystemColors.shadesWhite90,
modalKnob: designSystemColors.shadesWhite90,
modalAccentBackground: designSystemColors.shadesWhite90,
navigationCard: designSystemColors.shadesWhite100,
navigationChevron: designSystemColors.shadesWhite60,
panelBackground: designSystemColors.shadesWhite90,
panelBackgroundLabel: '#888888',
panelButton: designSystemColors.shadesWhite70,
panelForeground: designSystemColors.shadesWhite100,
panelForegroundBorder: designSystemColors.shadesWhite60,
panelForegroundLabel: designSystemColors.shadesBlack95,
+ panelForegroundIcon: designSystemColors.shadesBlack95,
panelForegroundSecondaryLabel: '#333333',
panelForegroundTertiaryLabel: '#888888',
panelInputBackground: designSystemColors.shadesWhite60,
panelInputSecondaryForeground: designSystemColors.shadesBlack50,
panelIosHighlightUnderlay: '#EBEBEBDD',
panelSecondaryForeground: designSystemColors.shadesWhite80,
panelSecondaryForegroundBorder: designSystemColors.shadesWhite70,
panelSeparator: designSystemColors.shadesWhite60,
purpleLink: designSystemColors.violetDark100,
purpleButton: designSystemColors.violetDark100,
reactionSelectionPopoverItemBackground: designSystemColors.shadesBlack75,
redText: designSystemColors.errorPrimary,
spoiler: designSystemColors.spoilerColor,
tabBarAccent: designSystemColors.violetDark100,
tabBarBackground: designSystemColors.shadesWhite90,
tabBarActiveTintColor: designSystemColors.violetDark100,
vibrantGreenButton: designSystemColors.successPrimary,
vibrantRedButton: designSystemColors.errorPrimary,
whiteText: designSystemColors.shadesWhite100,
tooltipBackground: designSystemColors.shadesWhite70,
logInSpacer: '#FFFFFF33',
siweButton: designSystemColors.shadesWhite100,
siweButtonText: designSystemColors.shadesBlack85,
drawerExpandButton: designSystemColors.shadesBlack50,
drawerExpandButtonDisabled: designSystemColors.shadesWhite60,
drawerItemLabelLevel0: designSystemColors.shadesBlack95,
drawerItemLabelLevel1: designSystemColors.shadesBlack95,
drawerItemLabelLevel2: designSystemColors.shadesBlack85,
drawerOpenCommunityBackground: designSystemColors.shadesWhite90,
drawerBackground: designSystemColors.shadesWhite100,
subthreadsModalClose: designSystemColors.shadesBlack50,
subthreadsModalBackground: designSystemColors.shadesWhite80,
subthreadsModalSearch: '#00000008',
messageLabel: designSystemColors.shadesBlack95,
modalSeparator: designSystemColors.shadesWhite60,
secondaryButtonBorder: designSystemColors.shadesWhite100,
inviteLinkLinkColor: designSystemColors.shadesBlack95,
inviteLinkButtonBackground: designSystemColors.shadesWhite60,
greenIndicatorInner: designSystemColors.successPrimary,
greenIndicatorOuter: designSystemColors.successDark50,
redIndicatorInner: designSystemColors.errorPrimary,
redIndicatorOuter: designSystemColors.errorDark50,
});
export type Colors = $Exact;
const dark: Colors = Object.freeze({
blockQuoteBackground: '#A9A9A9',
blockQuoteBorder: designSystemColors.shadesBlack50,
codeBackground: designSystemColors.shadesBlack95,
disabledButton: designSystemColors.shadesBlack75,
disabledButtonText: designSystemColors.shadesBlack50,
disconnectedBarBackground: designSystemColors.shadesBlack85,
editButton: designSystemColors.shadesBlack60,
floatingButtonBackground: designSystemColors.shadesBlack60,
floatingButtonLabel: designSystemColors.shadesWhite100,
headerChevron: designSystemColors.shadesWhite100,
inlineEngagementBackground: designSystemColors.shadesBlack60,
inlineEngagementLabel: designSystemColors.shadesWhite100,
link: designSystemColors.violetLight100,
listBackground: designSystemColors.shadesBlack95,
listBackgroundLabel: designSystemColors.shadesWhite60,
listBackgroundSecondaryLabel: '#BBBBBB',
listBackgroundTernaryLabel: designSystemColors.shadesBlack50,
listChatBubble: '#26252A',
listForegroundLabel: designSystemColors.shadesWhite100,
listForegroundSecondaryLabel: designSystemColors.shadesWhite60,
listForegroundTertiaryLabel: designSystemColors.shadesBlack50,
listInputBackground: designSystemColors.shadesBlack85,
listInputBar: designSystemColors.shadesBlack60,
listInputButton: designSystemColors.shadesWhite60,
listIosHighlightUnderlay: '#BBBBBB88',
listSearchBackground: designSystemColors.shadesBlack85,
listSearchIcon: designSystemColors.shadesWhite60,
listSeparatorLabel: designSystemColors.shadesWhite80,
modalBackground: designSystemColors.shadesBlack95,
modalBackgroundLabel: designSystemColors.shadesWhite60,
modalBackgroundSecondaryLabel: designSystemColors.shadesBlack60,
modalButton: designSystemColors.shadesBlack60,
modalButtonLabel: designSystemColors.shadesWhite100,
modalContrastBackground: designSystemColors.shadesWhite100,
modalContrastForegroundLabel: designSystemColors.shadesBlack95,
modalContrastOpacity: 0.85,
modalForeground: designSystemColors.shadesBlack85,
modalForegroundBorder: designSystemColors.shadesBlack85,
modalForegroundLabel: designSystemColors.shadesWhite100,
modalForegroundSecondaryLabel: '#AAAAAA',
modalForegroundTertiaryLabel: designSystemColors.shadesBlack60,
modalIosHighlightUnderlay: '#AAAAAA88',
modalSubtext: designSystemColors.shadesBlack75,
modalSubtextLabel: '#AAAAAA',
modalInputBackground: designSystemColors.shadesBlack75,
modalInputForeground: designSystemColors.shadesBlack50,
modalKnob: designSystemColors.shadesWhite90,
modalAccentBackground: designSystemColors.shadesBlack90,
navigationCard: '#2A2A2A',
navigationChevron: designSystemColors.shadesBlack60,
panelBackground: designSystemColors.shadesBlack95,
panelBackgroundLabel: designSystemColors.shadesWhite60,
panelButton: designSystemColors.shadesBlack60,
panelForeground: designSystemColors.shadesBlack85,
panelForegroundBorder: '#2C2C2E',
panelForegroundLabel: designSystemColors.shadesWhite100,
+ panelForegroundIcon: designSystemColors.shadesWhite100,
panelForegroundSecondaryLabel: designSystemColors.shadesWhite60,
panelForegroundTertiaryLabel: '#AAAAAA',
panelInputBackground: designSystemColors.shadesBlack75,
panelInputSecondaryForeground: designSystemColors.shadesBlack50,
panelIosHighlightUnderlay: '#313035',
panelSecondaryForeground: designSystemColors.shadesBlack75,
panelSecondaryForegroundBorder: designSystemColors.shadesBlack60,
panelSeparator: designSystemColors.shadesBlack75,
purpleLink: designSystemColors.violetLight100,
purpleButton: designSystemColors.violetDark100,
reactionSelectionPopoverItemBackground: designSystemColors.shadesBlack75,
redText: designSystemColors.errorPrimary,
spoiler: designSystemColors.spoilerColor,
tabBarAccent: designSystemColors.violetLight100,
tabBarBackground: designSystemColors.shadesBlack95,
tabBarActiveTintColor: designSystemColors.violetLight100,
vibrantGreenButton: designSystemColors.successPrimary,
vibrantRedButton: designSystemColors.errorPrimary,
whiteText: designSystemColors.shadesWhite100,
tooltipBackground: designSystemColors.shadesBlack85,
logInSpacer: '#FFFFFF33',
siweButton: designSystemColors.shadesWhite100,
siweButtonText: designSystemColors.shadesBlack85,
drawerExpandButton: designSystemColors.shadesBlack50,
drawerExpandButtonDisabled: designSystemColors.shadesBlack75,
drawerItemLabelLevel0: designSystemColors.shadesWhite60,
drawerItemLabelLevel1: designSystemColors.shadesWhite60,
drawerItemLabelLevel2: designSystemColors.shadesWhite90,
drawerOpenCommunityBackground: designSystemColors.shadesBlack90,
drawerBackground: designSystemColors.shadesBlack85,
subthreadsModalClose: designSystemColors.shadesBlack50,
subthreadsModalBackground: designSystemColors.shadesBlack85,
subthreadsModalSearch: '#FFFFFF04',
typeaheadTooltipBackground: '#1F1F1f',
typeaheadTooltipBorder: designSystemColors.shadesBlack75,
typeaheadTooltipText: 'white',
messageLabel: designSystemColors.shadesWhite60,
modalSeparator: designSystemColors.shadesBlack75,
secondaryButtonBorder: designSystemColors.shadesWhite100,
inviteLinkLinkColor: designSystemColors.shadesWhite80,
inviteLinkButtonBackground: designSystemColors.shadesBlack75,
greenIndicatorInner: designSystemColors.successPrimary,
greenIndicatorOuter: designSystemColors.successDark90,
redIndicatorInner: designSystemColors.errorPrimary,
redIndicatorOuter: designSystemColors.errorDark90,
});
const colors = { light, dark };
const colorsSelector: (state: AppState) => Colors = createSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
(theme: ?GlobalTheme) => {
const explicitTheme = theme ? theme : 'light';
return colors[explicitTheme];
},
);
const magicStrings = new Set();
for (const theme in colors) {
for (const magicString in colors[theme]) {
magicStrings.add(magicString);
}
}
type Styles = { [name: string]: { [field: string]: mixed } };
type ReplaceField = (input: any) => any;
export type StyleSheetOf = $ReadOnly<$ObjMap>;
function stylesFromColors(
obj: IS,
themeColors: Colors,
): StyleSheetOf {
const result: Styles = {};
for (const key in obj) {
const style = obj[key];
const filledInStyle = { ...style };
for (const styleKey in style) {
const styleValue = style[styleKey];
if (typeof styleValue !== 'string') {
continue;
}
if (magicStrings.has(styleValue)) {
const mapped = themeColors[styleValue];
if (mapped) {
filledInStyle[styleKey] = mapped;
}
}
}
result[key] = filledInStyle;
}
return StyleSheet.create(result);
}
function styleSelector(
obj: IS,
): (state: AppState) => StyleSheetOf {
return createSelector(colorsSelector, (themeColors: Colors) =>
stylesFromColors(obj, themeColors),
);
}
function useStyles(obj: IS): StyleSheetOf {
const ourColors = useColors();
return React.useMemo(
() => stylesFromColors(obj, ourColors),
[obj, ourColors],
);
}
function useOverlayStyles(obj: IS): StyleSheetOf {
const navContext = React.useContext(NavContext);
const navigationState = navContext && navContext.state;
const theme = useSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
);
const backgroundIsDark = React.useMemo(
() => selectBackgroundIsDark(navigationState, theme),
[navigationState, theme],
);
const syntheticTheme = backgroundIsDark ? 'dark' : 'light';
return React.useMemo(
() => stylesFromColors(obj, colors[syntheticTheme]),
[obj, syntheticTheme],
);
}
function useColors(): Colors {
return useSelector(colorsSelector);
}
function getStylesForTheme(
obj: IS,
theme: GlobalTheme,
): StyleSheetOf {
return stylesFromColors(obj, colors[theme]);
}
export type IndicatorStyle = 'white' | 'black';
function useIndicatorStyle(): IndicatorStyle {
const theme = useSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
);
return theme && theme === 'dark' ? 'white' : 'black';
}
const indicatorStyleSelector: (state: AppState) => IndicatorStyle =
createSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
(theme: ?GlobalTheme) => {
return theme && theme === 'dark' ? 'white' : 'black';
},
);
export type KeyboardAppearance = 'default' | 'light' | 'dark';
const keyboardAppearanceSelector: (state: AppState) => KeyboardAppearance =
createSelector(
(state: AppState) => state.globalThemeInfo.activeTheme,
(theme: ?GlobalTheme) => {
return theme && theme === 'dark' ? 'dark' : 'light';
},
);
function useKeyboardAppearance(): KeyboardAppearance {
return useSelector(keyboardAppearanceSelector);
}
export {
colors,
colorsSelector,
styleSelector,
useStyles,
useOverlayStyles,
useColors,
getStylesForTheme,
useIndicatorStyle,
indicatorStyleSelector,
useKeyboardAppearance,
};