diff --git a/native/components/community-joiner-modal.react.js b/native/components/community-joiner-modal.react.js new file mode 100644 --- /dev/null +++ b/native/components/community-joiner-modal.react.js @@ -0,0 +1,181 @@ +// @flow + +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { TabView, SceneMap, TabBar } from 'react-native-tab-view'; + +import { useThreadSearchIndex } from 'lib/selectors/nav-selectors.js'; +import { threadInfoFromRawThreadInfo } from 'lib/shared/thread-utils.js'; +import type { ClientCommunityInfoWithCommunityName } from 'lib/types/community-types.js'; + +import Modal from './modal.react.js'; +import CommunityList from '../components/community-list.react.js'; +import type { RootNavigationProp } from '../navigation/root-navigator.react.js'; +import type { NavigationRoute } from '../navigation/route-names.js'; +import { useSelector } from '../redux/redux-utils.js'; +import { useColors, useStyles } from '../themes/colors.js'; + +export type CommunityJoinerModalParams = { + +communities: $ReadOnlyArray, +}; + +type Props = { + +navigation: RootNavigationProp<'CommunityJoinerModal'>, + +route: NavigationRoute<'CommunityJoinerModal'>, +}; + +const defaultCommunities: $ReadOnlyArray = + []; + +// This should be updated with the names of the crypto communities on Comm +const cryptoCommunityNames: $ReadOnlyArray = []; + +function CommunityJoinerModal(props: Props): React.Node { + const { params } = props.route; + const communities = params?.communities ?? defaultCommunities; + + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const userInfos = useSelector(state => state.userStore.userInfos); + const styles = useStyles(unboundStyles); + + const generalCommunities = React.useMemo( + () => + communities.filter( + community => !cryptoCommunityNames.includes(community.communityName), + ), + [communities], + ); + const cryptoCommunities = React.useMemo( + () => + communities.filter(community => + cryptoCommunityNames.includes(community.communityName), + ), + [communities], + ); + + const generateThreadInfos = React.useCallback( + (communityList: $ReadOnlyArray) => + communityList + .map(community => + community.threadInfo + ? threadInfoFromRawThreadInfo( + community.threadInfo, + viewerID, + userInfos, + ) + : null, + ) + .filter(Boolean), + [userInfos, viewerID], + ); + + const generalThreadInfos = React.useMemo( + () => generateThreadInfos(generalCommunities), + [generateThreadInfos, generalCommunities], + ); + + const cryptoThreadInfos = React.useMemo( + () => generateThreadInfos(cryptoCommunities), + [generateThreadInfos, cryptoCommunities], + ); + + const generalIndex = useThreadSearchIndex(generalThreadInfos); + const cryptoIndex = useThreadSearchIndex(cryptoThreadInfos); + + const renderGeneralTab = () => ( + + ); + + const renderCryptoTab = () => ( + + ); + + const [index, setIndex] = React.useState(0); + const [routes] = React.useState([ + { key: 'general', title: 'General' }, + { key: 'crypto', title: 'Crypto' }, + ]); + + const renderScene = SceneMap({ + general: renderGeneralTab, + crypto: renderCryptoTab, + }); + + const colors = useColors(); + const { tabBarBackground, tabBarAccent } = colors; + + const screenOptions = React.useMemo( + () => ({ + tabBarShowIcon: true, + tabBarStyle: { + backgroundColor: tabBarBackground, + }, + tabBarItemStyle: { + flexDirection: 'row', + }, + tabBarIndicatorStyle: { + borderColor: tabBarAccent, + borderBottomWidth: 2, + }, + }), + [tabBarAccent, tabBarBackground], + ); + + const renderTabBar = (tabBarProps: mixed) => ( + + + + ); + + return ( + + + Discover communities + + + + ); +} + +const unboundStyles = { + headerContainer: { + padding: 10, + paddingBottom: 0, + }, + headerText: { + color: 'modalForegroundLabel', + fontSize: 20, + marginBottom: 8, + textAlign: 'center', + }, + threadListItem: { + paddingLeft: 10, + paddingRight: 10, + paddingVertical: 2, + }, + tabBarContainer: { + marginBottom: 15, + }, +}; + +export default CommunityJoinerModal; 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 @@ -58,6 +58,7 @@ CreateMissingSIWEBackupMessageRouteName, RestoreSIWEBackupRouteName, LinkedDevicesBottomSheetRouteName, + CommunityJoinerModalRouteName, } from './route-names.js'; import LoggedOutModal from '../account/logged-out-modal.react.js'; import CreateMissingSIWEBackupMessage from '../account/registration/missing-registration-data/missing-siwe-backup-message.react.js'; @@ -74,6 +75,7 @@ import SubchannelsListModal from '../chat/subchannels-list-modal.react.js'; import CommunityCreationNavigator from '../community-creation/community-creation-navigator.react.js'; import TagFarcasterChannelNavigator from '../community-settings/tag-farcaster-channel/tag-farcaster-channel-navigator.react.js'; +import CommunityJoinerModal from '../components/community-joiner-modal.react.js'; import ConnectFarcasterBottomSheet from '../components/connect-farcaster-bottom-sheet.react.js'; import DirectoryPromptBottomSheet from '../components/directory-prompt-bottom-sheet.react.js'; import InviteLinksNavigator from '../invite-links/invite-links-navigator.react.js'; @@ -311,6 +313,11 @@ component={DirectoryPromptBottomSheet} options={modalOverlayScreenOptions} /> +