Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workspace description #36058

Merged
merged 25 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ const CONST = {
UPDATE_MAX_EXPENSE_AMOUNT: 'POLICYCHANGELOG_UPDATE_MAX_EXPENSE_AMOUNT',
UPDATE_MAX_EXPENSE_AMOUNT_NO_RECEIPT: 'POLICYCHANGELOG_UPDATE_MAX_EXPENSE_AMOUNT_NO_RECEIPT',
UPDATE_NAME: 'POLICYCHANGELOG_UPDATE_NAME',
UPDATE_DESCRIPTION: 'POLICYCHANGELOG_UPDATE_DESCRIPTION',
UPDATE_OWNERSHIP: 'POLICYCHANGELOG_UPDATE_OWNERSHIP',
UPDATE_REIMBURSEMENT_CHOICE: 'POLICYCHANGELOG_UPDATE_REIMBURSEMENT_CHOICE',
UPDATE_REPORT_FIELD: 'POLICYCHANGELOG_UPDATE_REPORT_FIELD',
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ const ONYXKEYS = {
ADD_DEBIT_CARD_FORM_DRAFT: 'addDebitCardFormDraft',
WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm',
WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft',
WORKSPACE_DESCRIPTION_FORM: 'workspaceDescriptionForm',
WORKSPACE_DESCRIPTION_FORM_DRAFT: 'workspaceDescriptionFormDraft',
WORKSPACE_RATE_AND_UNIT_FORM: 'workspaceRateAndUnitForm',
WORKSPACE_RATE_AND_UNIT_FORM_DRAFT: 'workspaceRateAndUnitFormDraft',
CLOSE_ACCOUNT_FORM: 'closeAccount',
Expand Down Expand Up @@ -415,6 +417,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM]: FormTypes.ReportFieldEditForm;
[ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM]: FormTypes.ReimbursementAccountForm;
[ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT]: FormTypes.PersonalBankAccountForm;
[ONYXKEYS.FORMS.WORKSPACE_DESCRIPTION_FORM]: FormTypes.WorkspaceDescriptionForm;
};

type OnyxFormDraftValuesMapping = {
Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ const ROUTES = {
route: 'workspace/:policyID/profile/name',
getRoute: (policyID: string) => `workspace/${policyID}/profile/name` as const,
},
WORKSPACE_DESCRIPTION: {
route: 'workspace/:policyID/description',
getRoute: (policyID: string) => `workspace/${policyID}/description` as const,
},
WORKSPACE_AVATAR: {
route: 'workspace/:policyID/avatar',
getRoute: (policyID: string) => `workspace/${policyID}/avatar` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ const SCREENS = {
INVITE_MESSAGE: 'Workspace_Invite_Message',
CURRENCY: 'Workspace_Profile_Currency',
NAME: 'Workspace_Profile_Name',
DESCRIPTION: 'Workspace_Description',
},

EDIT_REQUEST: {
Expand Down
32 changes: 25 additions & 7 deletions src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextP
const roomWelcomeMessage = ReportUtils.getRoomWelcomeMessage(report, isUserPolicyAdmin);
const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(report, policy, participantAccountIDs);
const additionalText = moneyRequestOptions.map((item) => translate(`reportActionsView.iouTypes.${item}`)).join(', ');
const canEditPolicyDescription = ReportUtils.canEditPolicyDescription(policy);

const navigateToReport = () => {
if (!report?.reportID) {
Expand All @@ -61,13 +62,30 @@ function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextP
</View>
<View style={[styles.mt3, styles.mw100]}>
{isPolicyExpenseChat && (
<Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartOne')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getDisplayNameForParticipant(report?.ownerAccountID)}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartTwo')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getPolicyName(report)}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartThree')}</Text>
</Text>
<>
{policy?.description ? (
<PressableWithoutFeedback
onPress={() => {
if (!canEditPolicyDescription) {
return;
}
Navigation.navigate(ROUTES.WORKSPACE_DESCRIPTION.getRoute(policy.id));
}}
style={[styles.renderHTML, canEditPolicyDescription ? styles.cursorPointer : styles.cursorText]}
accessibilityLabel={translate('reportDescriptionPage.roomDescription')}
>
<RenderHTML html={policy.description} />
</PressableWithoutFeedback>
) : (
<Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartOne')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getDisplayNameForParticipant(report?.ownerAccountID)}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartTwo')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getPolicyName(report)}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartThree')}</Text>
</Text>
)}
</>
)}
{isChatRoom && (
<>
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1805,6 +1805,7 @@ export default {
`You have been invited to ${workspaceName || 'a workspace'}! Download the Expensify mobile app at use.expensify.com/download to start tracking your expenses.`,
},
editor: {
descriptionInputLabel: 'Workspace description',
nameInputLabel: 'Name',
nameInputHelpText: 'This is the name you will see on your workspace.',
nameIsRequiredError: 'You need to define a name for your workspace.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1830,6 +1830,7 @@ export default {
`¡Has sido invitado a ${workspaceName}! Descargue la aplicación móvil Expensify en use.expensify.com/download para comenzar a rastrear sus gastos.`,
},
editor: {
descriptionInputLabel: 'Descripción del espacio de trabajo',
nameInputLabel: 'Nombre',
nameInputHelpText: 'Este es el nombre que verás en tu espacio de trabajo.',
nameIsRequiredError: 'Debes definir un nombre para tu espacio de trabajo.',
Expand Down
6 changes: 6 additions & 0 deletions src/libs/API/parameters/UpdateWorkspaceDescriptionParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type UpdateWorkspaceDescriptionParams = {
policyID: string;
description: string;
};

export default UpdateWorkspaceDescriptionParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,4 @@ export type {default as SubmitReportParams} from './SubmitReportParams';
export type {default as DetachReceiptParams} from './DetachReceiptParams';
export type {default as PayMoneyRequestParams} from './PayMoneyRequestParams';
export type {default as AcceptACHContractForBankAccount} from './AcceptACHContractForBankAccount';
export type {default as UpdateWorkspaceDescriptionParams} from './UpdateWorkspaceDescriptionParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const WRITE_COMMANDS = {
PAY_MONEY_REQUEST_WITH_WALLET: 'PayMoneyRequestWithWallet',
PAY_MONEY_REQUEST: 'PayMoneyRequest',
ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT: 'AcceptACHContractForBankAccount',
UPDATE_WORKSPACE_DESCRIPTION: 'UpdateWorkspaceDescription',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -281,6 +282,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.PAY_MONEY_REQUEST_WITH_WALLET]: Parameters.PayMoneyRequestParams;
[WRITE_COMMANDS.PAY_MONEY_REQUEST]: Parameters.PayMoneyRequestParams;
[WRITE_COMMANDS.ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT]: Parameters.AcceptACHContractForBankAccount;
[WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION]: Parameters.UpdateWorkspaceDescriptionParams;
};

const READ_COMMANDS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType,
[SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType,
[SCREENS.WORKSPACE.NAME]: () => require('../../../pages/workspace/WorkspaceNamePage').default as React.ComponentType,
[SCREENS.WORKSPACE.DESCRIPTION]: () => require('../../../pages/workspace/WorkspaceDescriptionPage').default as React.ComponentType,
[SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceProfileCurrencyPage').default as React.ComponentType,
[SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType,
[SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.KEYBOARD_SHORTCUTS,
},
[SCREENS.WORKSPACE.NAME]: ROUTES.WORKSPACE_PROFILE_NAME.route,
[SCREENS.WORKSPACE.DESCRIPTION]: ROUTES.WORKSPACE_DESCRIPTION.route,
},
},
[SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: {
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ type SettingsNavigatorParamList = {
[SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_TIME]: undefined;
[SCREENS.WORKSPACE.CURRENCY]: undefined;
[SCREENS.WORKSPACE.NAME]: undefined;
[SCREENS.WORKSPACE.DESCRIPTION]: undefined;
[SCREENS.WORKSPACE.RATE_AND_UNIT]: {
policyID: string;
};
Expand Down
15 changes: 15 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2630,6 +2630,15 @@ function getReportDescriptionText(report: Report): string {
return parser.htmlToText(report.description);
}

function getPolicyDescriptionText(policy: Policy): string {
if (!policy.description) {
return '';
}

const parser = new ExpensiMark();
return parser.htmlToText(policy.description);
}

function buildOptimisticAddCommentReportAction(text?: string, file?: File, actorAccountID?: number): OptimisticReportAction {
const parser = new ExpensiMark();
const commentText = getParsedComment(text ?? '');
Expand Down Expand Up @@ -4667,6 +4676,10 @@ function canEditReportDescription(report: OnyxEntry<Report>, policy: OnyxEntry<P
);
}

function canEditPolicyDescription(policy: OnyxEntry<Policy>): boolean {
return PolicyUtils.isPolicyAdmin(policy);
}

/**
* Checks if report action has error when smart scanning
*/
Expand Down Expand Up @@ -5046,6 +5059,8 @@ export {
getAvailableReportFields,
reportFieldsEnabled,
getAllAncestorReportActionIDs,
canEditPolicyDescription,
getPolicyDescriptionText,
};

export type {
Expand Down
58 changes: 58 additions & 0 deletions src/libs/actions/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
OpenWorkspaceReimburseViewParams,
UpdateWorkspaceAvatarParams,
UpdateWorkspaceCustomUnitAndRateParams,
UpdateWorkspaceDescriptionParams,
UpdateWorkspaceGeneralSettingsParams,
} from '@libs/API/parameters';
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
Expand Down Expand Up @@ -922,6 +923,62 @@ function updateGeneralSettings(policyID: string, name: string, currency: string)
});
}

function updateDescription(policyID: string, description: string, currentDescription: string) {
if (description === currentDescription) {
return;
}
const parsedDescription = ReportUtils.getParsedComment(description);

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
description: parsedDescription,
pendingFields: {
description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
description: null,
},
},
},
];
const finallyData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
pendingFields: {
description: null,
},
},
},
];
const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
errorFields: {
description: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'),
},
},
},
];

const params: UpdateWorkspaceDescriptionParams = {
policyID,
description: parsedDescription,
};

API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION, params, {
optimisticData,
finallyData,
failureData,
});
}

function clearWorkspaceGeneralSettingsErrors(policyID: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {
errorFields: {
Expand Down Expand Up @@ -2069,4 +2126,5 @@ export {
buildOptimisticPolicyRecentlyUsedTags,
createDraftInitialWorkspace,
setWorkspaceInviteMessageDraft,
updateDescription,
};
46 changes: 41 additions & 5 deletions src/pages/home/HeaderView.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ const propTypes = {

/** The URL for the policy avatar */
avatar: PropTypes.string,

/** The id of the policy */
id: PropTypes.string,
}),

/** The reportID of the request */
Expand Down Expand Up @@ -111,7 +114,21 @@ function HeaderView(props) {
const isPolicyMember = useMemo(() => !_.isEmpty(props.policy), [props.policy]);
const canLeaveRoom = ReportUtils.canLeaveRoom(props.report, isPolicyMember);
const reportDescription = ReportUtils.getReportDescriptionText(props.report);
const policyName = ReportUtils.getPolicyName(props.report);
const policyName = ReportUtils.getPolicyName(props.report, true);
const policyDescription = ReportUtils.getPolicyDescriptionText(props.policy);
const isPersonalExpenseChat = isPolicyExpenseChat && ReportUtils.isCurrentUserSubmitter(props.report.reportID);
const shouldShowSubtitle = () => {
if (_.isEmpty(subtitle)) {
return false;
}
if (isChatRoom) {
return _.isEmpty(reportDescription);
}
if (isPolicyExpenseChat) {
return _.isEmpty(policyDescription);
}
return true;
};

// We hide the button when we are chatting with an automated Expensify account since it's not possible to contact
// these users via alternative means. It is possible to request a call with Concierge so we leave the option for them.
Expand Down Expand Up @@ -177,7 +194,7 @@ function HeaderView(props) {
);

const renderAdditionalText = () => {
if (_.isEmpty(policyName) || _.isEmpty(reportDescription) || !_.isEmpty(parentNavigationSubtitleData)) {
if (shouldShowSubtitle() || isPersonalExpenseChat || _.isEmpty(policyName) || !_.isEmpty(parentNavigationSubtitleData)) {
return null;
}
return (
Expand Down Expand Up @@ -280,15 +297,15 @@ function HeaderView(props) {
pressableStyles={[styles.alignSelfStart, styles.mw100]}
/>
)}
{!_.isEmpty(subtitle) && _.isEmpty(reportDescription) && (
{shouldShowSubtitle() && (
<Text
style={[styles.sidebarLinkText, styles.optionAlternateText, styles.textLabelSupporting]}
numberOfLines={1}
>
{subtitle}
</Text>
)}
{!_.isEmpty(reportDescription) && _.isEmpty(parentNavigationSubtitleData) && (
{isChatRoom && !_.isEmpty(reportDescription) && _.isEmpty(parentNavigationSubtitleData) && (
<PressableWithoutFeedback
onPress={() => {
if (ReportUtils.canEditReportDescription(props.report, props.policy)) {
Expand All @@ -308,6 +325,26 @@ function HeaderView(props) {
</Text>
</PressableWithoutFeedback>
)}
{isPolicyExpenseChat && !_.isEmpty(policyDescription) && _.isEmpty(parentNavigationSubtitleData) && (
<PressableWithoutFeedback
onPress={() => {
if (ReportUtils.canEditPolicyDescription(props.policy)) {
Navigation.navigate(ROUTES.WORKSPACE_DESCRIPTION.getRoute(props.report.policyID));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line created a regression. Issue here #36991

return;
}
Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(props.reportID));
}}
style={[styles.alignSelfStart, styles.mw100]}
accessibilityLabel={translate('workspace.editor.descriptionInputLabel')}
>
<Text
style={[styles.sidebarLinkText, styles.optionAlternateText, styles.textLabelSupporting]}
numberOfLines={1}
>
{policyDescription}
</Text>
</PressableWithoutFeedback>
)}
</View>
{brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && (
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
Expand Down Expand Up @@ -371,7 +408,6 @@ export default memo(
},
policy: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
selector: (policy) => _.pick(policy, ['name', 'avatar', 'pendingAction']),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why we need to make this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjcoffee We need to use many other properties on the policy object like role, description. I don't see a selector like this being used anywhere else for policy because we are querying only one policy and the collection policies, I don't think it will hurt to not use the selector and use the whole policy object.

},
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/report/ReportActionItemCreated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export default withOnyx<ReportActionItemCreatedProps, ReportActionItemCreatedOny
prevProps.report?.lastReadTime === nextProps.report?.lastReadTime &&
prevProps.report?.description === nextProps.report?.description &&
prevProps.personalDetails === nextProps.personalDetails &&
prevProps.policy?.description === nextProps.policy?.description &&
prevProps.report?.reportName === nextProps.report?.reportName,
),
);
Loading
Loading