Skip to content

Commit

Permalink
Merge pull request #27966 from Expensify/jasper-uiForRoomInvitations/…
Browse files Browse the repository at this point in the history
…Removals

UI for room invitations/removals
  • Loading branch information
jasperhuangg authored Oct 19, 2023
2 parents c23adc0 + 5015b1a commit 409da51
Show file tree
Hide file tree
Showing 20 changed files with 904 additions and 33 deletions.
8 changes: 8 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,8 @@ const CONST = {
DELETE_TAG: 'POLICYCHANGELOG_DELETE_TAG',
IMPORT_CUSTOM_UNIT_RATES: 'POLICYCHANGELOG_IMPORT_CUSTOM_UNIT_RATES',
IMPORT_TAGS: 'POLICYCHANGELOG_IMPORT_TAGS',
INVITE_TO_ROOM: 'POLICYCHANGELOG_INVITETOROOM',
REMOVE_FROM_ROOM: 'POLICYCHANGELOG_REMOVEFROMROOM',
SET_AUTOREIMBURSEMENT: 'POLICYCHANGELOG_SET_AUTOREIMBURSEMENT',
SET_AUTO_JOIN: 'POLICYCHANGELOG_SET_AUTO_JOIN',
SET_CATEGORY_NAME: 'POLICYCHANGELOG_SET_CATEGORY_NAME',
Expand Down Expand Up @@ -551,6 +553,11 @@ const CONST = {
UPDATE_TIME_ENABLED: 'POLICYCHANGELOG_UPDATE_TIME_ENABLED',
UPDATE_TIME_RATE: 'POLICYCHANGELOG_UPDATE_TIME_RATE',
},
ROOMCHANGELOG: {
INVITE_TO_ROOM: 'INVITETOROOM',
REMOVE_FROM_ROOM: 'REMOVEFROMROOM',
JOIN_ROOM: 'JOINROOM',
},
},
},
ARCHIVE_REASON: {
Expand Down Expand Up @@ -1424,6 +1431,7 @@ const CONST = {
REPORT_DETAILS_MENU_ITEM: {
SHARE_CODE: 'shareCode',
MEMBERS: 'member',
INVITE: 'invite',
SETTINGS: 'settings',
LEAVE_ROOM: 'leaveRoom',
WELCOME_MESSAGE: 'welcomeMessage',
Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ export default {
route: 'r/:reportID/notes/:accountID/edit',
getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}/edit`,
},
ROOM_MEMBERS: {
route: 'r/:reportID/members',
getRoute: (reportID: string) => `r/${reportID}/members`,
},
ROOM_INVITE: {
route: 'r/:reportID/invite',
getRoute: (reportID: string) => `r/${reportID}/invite`,
},

// To see the available iouType, please refer to CONST.IOU.TYPE
MONEY_REQUEST: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/FormAlertWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function FormAlertWrapper(props) {
</Text>
);
} else if (props.isMessageHtml) {
children = <RenderHTML html={`<muted-text>${props.message}</muted-text>`} />;
children = <RenderHTML html={`<alert-text>${props.message}</alert-text>`} />;
}
return (
<View style={props.containerStyles}>
Expand Down
6 changes: 5 additions & 1 deletion src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ const customHTMLElementModels = {
edited: defaultHTMLElementModels.span.extend({
tagName: 'edited',
}),
'alert-text': defaultHTMLElementModels.div.extend({
tagName: 'alert-text',
mixedUAStyles: {...styles.formError, ...styles.mb0},
}),
'muted-text': defaultHTMLElementModels.div.extend({
tagName: 'muted-text',
mixedUAStyles: {...styles.formError, ...styles.mb0},
mixedUAStyles: {...styles.colorMuted, ...styles.mb0},
}),
comment: defaultHTMLElementModels.div.extend({
tagName: 'comment',
Expand Down
11 changes: 8 additions & 3 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import type {
ConfirmThatParams,
UntilTimeParams,
StepCounterParams,
UserIsAlreadyMemberOfWorkspaceParams,
UserIsAlreadyMemberParams,
GoToRoomParams,
WelcomeNoteParams,
RoomNameReservedErrorParams,
Expand Down Expand Up @@ -1200,7 +1200,7 @@ export default {
messages: {
errorMessageInvalidPhone: `Please enter a valid phone number without brackets or dashes. If you're outside the US please include your country code (e.g. ${CONST.EXAMPLE_PHONE_NUMBER}).`,
errorMessageInvalidEmail: 'Invalid email',
userIsAlreadyMemberOfWorkspace: ({login, workspace}: UserIsAlreadyMemberOfWorkspaceParams) => `${login} is already a member of ${workspace}`,
userIsAlreadyMember: ({login, name}: UserIsAlreadyMemberParams) => `${login} is already a member of ${name}`,
},
onfidoStep: {
acceptTerms: 'By continuing with the request to activate your Expensify wallet, you confirm that you have read, understand and accept ',
Expand Down Expand Up @@ -1590,13 +1590,18 @@ export default {
selectAWorkspace: 'Select a workspace',
growlMessageOnRenameError: 'Unable to rename policy room, please check your connection and try again.',
visibilityOptions: {
restricted: 'Restricted',
restricted: 'Workspace', // the translation for "restricted" visibility is actually workspace. This is so we can display restricted visibility rooms as "workspace" without having to change what's stored.
private: 'Private',
public: 'Public',
// eslint-disable-next-line @typescript-eslint/naming-convention
public_announce: 'Public Announce',
},
},
roomMembersPage: {
memberNotFound: 'Member not found. To invite a new member to the room, please use the Invite button above.',
notAuthorized: `You do not have access to this page. Are you trying to join the room? Please reach out to a member of this room so they can add you as a member! Something else? Reach out to ${CONST.EMAIL.CONCIERGE}`,
removeMembersPrompt: 'Are you sure you want to remove the selected members from the room?',
},
newTaskPage: {
assignTask: 'Assign task',
assignMe: 'Assign to me',
Expand Down
11 changes: 8 additions & 3 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import type {
ConfirmThatParams,
UntilTimeParams,
StepCounterParams,
UserIsAlreadyMemberOfWorkspaceParams,
UserIsAlreadyMemberParams,
GoToRoomParams,
WelcomeNoteParams,
RoomNameReservedErrorParams,
Expand Down Expand Up @@ -1218,7 +1218,7 @@ export default {
messages: {
errorMessageInvalidPhone: `Por favor, introduce un número de teléfono válido sin paréntesis o guiones. Si reside fuera de Estados Unidos, por favor incluye el prefijo internacional (p. ej. ${CONST.EXAMPLE_PHONE_NUMBER}).`,
errorMessageInvalidEmail: 'Email inválido',
userIsAlreadyMemberOfWorkspace: ({login, workspace}: UserIsAlreadyMemberOfWorkspaceParams) => `${login} ya es miembro de ${workspace}`,
userIsAlreadyMember: ({login, name}: UserIsAlreadyMemberParams) => `${login} ya es miembro de ${name}`,
},
onfidoStep: {
acceptTerms: 'Al continuar con la solicitud para activar su billetera Expensify, confirma que ha leído, comprende y acepta ',
Expand Down Expand Up @@ -1614,13 +1614,18 @@ export default {
selectAWorkspace: 'Seleccionar un espacio de trabajo',
growlMessageOnRenameError: 'No se ha podido cambiar el nombre del espacio de trabajo, por favor, comprueba tu conexión e inténtalo de nuevo.',
visibilityOptions: {
restricted: 'Restringida',
restricted: 'Espacio de trabajo', // the translation for "restricted" visibility is actually workspace. This is so we can display restricted visibility rooms as "workspace" without having to change what's stored.
private: 'Privada',
public: 'Público',
// eslint-disable-next-line @typescript-eslint/naming-convention
public_announce: 'Anuncio Público',
},
},
roomMembersPage: {
memberNotFound: 'Miembro no encontrado. Para invitar a un nuevo miembro a la sala de chat, por favor, utiliza el botón Invitar que está más arriba.',
notAuthorized: `No tienes acceso a esta página. ¿Estás tratando de unirte a la sala de chat? Comunícate con el propietario de esta sala de chat para que pueda añadirte como miembro. ¿Necesitas algo más? Comunícate con ${CONST.EMAIL.CONCIERGE}`,
removeMembersPrompt: '¿Estás seguro de que quieres eliminar a los miembros seleccionados de la sala de chat?',
},
newTaskPage: {
assignTask: 'Asignar tarea',
assignMe: 'Asignar a mí mismo',
Expand Down
4 changes: 2 additions & 2 deletions src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ type UntilTimeParams = {time: string};

type StepCounterParams = {step: number; total?: number; text?: string};

type UserIsAlreadyMemberOfWorkspaceParams = {login: string; workspace: string};
type UserIsAlreadyMemberParams = {login: string; name: string};

type GoToRoomParams = {roomName: string};

Expand Down Expand Up @@ -303,7 +303,7 @@ export type {
ConfirmThatParams,
UntilTimeParams,
StepCounterParams,
UserIsAlreadyMemberOfWorkspaceParams,
UserIsAlreadyMemberParams,
GoToRoomParams,
WelcomeNoteParams,
RoomNameReservedErrorParams,
Expand Down
10 changes: 10 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ const ReportParticipantsModalStackNavigator = createModalStackNavigator({
ReportParticipants_Root: () => require('../../../pages/ReportParticipantsPage').default,
});

const RoomMembersModalStackNavigator = createModalStackNavigator({
RoomMembers_Root: () => require('../../../pages/RoomMembersPage').default,
});

const RoomInviteModalStackNavigator = createModalStackNavigator({
RoomInvite_Root: () => require('../../../pages/RoomInvitePage').default,
});

const SearchModalStackNavigator = createModalStackNavigator({
Search_Root: () => require('../../../pages/SearchPage').default,
});
Expand Down Expand Up @@ -231,4 +239,6 @@ export {
PrivateNotesModalStackNavigator,
NewTeachersUniteNavigator,
SignInModalStackNavigator,
RoomMembersModalStackNavigator,
RoomInviteModalStackNavigator,
};
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ function RightModalNavigator(props) {
name="Participants"
component={ModalStackNavigators.ReportParticipantsModalStackNavigator}
/>
<Stack.Screen
name="RoomMembers"
component={ModalStackNavigators.RoomMembersModalStackNavigator}
/>
<Stack.Screen
name="RoomInvite"
component={ModalStackNavigators.RoomInviteModalStackNavigator}
/>
<Stack.Screen
name="MoneyRequest"
component={ModalStackNavigators.MoneyRequestModalStackNavigator}
Expand Down
10 changes: 10 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,16 @@ export default {
ReportParticipants_Root: ROUTES.REPORT_PARTICIPANTS.route,
},
},
RoomInvite: {
screens: {
RoomInvite_Root: ROUTES.ROOM_INVITE.route,
},
},
RoomMembers: {
screens: {
RoomMembers_Root: ROUTES.ROOM_MEMBERS.route,
},
},
MoneyRequest: {
screens: {
Money_Request: {
Expand Down
9 changes: 9 additions & 0 deletions src/libs/PolicyUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ function isExpensifyGuideTeam(email) {
*/
const isPolicyAdmin = (policy) => lodashGet(policy, 'role') === CONST.POLICY.ROLE.ADMIN;

/**
*
* @param {String} policyID
* @param {Object} policies
* @returns {Boolean}
*/
const isPolicyMember = (policyID, policies) => _.some(policies, (policy) => policy.id === policyID);

/**
* @param {Object} policyMembers
* @param {Object} personalDetails
Expand Down Expand Up @@ -276,6 +284,7 @@ export {
isPolicyAdmin,
getMemberAccountIDsForWorkspace,
getIneligibleInvitees,
isPolicyMember,
getTag,
getTagListName,
getTagList,
Expand Down
40 changes: 37 additions & 3 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {isEqual, max, parseISO} from 'date-fns';
import _ from 'lodash';
import lodashFindLast from 'lodash/findLast';
import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import {ValueOf} from 'type-fest';
Expand All @@ -10,6 +11,7 @@ import {ActionName} from '../types/onyx/OriginalMessage';
import * as CollectionUtils from './CollectionUtils';
import Log from './Log';
import isReportMessageAttachment from './isReportMessageAttachment';
import * as Environment from './Environment/Environment';

type LastVisibleMessage = {
lastMessageTranslationKey?: string;
Expand Down Expand Up @@ -49,6 +51,9 @@ Onyx.connect({
callback: (val) => (isNetworkOffline = val?.isOffline ?? false),
});

let environmentURL: string;
Environment.getEnvironmentURL().then((url: string) => (environmentURL = url));

function isCreatedAction(reportAction: OnyxEntry<ReportAction>): boolean {
return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED;
}
Expand Down Expand Up @@ -288,8 +293,8 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry<ReportAction>, key:
return false;
}

const {POLICYCHANGELOG: policyChangelogTypes, ...otherActionTypes} = CONST.REPORT.ACTIONS.TYPE;
const supportedActionTypes: ActionName[] = [...Object.values(otherActionTypes), ...Object.values(policyChangelogTypes)];
const {POLICYCHANGELOG: policyChangelogTypes, ROOMCHANGELOG: roomChangeLogTypes, ...otherActionTypes} = CONST.REPORT.ACTIONS.TYPE;
const supportedActionTypes: ActionName[] = [...Object.values(otherActionTypes), ...Object.values(policyChangelogTypes), ...Object.values(roomChangeLogTypes)];

// Filter out any unsupported reportAction types
if (!supportedActionTypes.includes(reportAction.actionName)) {
Expand Down Expand Up @@ -333,6 +338,34 @@ function shouldReportActionBeVisibleAsLastAction(reportAction: OnyxEntry<ReportA
);
}

/**
* For invite to room and remove from room policy change logs, report URLs are generated in the server,
* which includes a baseURL placeholder that's replaced in the client.
*/
function replaceBaseURL(reportAction: ReportAction): ReportAction {
if (!reportAction) {
return reportAction;
}

if (
!reportAction ||
(reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM && reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.REMOVE_FROM_ROOM)
) {
return reportAction;
}
if (!reportAction.message) {
return reportAction;
}
const updatedReportAction = _.clone(reportAction);
if (!updatedReportAction.message) {
return updatedReportAction;
}
updatedReportAction.message[0].html = reportAction.message[0].html.replace('%baseURL', environmentURL);
return updatedReportAction;
}

/**
*/
function getLastVisibleAction(reportID: string, actionsToMerge: ReportActions = {}): OnyxEntry<ReportAction> {
const updatedActionsToMerge: ReportActions = {};
if (actionsToMerge && Object.keys(actionsToMerge).length !== 0) {
Expand Down Expand Up @@ -397,7 +430,8 @@ function getSortedReportActionsForDisplay(reportActions: ReportActions | null):
const filteredReportActions = Object.entries(reportActions ?? {})
.filter(([key, reportAction]) => shouldReportActionBeVisible(reportAction, key))
.map((entry) => entry[1]);
return getSortedReportActions(filteredReportActions, true);
const baseURLAdjustedReportActions = filteredReportActions.map((reportAction) => replaceBaseURL(reportAction));
return getSortedReportActions(baseURLAdjustedReportActions, true);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3156,7 +3156,7 @@ function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas,
(report.participantAccountIDs &&
report.participantAccountIDs.length === 0 &&
!isChatThread(report) &&
!isPublicRoom(report) &&
!isUserCreatedPolicyRoom(report) &&
!isArchivedRoom(report) &&
!isMoneyRequestReport(report) &&
!isTaskReport(report))
Expand Down
28 changes: 28 additions & 0 deletions src/libs/SidebarUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,34 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
result.alternateText = `${Localize.translate(preferredLocale, 'task.messages.reopened')}`;
} else if (lastAction && lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED) {
result.alternateText = `${Localize.translate(preferredLocale, 'task.messages.completed')}`;
} else if (
lastAction &&
_.includes(
[
CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM,
CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM,
CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM,
CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.REMOVE_FROM_ROOM,
],
lastAction.actionName,
)
) {
const targetAccountIDs = lodashGet(lastAction, 'originalMessage.targetAccountIDs', []);
const verb =
lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM
? 'invited'
: 'removed';
const users = targetAccountIDs.length > 1 ? 'users' : 'user';
result.alternateText = `${verb} ${targetAccountIDs.length} ${users}`;

const roomName = lodashGet(lastAction, 'originalMessage.roomName', '');
if (roomName) {
const preposition =
lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM
? ' to'
: ' from';
result.alternateText += `${preposition} ${roomName}`;
}
} else if (lastAction && lastAction.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lastActorDisplayName && lastMessageTextFromReport) {
result.alternateText = `${lastActorDisplayName}: ${lastMessageText}`;
} else {
Expand Down
Loading

0 comments on commit 409da51

Please sign in to comment.