From 54026ef24e1a7d5c8338b34f736b0e3b29449b77 Mon Sep 17 00:00:00 2001 From: Michael Lustig Date: Fri, 10 Jan 2025 17:14:56 -0500 Subject: [PATCH 01/16] progress --- containers/GroupScreenAddition.tsx | 38 +- .../conversation-list.screen.tsx | 7 +- .../conversation-composer.tsx | 5 +- .../InviteUsersToExistingGroup.nav.tsx | 37 ++ ...invite-users-to-exisiting-group.screen.tsx | 583 ++++++++++++++++++ features/new-chat/readme.md | 4 + .../ProfileSearchResultsList.tsx} | 4 +- i18n/translations/en.ts | 4 +- i18n/translations/fr.ts | 4 +- ios/Podfile.lock | 10 +- screens/Main.tsx | 4 +- screens/Navigation/Navigation.tsx | 16 +- screens/Navigation/NewConversationNav.tsx | 21 +- screens/NewConversation/NewConversation.tsx | 195 ++++-- .../NewConversation/NewConversationModal.tsx | 44 +- 15 files changed, 879 insertions(+), 97 deletions(-) create mode 100644 features/groups/invite-to-group/InviteUsersToExistingGroup.nav.tsx create mode 100644 features/groups/invite-to-group/invite-users-to-exisiting-group.screen.tsx create mode 100644 features/new-chat/readme.md rename features/search/{screens/ProfileSearch.tsx => components/ProfileSearchResultsList.tsx} (96%) diff --git a/containers/GroupScreenAddition.tsx b/containers/GroupScreenAddition.tsx index a16f3183b..717ae974c 100644 --- a/containers/GroupScreenAddition.tsx +++ b/containers/GroupScreenAddition.tsx @@ -42,6 +42,7 @@ import { saveInviteIdByGroupId, } from "../features/GroupInvites/groupInvites.utils"; import { captureErrorWithToast } from "@/utils/capture-error"; +import logger from "@/utils/logger"; type GroupScreenAdditionProps = { topic: ConversationTopic; @@ -52,20 +53,25 @@ const noop = () => {}; export const GroupScreenAddition: FC = ({ topic, }) => { + logger.debug("[GroupScreenAddition] Rendering component", { topic }); + const colorScheme = useColorScheme(); const currentAccount = useCurrentAccount() as string; const { members } = useGroupMembers(topic); - const { currentAccountIsAdmin, currentAccountIsSuperAdmin } = useMemo( - () => ({ + const { currentAccountIsAdmin, currentAccountIsSuperAdmin } = useMemo(() => { + logger.debug("[GroupScreenAddition] Calculating admin status", { + members, + currentAccount, + }); + return { currentAccountIsAdmin: getAddressIsAdmin(members, currentAccount), currentAccountIsSuperAdmin: getAddressIsSuperAdmin( members, currentAccount ), - }), - [currentAccount, members] - ); + }; + }, [currentAccount, members]); const styles = useStyles(); const [snackMessage, setSnackMessage] = useState(null); @@ -87,18 +93,25 @@ export const GroupScreenAddition: FC = ({ useChatStore(useSelect(["setGroupInviteLink", "deleteGroupInviteLink"])); const onAddMemberPress = useCallback(() => { - navigate("NewConversation", { addingToGroupTopic: topic }); + logger.debug("[GroupScreenAddition] Adding new member", { topic }); + navigate("InviteUsersToExistingGroup", { addingToGroupTopic: topic }); }, [topic]); const onCopyInviteLinkPress = useCallback(() => { if (!groupInviteLink) { + logger.warn("[GroupScreenAddition] No invite link to copy"); return; } + logger.debug("[GroupScreenAddition] Copying invite link"); setSnackMessage(translate("group_invite_link_copied")); Clipboard.setString(groupInviteLink); }, [groupInviteLink]); const onCreateInviteLinkPress = useCallback(() => { + logger.debug("[GroupScreenAddition] Creating invite link", { + groupName, + topic, + }); createGroupInvite(currentAccount, { groupName: groupName ?? translate("group_invite_default_group_name"), imageUrl: groupPhoto, @@ -106,6 +119,9 @@ export const GroupScreenAddition: FC = ({ groupId: getV3IdFromTopic(topic), }) .then((groupInvite) => { + logger.debug("[GroupScreenAddition] Created invite link successfully", { + inviteId: groupInvite.id, + }); saveInviteIdByGroupId(getV3IdFromTopic(topic), groupInvite.id); saveGroupInviteLink(groupInvite.id, getV3IdFromTopic(topic)); setGroupInviteLink(topic, groupInvite.inviteLink); @@ -113,6 +129,7 @@ export const GroupScreenAddition: FC = ({ setSnackMessage(translate("group_invite_link_created_copied")); }) .catch((err) => { + logger.error("[GroupScreenAddition] Failed to create invite link", err); captureErrorWithToast(err, { message: translate("group_opertation_an_error_occurred"), }); @@ -127,13 +144,18 @@ export const GroupScreenAddition: FC = ({ ]); const onDeleteInviteLink = useCallback(() => { + logger.debug("[GroupScreenAddition] Deleting invite link"); Haptics.impactAsync(); const groupId = getV3IdFromTopic(topic); const inviteId = getInviteIdByGroupId(groupId); if (!inviteId) { + logger.warn("[GroupScreenAddition] No invite ID found to delete"); return; } deleteGroupInvite(currentAccount, inviteId).then(() => { + logger.debug("[GroupScreenAddition] Deleted invite link successfully", { + inviteId, + }); setSnackMessage(translate("group_invite_link_deleted")); deleteLinkFromState(topic); deleteLinkFromStore(inviteId); @@ -142,10 +164,14 @@ export const GroupScreenAddition: FC = ({ }, [deleteLinkFromState, topic, currentAccount]); const dismissSnackBar = useCallback(() => { + logger.debug("[GroupScreenAddition] Dismissing snackbar"); setSnackMessage(null); }, [setSnackMessage]); if (!canAddMember) { + logger.debug( + "[GroupScreenAddition] User cannot add members, not rendering" + ); return null; } diff --git a/features/conversation-list/conversation-list.screen.tsx b/features/conversation-list/conversation-list.screen.tsx index 205269d8b..e09977d13 100644 --- a/features/conversation-list/conversation-list.screen.tsx +++ b/features/conversation-list/conversation-list.screen.tsx @@ -48,7 +48,7 @@ import { import { useDisconnectActionSheet } from "@hooks/useDisconnectActionSheet"; import { useNavigation } from "@react-navigation/native"; import { NativeStackScreenProps } from "@react-navigation/native-stack"; -import React, { memo, useCallback, useMemo } from "react"; +import React, { memo, useCallback, useEffect, useMemo } from "react"; import { Alert, Platform, @@ -85,6 +85,10 @@ export function ConversationListScreen({ // ]) // ); + useEffect(() => { + navigation.navigate("NewConversation", {}); + }, []); + const { data: conversations, isLoading: isLoadingConversations, @@ -502,6 +506,7 @@ const EphemeralAccountBanner = React.memo(function EphemeralAccountBanner() { const colorScheme = useColorScheme(); const showDisconnectActionSheet = useDisconnectActionSheet(); + return ( showDisconnectActionSheet(colorScheme)} diff --git a/features/conversation/conversation-composer/conversation-composer.tsx b/features/conversation/conversation-composer/conversation-composer.tsx index be69a5f42..32ff806e1 100644 --- a/features/conversation/conversation-composer/conversation-composer.tsx +++ b/features/conversation/conversation-composer/conversation-composer.tsx @@ -16,6 +16,7 @@ import { useConversationComposerStore, useConversationComposerStoreContext, } from "./conversation-composer.store-context"; +import { debugBorder } from "@/utils/debug-style"; type IComposerProps = { onSend: (args: ISendMessageParams) => Promise; @@ -113,13 +114,13 @@ export const Composer = memo(function Composer(props: IComposerProps) { return ( diff --git a/features/groups/invite-to-group/InviteUsersToExistingGroup.nav.tsx b/features/groups/invite-to-group/InviteUsersToExistingGroup.nav.tsx new file mode 100644 index 000000000..4caf62138 --- /dev/null +++ b/features/groups/invite-to-group/InviteUsersToExistingGroup.nav.tsx @@ -0,0 +1,37 @@ +import { + NativeStack, + navigationAnimation, +} from "@/screens/Navigation/Navigation"; +import { Platform } from "react-native"; +import { ConversationTopic } from "@xmtp/react-native-sdk"; +import { useColorScheme } from "react-native"; +import { InviteUsersToExistingGroupScreen } from "./invite-users-to-exisiting-group.screen"; +import { translate } from "@/i18n"; +import { + headerTitleStyle, + textPrimaryColor, + textSecondaryColor, +} from "@/styles/colors"; + +export type InviteUsersToExistingGroupParams = { + addingToGroupTopic: ConversationTopic; +}; + +export function InviteUsersToExistingGroupNav() { + const colorScheme = useColorScheme(); + return ( + ({ + headerTitle: translate("group_info"), + headerTintColor: + Platform.OS === "android" + ? textSecondaryColor(colorScheme) + : textPrimaryColor(colorScheme), + animation: navigationAnimation, + headerTitleStyle: headerTitleStyle(colorScheme), + })} + /> + ); +} diff --git a/features/groups/invite-to-group/invite-users-to-exisiting-group.screen.tsx b/features/groups/invite-to-group/invite-users-to-exisiting-group.screen.tsx new file mode 100644 index 000000000..2d2f064c3 --- /dev/null +++ b/features/groups/invite-to-group/invite-users-to-exisiting-group.screen.tsx @@ -0,0 +1,583 @@ +import { Button } from "@design-system/Button/Button"; +import { NativeStackScreenProps } from "@react-navigation/native-stack"; +import { + backgroundColor, + itemSeparatorColor, + primaryColor, + textPrimaryColor, + textSecondaryColor, +} from "@styles/colors"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { + Alert, + Platform, + ScrollView, + StyleSheet, + Text, + TextInput, + View, + useColorScheme, +} from "react-native"; + +import { translate } from "@/i18n"; +import { getCleanAddress } from "@/utils/evm/getCleanAddress"; +import { useGroupQuery } from "@queries/useGroupQuery"; +import SearchBar from "@search/components/SearchBar"; +import { canMessageByAccount } from "@utils/xmtpRN/contacts"; +import { InboxId } from "@xmtp/react-native-sdk"; +import { IProfileSocials } from "@/features/profiles/profile-types"; +import { setProfileRecordSocialsQueryData } from "@/queries/useProfileSocialsQuery"; +import { + currentAccount, + useRecommendationsStore, +} from "@/data/store/accountsStore"; +import { NavigationParamList } from "@/screens/Navigation/Navigation"; +import { useGroupMembers } from "@/hooks/useGroupMembers"; +import AndroidBackAction from "@/components/AndroidBackAction"; +import { ActivityIndicator } from "react-native-paper"; +import { useSelect } from "@/data/store/storeHelpers"; +import { getAddressForPeer, isSupportedPeer } from "@/utils/evm/address"; +import config from "@/config"; +import { searchProfiles } from "@/utils/api"; +import { isEmptyObject } from "@/utils/objects"; +import { getPreferredName } from "@/utils/profile"; +import { ProfileSearchResultsList } from "@/features/search/components/ProfileSearchResultsList"; +import Recommendations from "@/components/Recommendations/Recommendations"; +import TableView from "@/components/TableView/TableView"; +import { TableViewPicto } from "@/components/TableView/TableViewImage"; + +/** + * @deprecated + * We are redoing our Create new chat flow, and this screen was shared between + * that and the add members to existing group flow. + * + * This screen will need some design work, but is outside of scope of the + * current work. + * + * @see https://github.com/ephemeraHQ/converse-app/issues/1498 + * @see https://www.figma.com/design/p6mt4tEDltI4mypD3TIgUk/Converse-App?node-id=5026-26989&m=dev + */ +export function InviteUsersToExistingGroupScreen({ + route, + navigation, +}: NativeStackScreenProps) { + const colorScheme = useColorScheme(); + + const { data: existingGroup } = useGroupQuery({ + account: currentAccount(), + topic: route.params?.addingToGroupTopic!, + }); + const [group, setGroup] = useState({ + enabled: !!route.params?.addingToGroupTopic, + members: [] as (IProfileSocials & { address: string })[], + }); + + const { addMembers, members } = useGroupMembers( + route.params?.addingToGroupTopic! + ); + + const [loading, setLoading] = useState(false); + + const handleBack = useCallback(() => navigation.goBack(), [navigation]); + + const styles = useStyles(); + + const handleRightAction = useCallback(async () => { + // if (route.params?.addingToGroupTopic) { + setLoading(true); + try { + // TODO: Support multiple addresses + await addMembers(group.members.map((m) => m.address)); + navigation.goBack(); + } catch (e) { + setLoading(false); + console.error(e); + Alert.alert("An error occured"); + } + // } else { + // navigation.push("NewGroupSummary", { + // members: group.members, + // }); + // } + }, [addMembers, group.members, navigation, route.params?.addingToGroupTopic]); + + useEffect(() => { + navigation.setOptions({ + headerLeft: () => + Platform.OS === "ios" ? ( +