Skip to content

Commit

Permalink
members, avatar, rename report, settings, default view for other reports
Browse files Browse the repository at this point in the history
  • Loading branch information
waterim committed Apr 4, 2024
1 parent a1801c8 commit 9dba74f
Show file tree
Hide file tree
Showing 22 changed files with 1,131 additions and 112 deletions.
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ const ONYXKEYS = {
WORKSPACE_TAX_NAME_FORM_DRAFT: 'workspaceTaxNameFormDraft',
WORKSPACE_TAX_VALUE_FORM: 'workspaceTaxValueForm',
WORKSPACE_TAX_VALUE_FORM_DRAFT: 'workspaceTaxValueFormDraft',
NEW_CHAT_NAME_FORM: 'newChatNameForm',
NEW_CHAT_NAME_FORM_DRAFT: 'newChatNameFormDraft',
},
} as const;

Expand Down Expand Up @@ -500,6 +502,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_EDIT_FORM]: FormTypes.PolicyDistanceRateEditForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_NAME_FORM]: FormTypes.WorkspaceTaxNameForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_VALUE_FORM]: FormTypes.WorkspaceTaxValueForm;
[ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM]: FormTypes.NewChatNameForm;
};

type OnyxFormDraftValuesMapping = {
Expand Down
16 changes: 16 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ const ROUTES = {
NEW: 'new',
NEW_CHAT: 'new/chat',
NEW_CHAT_CONFIRM: 'new/chat/confirm',
NEW_CHAT_EDIT_NAME: {
route: 'new/chat/confirm/:chatName/edit',
getRoute: (chatName: string) => `new/chat/confirm/${encodeURIComponent(chatName)}/edit` as const,
},
NEW_ROOM: 'new/room',

REPORT: 'r',
Expand Down Expand Up @@ -211,6 +215,18 @@ const ROUTES = {
route: 'r/:reportID/participants',
getRoute: (reportID: string) => `r/${reportID}/participants` as const,
},
REPORT_PARTICIPANTS_INVITE: {
route: 'r/:reportID/participants/invite',
getRoute: (reportID: string) => `r/${reportID}/participants/invite` as const,
},
REPORT_PARTICIPANTS_DETAILS: {
route: 'r/:reportID/participants/:accountID',
getRoute: (reportID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants/${accountID}`, backTo),
},
REPORT_PARTICIPANTS_ROLE_SELECTION: {
route: 'r/:reportID/participants/:accountID/role',
getRoute: (reportID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants/${accountID}/role`, backTo),
},
REPORT_WITH_ID_DETAILS: {
route: 'r/:reportID/details',
getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/details`, backTo),
Expand Down
8 changes: 7 additions & 1 deletion src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ const SCREENS = {
ROOT: 'NewChat_Root',
NEW_CHAT: 'chat',
NEW_CHAT_CONFIRM: 'NewChat_Confirm',
NEW_CHAT_EDIT_NAME: 'NewChat_Edit_Name',
NEW_ROOM: 'room',
},

Expand Down Expand Up @@ -287,7 +288,12 @@ const SCREENS = {
PROFILE_ROOT: 'Profile_Root',
PROCESS_MONEY_REQUEST_HOLD_ROOT: 'ProcessMoneyRequestHold_Root',
REPORT_DESCRIPTION_ROOT: 'Report_Description_Root',
REPORT_PARTICIPANTS_ROOT: 'ReportParticipants_Root',
REPORT_PARTICIPANTS: {
ROOT: 'ReportParticipants_Root',
INVITE: 'ReportParticipants_Invite',
DETAILS: 'ReportParticipants_Details',
ROLE: 'ReportParticipants_Role',
},
ROOM_MEMBERS_ROOT: 'RoomMembers_Root',
ROOM_INVITE_ROOT: 'RoomInvite_Root',
SEARCH_ROOT: 'Search_Root',
Expand Down
8 changes: 8 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,14 @@ export default {
},
groupConfirmPage: {
groupName: 'Group name',
editName: {
nameRequiredError: 'Group chat name is required.',
},
},
groupPage: {
people: {
groupMembersListTitle: 'Directory of all group members.',
},
},
languagePage: {
language: 'Language',
Expand Down
6 changes: 6 additions & 0 deletions src/libs/API/parameters/InviteMembersToGroupChat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type InviteMembersToGroupChat = {
reportID: string;
inviteeEmails: string[];
};

export default InviteMembersToGroupChat;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export type {default as AddEmojiReactionParams} from './AddEmojiReactionParams';
export type {default as RemoveEmojiReactionParams} from './RemoveEmojiReactionParams';
export type {default as LeaveRoomParams} from './LeaveRoomParams';
export type {default as InviteToRoomParams} from './InviteToRoomParams';
export type {default as InviteMembersToGroupChat} from './InviteMembersToGroupChat';
export type {default as RemoveFromRoomParams} from './RemoveFromRoomParams';
export type {default as FlagCommentParams} from './FlagCommentParams';
export type {default as UpdateReportPrivateNoteParams} from './UpdateReportPrivateNoteParams';
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const WRITE_COMMANDS = {
REMOVE_EMOJI_REACTION: 'RemoveEmojiReaction',
LEAVE_ROOM: 'LeaveRoom',
INVITE_TO_ROOM: 'InviteToRoom',
INVITE_MEMBERS_TO_GROUP_CHAT: 'InviteMembersToGroupChat',
REMOVE_FROM_ROOM: 'RemoveFromRoom',
FLAG_COMMENT: 'FlagComment',
UPDATE_REPORT_PRIVATE_NOTE: 'UpdateReportPrivateNote',
Expand Down Expand Up @@ -286,6 +287,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.REMOVE_EMOJI_REACTION]: Parameters.RemoveEmojiReactionParams;
[WRITE_COMMANDS.LEAVE_ROOM]: Parameters.LeaveRoomParams;
[WRITE_COMMANDS.INVITE_TO_ROOM]: Parameters.InviteToRoomParams;
[WRITE_COMMANDS.INVITE_MEMBERS_TO_GROUP_CHAT]: Parameters.InviteMembersToGroupChat;
[WRITE_COMMANDS.REMOVE_FROM_ROOM]: Parameters.RemoveFromRoomParams;
[WRITE_COMMANDS.FLAG_COMMENT]: Parameters.FlagCommentParams;
[WRITE_COMMANDS.UPDATE_REPORT_PRIVATE_NOTE]: Parameters.UpdateReportPrivateNoteParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,10 @@ const ReportDescriptionModalStackNavigator = createModalStackNavigator<ReportDes
});

const ReportParticipantsModalStackNavigator = createModalStackNavigator<ParticipantsNavigatorParamList>({
[SCREENS.REPORT_PARTICIPANTS_ROOT]: () => require('../../../../pages/ReportParticipantsPage').default as React.ComponentType,
[SCREENS.REPORT_PARTICIPANTS.ROOT]: () => require('../../../../pages/ReportParticipantsPage').default as React.ComponentType,
[SCREENS.REPORT_PARTICIPANTS.INVITE]: () => require('../../../../pages/InviteReportParticipantsPage').default as React.ComponentType,
[SCREENS.REPORT_PARTICIPANTS.DETAILS]: () => require('../../../../pages/ReportParticipantDetailsPage').default as React.ComponentType,
[SCREENS.REPORT_PARTICIPANTS.ROLE]: () => require('../../../../pages/ReportParticipantDetailsRoleSelectionPage').default as React.ComponentType,
});

const RoomMembersModalStackNavigator = createModalStackNavigator<RoomMembersNavigatorParamList>({
Expand All @@ -151,6 +154,7 @@ const SearchModalStackNavigator = createModalStackNavigator<SearchNavigatorParam
const NewChatModalStackNavigator = createModalStackNavigator<NewChatNavigatorParamList>({
[SCREENS.NEW_CHAT.ROOT]: () => require('../../../../pages/NewChatSelectorPage').default as React.ComponentType,
[SCREENS.NEW_CHAT.NEW_CHAT_CONFIRM]: () => require('../../../../pages/NewChatConfirmPage').default as React.ComponentType,
[SCREENS.NEW_CHAT.NEW_CHAT_EDIT_NAME]: () => require('../../../../pages/GroupChatNameEditPage').default as React.ComponentType,
});

const NewTaskModalStackNavigator = createModalStackNavigator<NewTaskNavigatorParamList>({
Expand Down
9 changes: 8 additions & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,10 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.NEW_CHAT_CONFIRM,
exact: true,
},
[SCREENS.NEW_CHAT.NEW_CHAT_EDIT_NAME]: {
path: ROUTES.NEW_CHAT_EDIT_NAME.route,
exact: true,
},
},
},
[SCREENS.RIGHT_MODAL.NEW_TASK]: {
Expand Down Expand Up @@ -475,7 +479,10 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
},
[SCREENS.RIGHT_MODAL.PARTICIPANTS]: {
screens: {
[SCREENS.REPORT_PARTICIPANTS_ROOT]: ROUTES.REPORT_PARTICIPANTS.route,
[SCREENS.REPORT_PARTICIPANTS.ROOT]: ROUTES.REPORT_PARTICIPANTS.route,
[SCREENS.REPORT_PARTICIPANTS.INVITE]: ROUTES.REPORT_PARTICIPANTS_INVITE.route,
[SCREENS.REPORT_PARTICIPANTS.DETAILS]: ROUTES.REPORT_PARTICIPANTS_DETAILS.route,
[SCREENS.REPORT_PARTICIPANTS.ROLE]: ROUTES.REPORT_PARTICIPANTS_ROLE_SELECTION.route,
},
},
[SCREENS.RIGHT_MODAL.ROOM_INVITE]: {
Expand Down
14 changes: 13 additions & 1 deletion src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ type SettingsNavigatorParamList = {

type NewChatNavigatorParamList = {
[SCREENS.NEW_CHAT.ROOT]: undefined;
[SCREENS.NEW_CHAT.NEW_CHAT_EDIT_NAME]: {chatName: string};
};

type SearchNavigatorParamList = {
Expand Down Expand Up @@ -322,7 +323,18 @@ type ReportDescriptionNavigatorParamList = {
};

type ParticipantsNavigatorParamList = {
[SCREENS.REPORT_PARTICIPANTS_ROOT]: {reportID: string};
[SCREENS.REPORT_PARTICIPANTS.ROOT]: {reportID: string};
[SCREENS.REPORT_PARTICIPANTS.INVITE]: {reportID: string};
[SCREENS.REPORT_PARTICIPANTS.DETAILS]: {
reportID: string;
accountID: string;
backTo: Routes;
};
[SCREENS.REPORT_PARTICIPANTS.ROLE]: {
reportID: string;
accountID: string;
backTo: Routes;
};
};

type RoomMembersNavigatorParamList = {
Expand Down
4 changes: 4 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5006,6 +5006,10 @@ function shouldDisableRename(report: OnyxEntry<Report>, policy: OnyxEntry<Policy
return true;
}

if (isGroupChat(report)) {
return false;
}

// if the linked workspace is null, that means the person isn't a member of the workspace the report is in
// which means this has to be a public room we want to disable renaming for
if (!policy) {
Expand Down
84 changes: 82 additions & 2 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
GetNewerActionsParams,
GetOlderActionsParams,
GetReportPrivateNoteParams,
InviteMembersToGroupChatParams,

Check failure on line 24 in src/libs/actions/Report.ts

View workflow job for this annotation

GitHub Actions / typecheck

'"@libs/API/parameters"' has no exported member named 'InviteMembersToGroupChatParams'. Did you mean 'InviteMembersToGroupChat'?
InviteToRoomParams,
LeaveRoomParams,
MarkAsUnreadParams,
Expand Down Expand Up @@ -81,7 +82,7 @@ import type {
ReportUserIsTyping,
} from '@src/types/onyx';
import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage';
import type {NotificationPreference, RoomVisibility, WriteCapability} from '@src/types/onyx/Report';
import type {NotificationPreference, Participants, Participant as ReportParticipant, RoomVisibility, WriteCapability} from '@src/types/onyx/Report';
import type Report from '@src/types/onyx/Report';
import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction';
import type ReportAction from '@src/types/onyx/ReportAction';
Expand Down Expand Up @@ -2524,6 +2525,84 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record<string
API.write(WRITE_COMMANDS.INVITE_TO_ROOM, parameters, {optimisticData, successData, failureData});
}

/** Invites people to a group chat */
function inviteMembersToGroupChat(reportID: string, inviteeEmailsToAccountIDs: Record<string, number>) {
const report = currentReportData?.[reportID];

if (!report) {
return;
}

const inviteeEmails = Object.keys(inviteeEmailsToAccountIDs);
const inviteeAccountIDs = Object.values(inviteeEmailsToAccountIDs);
const participantAccountIDsAfterInvitation = [...new Set([...(report?.participantAccountIDs ?? []), ...inviteeAccountIDs])].filter(
(accountID): accountID is number => typeof accountID === 'number',
);
const visibleMemberAccountIDsAfterInvitation = [...new Set([...(report?.visibleChatMemberAccountIDs ?? []), ...inviteeAccountIDs])].filter(
(accountID): accountID is number => typeof accountID === 'number',
);

const participantsAfterInvitation = [...new Set([...(report?.participantAccountIDs ?? []), ...inviteeAccountIDs])].reduce((reportParticipants: Participants, accountID: number) => {
const participant: ReportParticipant = {
hidden: false,
role: accountID === currentUserAccountID ? CONST.REPORT.ROLE.ADMIN : CONST.REPORT.ROLE.MEMBER,
};
// eslint-disable-next-line no-param-reassign
reportParticipants[accountID] = participant;
return reportParticipants;
}, {} as Participants);

const logins = inviteeEmails.map((memberLogin) => PhoneNumber.addSMSDomainIfPhoneNumber(memberLogin));
const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, inviteeAccountIDs);
const pendingChatMembers = ReportUtils.getPendingChatMembers(inviteeAccountIDs, report?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD);

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
participantAccountIDs: participantAccountIDsAfterInvitation,
visibleChatMemberAccountIDs: visibleMemberAccountIDsAfterInvitation,
participants: participantsAfterInvitation,
pendingChatMembers,
},
},
...newPersonalDetailsOnyxData.optimisticData,
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
pendingChatMembers: report?.pendingChatMembers ?? null,
},
},
...newPersonalDetailsOnyxData.finallyData,
];
const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
participantAccountIDs: report.participantAccountIDs,
visibleChatMemberAccountIDs: report.visibleChatMemberAccountIDs,
participants: report.participants,
pendingChatMembers: report?.pendingChatMembers ?? null,
},
},
...newPersonalDetailsOnyxData.finallyData,
];

const parameters: InviteMembersToGroupChatParams = {
reportID,
inviteeEmails,
};

// Looks like a wrong API command
API.write(WRITE_COMMANDS.INVITE_MEMBERS_TO_GROUP_CHAT, parameters, {optimisticData, successData, failureData});
}

/** Removes people from a room
* Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details
*/
Expand Down Expand Up @@ -3022,7 +3101,7 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt
API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData});
}

function setGroupDraft(participants: Array<{login: string; accountID: number}>, reportName = '') {
function setGroupDraft(participants?: Array<{login: string; accountID: number}>, reportName = '') {
Onyx.merge(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {participants, reportName});
}

Expand Down Expand Up @@ -3075,6 +3154,7 @@ export {
shouldShowReportActionNotification,
leaveRoom,
inviteToRoom,
inviteMembersToGroupChat,
removeFromRoom,
getCurrentUserAccountID,
setLastOpenedPublicRoom,
Expand Down
81 changes: 81 additions & 0 deletions src/pages/GroupChatNameEditPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback} from 'react';
import {Keyboard} from 'react-native';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
import type {NewChatNavigatorParamList} from '@navigation/types';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/NewChatNameForm';

type GroupChatNameEditPageProps = StackScreenProps<NewChatNavigatorParamList, typeof SCREENS.NEW_CHAT.NEW_CHAT_EDIT_NAME>;

function GroupChatNameEditPage({route}: GroupChatNameEditPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {inputCallbackRef} = useAutoFocusInput();
const currentChatName = decodeURIComponent(route.params.chatName);
const validate = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM>) => {
const errors: FormInputErrors<typeof ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM> = {};
const chatName = values.newChatName.trim();
if (!ValidationUtils.isRequiredFulfilled(chatName)) {
errors.newChatName = 'groupConfirmPage.editName.nameRequiredError';
}
return errors;
}, []);

const editName = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM>) => {
Report.setGroupDraft(undefined, values[INPUT_IDS.NEW_CHAT_NAME]);
Keyboard.dismiss();
Navigation.goBack(ROUTES.NEW_CHAT_CONFIRM);
}, []);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
style={[styles.defaultModalContainer]}
testID={GroupChatNameEditPage.displayName}
shouldEnableMaxHeight
>
<HeaderWithBackButton
title={translate('groupConfirmPage.groupName')}
onBackButtonPress={Navigation.goBack}
/>
<FormProvider
formID={ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM}
onSubmit={editName}
submitButtonText={translate('common.save')}
validate={validate}
style={[styles.mh5, styles.flex1]}
enabledWhenOffline
>
<InputWrapper
InputComponent={TextInput}
maxLength={CONST.TAG_NAME_LIMIT}
defaultValue={currentChatName}
label={translate('common.name')}
accessibilityLabel={translate('common.name')}
inputID={INPUT_IDS.NEW_CHAT_NAME}
role={CONST.ROLE.PRESENTATION}
ref={inputCallbackRef}
/>
</FormProvider>
</ScreenWrapper>
);
}

GroupChatNameEditPage.displayName = 'GroupChatNameEditPage';

export default GroupChatNameEditPage;
Loading

0 comments on commit 9dba74f

Please sign in to comment.