diff --git a/ios/EphemeraMobileChat.xcodeproj/project.pbxproj b/ios/EphemeraMobileChat.xcodeproj/project.pbxproj index f240d5a..8f1cc98 100644 --- a/ios/EphemeraMobileChat.xcodeproj/project.pbxproj +++ b/ios/EphemeraMobileChat.xcodeproj/project.pbxproj @@ -672,11 +672,7 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_VERSION = 5.0; @@ -745,11 +741,7 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_VERSION = 5.0; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1499500..b3bb776 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1666,8 +1666,8 @@ SPEC CHECKSUMS: web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959 XMTP: 1deb40ac712ba315dcfdecd590a9b924d8c2241a XMTPReactNative: ffc7fb00c38f3d3f8af038ae5d1f9712cac0bb5f - Yoga: 2a16e58450c48e110211dae1159fb114bbcdcfc0 + Yoga: e5b887426cee15d2a326bdd34afc0282fc0486ad PODFILE CHECKSUM: 47641fd2f7a6fc6169785131e7615d136e56edd8 -COCOAPODS: 1.15.2 +COCOAPODS: 1.14.3 diff --git a/src/components/ConversationListHeader.tsx b/src/components/ConversationListHeader.tsx index c564c45..fbf20e2 100644 --- a/src/components/ConversationListHeader.tsx +++ b/src/components/ConversationListHeader.tsx @@ -23,7 +23,7 @@ export const ConversationListHeader: FC = () => { const {avatarUrl} = useContactInfo(address); const handleNewMessagePress = useCallback(() => { - navigate(ScreenNames.Search); + navigate(ScreenNames.CreateGroup); }, [navigate]); const handleAccountPress = useCallback(() => { diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f959d8b..f55a45b 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -18,7 +18,8 @@ "no": "No", "yes": "Yes", "start": "Start", - "recents": "Recents", + + "recents": "Recent", "contacts": "Contacts", "your_interoperable_web3_inbox": "Your interoperable web3 inbox", "youre_just_a_few_steps_away_from_secure_wallet_to_wallet_messaging": "You're just a few steps away from secure, wallet-to-wallet messaging", @@ -43,6 +44,7 @@ "guest_wallet": "Guest Wallet", "group_changed": "Group Changed", "add_to_group": "Add", + "add_members_to_group": "Add members", "group": "Group", "dev_screen": "Dev Screen", "client": "Client", @@ -68,5 +70,8 @@ "reply": "Reply", "conversation_replying_to": "Replying to %{name}", "replied_to": "Replied to an earlier message", - "group_chats": "Group Chats" -} \ No newline at end of file + "group_chats": "Group Chats", + + "cancel": "Cancel", + "create_group": "Create" +} diff --git a/src/navigation/AppNavigation.tsx b/src/navigation/AppNavigation.tsx index beb1c8f..42a48de 100644 --- a/src/navigation/AppNavigation.tsx +++ b/src/navigation/AppNavigation.tsx @@ -5,6 +5,7 @@ import {Platform} from 'react-native'; import {useClientContext} from '../context/ClientContext'; import {AccountSettingsScreen} from '../screens/AccountSettingsScreen'; import {ConversationListScreen} from '../screens/ConversationListScreen'; +import {CreateGroupScreen} from '../screens/CreateGroupScreen'; import {DevScreen} from '../screens/DevScreen'; import {GroupScreen} from '../screens/GroupScreen'; import {LoadingScreen} from '../screens/LoadingScreen'; @@ -12,7 +13,6 @@ import {NewConversationScreen} from '../screens/NewConversationScreen'; import {OnboardingConnectWalletScreen} from '../screens/OnboardingConnectWalletScreen'; import {OnboardingEnableIdentityScreen} from '../screens/OnboardingEnableIdentityScreen'; import {QrCodeScreen} from '../screens/QrCodeScreen'; -import {SearchScreen} from '../screens/SearchScreen'; import {UserProfilesScreen} from '../screens/UserProfilesScreen'; import {ScreenNames} from './ScreenNames'; import { @@ -87,8 +87,8 @@ export const AppNavigation = () => { component={NewConversationScreen} /> = { addresses: addresses => addresses.split(','), }, }, - [ScreenNames.Search]: 'search', + [ScreenNames.CreateGroup]: 'create_group', [ScreenNames.QRCode]: 'qr_code', [ScreenNames.UserProfiles]: 'user_profiles', }, diff --git a/src/screens/SearchScreen.tsx b/src/screens/CreateGroupScreen.tsx similarity index 69% rename from src/screens/SearchScreen.tsx rename to src/screens/CreateGroupScreen.tsx index fc0cb32..ff7998f 100644 --- a/src/screens/SearchScreen.tsx +++ b/src/screens/CreateGroupScreen.tsx @@ -3,9 +3,7 @@ import React, {FC, useCallback, useEffect, useMemo, useState} from 'react'; import {SectionListRenderItem} from 'react-native'; import {getAddress, isAddress} from 'viem'; import {AvatarWithFallback} from '../components/AvatarWithFallback'; -import {Button} from '../components/common/Button'; import {Icon} from '../components/common/Icon'; -import {Pill} from '../components/common/Pill'; import {Screen} from '../components/common/Screen'; import {Text} from '../components/common/Text'; import {TestIds} from '../consts/TestIds'; @@ -84,67 +82,117 @@ const useData = () => { }; }; -const ListItem: FC<{ - item: Contact; - section: { - title: string; - onPress: (item: Contact) => void; - data: readonly Contact[]; - }; - index: number; -}> = ({item, index, section}) => { +const VerticalListItem: FC<{item: Contact; index: number; section: any}> = ({ + item, + index, + section, +}) => { const {avatarUrl, displayName} = useContactInfo(item.address); - const isTop = index === 0; - const isBottom = index === section.data.length - 1; - const handlePress = () => { - section.onPress(item); - }; + const handlePress = () => section.onPress(item); + return ( + flexDirection="row"> - - + + {displayName ?? formatAddress(item.address)} + paddingLeft="16px"> {formatAddress(item.address)} - {item.isConnected ? : null} + {item.isConnected ? : null} ); }; -export const SearchScreen = () => { +const HorizontalListItem: FC<{item: Contact; index: number; section: any}> = ({ + item, + index, + section, +}) => { + const {avatarUrl, displayName} = useContactInfo(item.address); + const handlePress = () => section.onPress(item); + return ( + + + + + {displayName ?? formatAddress(item.address)} + + {item.isConnected ? : null} + + + ); +}; + +const ListItem: FC<{ + item: Contact; + section: { + title: string; + onPress: (item: Contact) => void; + data: readonly Contact[]; + }; + index: number; + horizontal?: boolean; +}> = ({item, index, section, horizontal}) => { + return horizontal ? ( + + ) : ( + + ); +}; + +export const CreateGroupScreen = () => { const {goBack, navigate} = useTypedNavigation(); const {client} = useClient(); const [errorString, setErrorString] = useState(null); const [searchText, setSearchText] = useState(''); - const [participants, setParticipants] = useState([]); + const [participants, setParticipants] = useState([]); const {recents, contacts} = useData(); const onItemPress = useCallback( (item: Contact) => { - setParticipants(prev => [...prev, item.address]); + setParticipants(prev => [...prev, item]); }, [setParticipants], ); @@ -152,15 +200,18 @@ export const SearchScreen = () => { const onGroupStart = useCallback(async () => { setErrorString(null); - const canMessage = await client?.canGroupMessage(participants); - for (const address of participants) { + const participantAddresses = participants.map( + participant => participant.address, + ); + const canMessage = await client?.canGroupMessage(participantAddresses); + for (const address of participantAddresses) { if (!canMessage?.[address.toLowerCase()]) { setErrorString(translate('not_on_xmtp_group')); return; } } goBack(); - navigate(ScreenNames.NewConversation, {addresses: participants}); + navigate(ScreenNames.NewConversation, {addresses: participantAddresses}); }, [participants, navigate, goBack, client]); const {data: ensAddress} = useEnsAddress(searchText); const isValidAddress = useMemo(() => isAddress(searchText), [searchText]); @@ -222,49 +273,51 @@ export const SearchScreen = () => { ]; }, [recents, contacts, isValidAddress, ensAddress, searchText]); - const renderItem: SectionListRenderItem = ({ - item, - section, - index, - ...rest - }) => { - return ( - - ); - }; - - const removeParticipant = useCallback( - (address: string) => { - setErrorString(null); - setParticipants(prev => prev.filter(it => it !== address)); - }, - [setParticipants], - ); + const renderItem = + ({horizontal = false}: {horizontal?: boolean} = {}): SectionListRenderItem< + Contact, + {title: string; data: Contact[]} + > => + ({item, section, index, ...rest}) => { + return ( + + ); + }; return ( - {translate('search')} + + {translate('add_members_to_group')} } + includeTopPadding={false} left={ - navigate(ScreenNames.QRCode)}> - + + + {translate('cancel')} + } right={ - - + 0)}> + 0 ? 'text-sm/semibold' : 'text-sm/regular' + } + textAlign={'right'}> + {translate('create_group')} + }> { )} - - {participants.map(participant => { - return ( - removeParticipant(participant)} - text={formatAddress(participant)} - /> - ); - })} - - {participants.length > 0 && ( - - )} + item.address} - renderItem={renderItem} + renderItem={renderItem()} stickySectionHeadersEnabled={false} renderSectionHeader={({section}) => { if (section.data.length === 0) {