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

Support read only messages #40010

Merged
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7f4f512
feat: added component for displaying static message inm onboarding
barttom Mar 21, 2024
297a231
Merge branch 'feat/38773/support-read-only-messages' of https://githu…
rezkiy37 Apr 10, 2024
cd7a95f
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Apr 17, 2024
26bcce6
add permissions to report
rezkiy37 Apr 17, 2024
de29862
create isReadOnly
rezkiy37 Apr 17, 2024
e2ad69d
integrate OnboardingReportFooterMessage into ReportFooter
rezkiy37 Apr 17, 2024
471a02a
prettify
rezkiy37 Apr 17, 2024
b4aed0f
restrict task action in header
rezkiy37 Apr 17, 2024
0c1efc3
read only for TaskView
rezkiy37 Apr 17, 2024
d72ceab
read only for TaskPreview
rezkiy37 Apr 17, 2024
287fbe1
read only for HeaderView
rezkiy37 Apr 17, 2024
7adce5f
Merge branch 'feature/38771-tasks-for-guided-setup' of https://github…
rezkiy37 Apr 18, 2024
c386da0
fix isReadOnly condition
rezkiy37 Apr 18, 2024
8243a2b
improve OnboardingReportFooterMessage
rezkiy37 Apr 18, 2024
3896162
add permissions to report
rezkiy37 Apr 18, 2024
af3b05f
improve read only for tasks
rezkiy37 Apr 18, 2024
09a118c
Consierge read only
rezkiy37 Apr 18, 2024
cd25155
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Apr 22, 2024
15478c0
revert dev change
rezkiy37 Apr 22, 2024
c5da50e
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Apr 23, 2024
6b41cde
restrict actions for read-only chats
rezkiy37 Apr 23, 2024
84c583b
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Apr 24, 2024
1b7d32a
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Apr 25, 2024
990e3cb
translate
rezkiy37 Apr 25, 2024
8fa54e6
remove todo
rezkiy37 Apr 25, 2024
a93647a
fix translations
rezkiy37 Apr 25, 2024
23b0ba0
Concierge read-only
rezkiy37 Apr 25, 2024
ace67dc
Revert "Concierge read-only"
rezkiy37 Apr 25, 2024
517253c
improve OnboardingReportFooterMessage
rezkiy37 Apr 25, 2024
c98a79f
fix check in ContextMenuActions
rezkiy37 Apr 25, 2024
68bcbd2
fix comment
rezkiy37 Apr 25, 2024
ab9b7f2
reuse Banner
rezkiy37 Apr 25, 2024
5eb36c8
add missed line
rezkiy37 Apr 25, 2024
a35ea06
clarify type
rezkiy37 Apr 25, 2024
cfc1231
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Apr 26, 2024
a85b757
redesign OnboardingReportFooterMessage
rezkiy37 Apr 26, 2024
4a92c91
update a place
rezkiy37 Apr 26, 2024
2f17417
rename fucntion
rezkiy37 Apr 26, 2024
ae9af7f
integrate canWriteInReport properly
rezkiy37 Apr 26, 2024
3512f7c
fix report footer styles
rezkiy37 Apr 26, 2024
0c80ad7
prettify
rezkiy37 Apr 26, 2024
9076bc0
rename the component
rezkiy37 Apr 26, 2024
62b193a
rename keys
rezkiy37 Apr 26, 2024
90a72e9
simplify admin chat getting
rezkiy37 Apr 26, 2024
907167c
improve admin chat searching
rezkiy37 Apr 26, 2024
38cbb1b
attach chatReportIDAdmins to policy optimistically
rezkiy37 Apr 26, 2024
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
6 changes: 6 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,12 @@ const CONST = {
OWNER_EMAIL_FAKE: '__FAKE__',
OWNER_ACCOUNT_ID_FAKE: 0,
DEFAULT_REPORT_NAME: 'Chat Report',
PERMISSIONS: {
READ: 'read',
WRITE: 'write',
SHARE: 'share',
OWN: 'own',
}
},
NEXT_STEP: {
FINISHED: 'Finished!',
Expand Down
4 changes: 4 additions & 0 deletions src/components/TaskHeaderActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ function TaskHeaderActionButton({report, session}: TaskHeaderActionButtonProps)
const {translate} = useLocalize();
const styles = useThemeStyles();

if (ReportUtils.isReadOnly(report)) {
return null;
}

return (
<View style={[styles.flexRow, styles.alignItemsCenter, styles.justifyContentEnd]}>
<Button
Expand Down
10 changes: 10 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2741,6 +2741,16 @@ export default {
},
copyReferralLink: 'Copy invite link',
},
onboardingBottomMessage: {
Copy link
Contributor

Choose a reason for hiding this comment

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

I propose to name this systemChatFooterMessage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done - 62b193a.

[CONST.INTRO_CHOICES.MANAGE_TEAM]: {
phrase1: 'Chat with your setup specialist in ',
phrase2: ' for help',
},
default: {
phrase1: 'Message ',
phrase2: ' for help with setup',
},
},
violations: {
allTagLevelsRequired: 'All tags required',
autoReportedRejectedExpense: ({rejectReason, rejectedBy}: ViolationsAutoReportedRejectedExpenseParams) => `${rejectedBy} rejected this expense with the comment "${rejectReason}"`,
Expand Down
10 changes: 10 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3234,6 +3234,16 @@ export default {
},
copyReferralLink: 'Copiar enlace de invitación',
},
onboardingBottomMessage: {
[CONST.INTRO_CHOICES.MANAGE_TEAM]: {
phrase1: 'Chatea con tu especialista asignado en ',
phrase2: ' para obtener ayuda',
},
default: {
phrase1: 'Envía un email a ',
phrase2: ' para obtener ayuda con la configuración',
},
Comment on lines +3260 to +3267
Copy link
Contributor

Choose a reason for hiding this comment

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

I would check the style does not go crazy in Spanish because I think the translations is slightly longer

},
violations: {
allTagLevelsRequired: 'Todas las etiquetas son obligatorias',
autoReportedRejectedExpense: ({rejectedBy, rejectReason}: ViolationsAutoReportedRejectedExpenseParams) => `${rejectedBy} rechazó la solicitud y comentó "${rejectReason}"`,
Expand Down
20 changes: 18 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1172,10 +1172,25 @@ function isJoinRequestInAdminRoom(report: OnyxEntry<Report>): boolean {
return ReportActionsUtils.isActionableJoinRequestPending(report.reportID);
}

/**
* Checks if report is in read-only mode.
*/
function isReadOnly(report: OnyxEntry<Report>): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the implementation and possible permissions, this name feels kind of wrong. Something like canWriteInReport sounds more accurate to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done - 2f17417, ae9af7f.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

UPD.mp4

if (Array.isArray(report?.permissions)) {
return !report?.permissions?.includes(CONST.REPORT.PERMISSIONS.WRITE);
}

return false;
}

/**
* Checks if the current user is allowed to comment on the given report.
*/
function isAllowedToComment(report: OnyxEntry<Report>): boolean {
if (isReadOnly(report)) {
return false;
}

// Default to allowing all users to post
const capability = report?.writeCapability ?? CONST.REPORT.WRITE_CAPABILITIES.ALL;

Expand Down Expand Up @@ -5374,7 +5389,7 @@ function canUserPerformWriteAction(report: OnyxEntry<Report>) {
return false;
}

return !isArchivedRoom(report) && isEmptyObject(reportErrors) && report && isAllowedToComment(report) && !isAnonymousUser;
return !isArchivedRoom(report) && isEmptyObject(reportErrors) && report && isAllowedToComment(report) && !isAnonymousUser && !isReadOnly(report);
}

/**
Expand Down Expand Up @@ -6254,7 +6269,6 @@ export {
getParsedComment,
getParticipantAccountIDs,
getParticipants,
getPayeeName,
getPendingChatMembers,
getPersonalDetailsForAccountID,
getPolicyDescriptionText,
Expand Down Expand Up @@ -6289,6 +6303,7 @@ export {
getWorkspaceChats,
getWorkspaceIcon,
goBackToDetailsPage,
getPayeeName,
hasActionsWithErrors,
hasAutomatedExpensifyAccountIDs,
hasExpensifyGuidesEmails,
Expand Down Expand Up @@ -6376,6 +6391,7 @@ export {
isValidReport,
isValidReportIDFromPath,
isWaitingForAssigneeToCompleteTask,
isReadOnly,
navigateToDetailsPage,
navigateToPrivateNotes,
parseReportRouteParams,
Expand Down
4 changes: 4 additions & 0 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,10 @@ function canModifyTask(taskReport: OnyxEntry<OnyxTypes.Report>, sessionAccountID
return true;
}

if (ReportUtils.isReadOnly(ReportUtils.getReport(taskReport?.reportID))) {
return false;
}

return !isEmptyObject(taskReport) && ReportUtils.isAllowedToComment(taskReport);
}

Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/HeaderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function HeaderView({report, personalDetails, parentReport, parentReportAction,
}

// Task is not closed
if (report.stateNum !== CONST.REPORT.STATE_NUM.APPROVED && report.statusNum !== CONST.REPORT.STATUS_NUM.CLOSED && canModifyTask) {
if (!ReportUtils.isReadOnly(report) && report.stateNum !== CONST.REPORT.STATE_NUM.APPROVED && report.statusNum !== CONST.REPORT.STATUS_NUM.CLOSED && canModifyTask) {
threeDotMenuItems.push({
icon: Expensicons.Trashcan,
text: translate('common.delete'),
Expand Down
2 changes: 2 additions & 0 deletions src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ function ReportScreen({
isOptimisticReport: reportProp?.isOptimisticReport,
lastMentionedTime: reportProp?.lastMentionedTime,
avatarUrl: reportProp?.avatarUrl,
permissions: reportProp?.permissions,
Copy link
Contributor

Choose a reason for hiding this comment

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

When a user signs-in via google after viewing a public room, the permissions was not getting updated. A deep comparison was required here to update the dependency. This caused #41955 where the composer was not getting displayed even after signing-in via google.

}),
[
reportProp?.lastReadTime,
Expand Down Expand Up @@ -246,6 +247,7 @@ function ReportScreen({
reportProp?.isOptimisticReport,
reportProp?.lastMentionedTime,
reportProp?.avatarUrl,
reportProp?.permissions,
],
);

Expand Down
13 changes: 13 additions & 0 deletions src/pages/home/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -517,5 +517,18 @@ const ContextMenuActions: ContextMenuAction[] = [
},
];

const restrictedReadOnlyActions = [
rezkiy37 marked this conversation as resolved.
Show resolved Hide resolved
'common.download',
'reportActionContextMenu.replyInThread',
'reportActionContextMenu.editAction',
'reportActionContextMenu.joinThread',
'reportActionContextMenu.deleteAction',
];

const RestrictedReadOnlyContextMenuActions: ContextMenuAction[] = ContextMenuActions.filter(
(action) => 'textTranslateKey' in action && restrictedReadOnlyActions.includes(action.textTranslateKey),
);

export {RestrictedReadOnlyContextMenuActions};
export default ContextMenuActions;
export type {ContextMenuActionPayload, ContextMenuAction};
107 changes: 107 additions & 0 deletions src/pages/home/report/OnboardingReportFooterMessage.tsx
Copy link
Contributor

Choose a reason for hiding this comment

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

I propose to name this SystemChatReportFooterMessage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done - 9076bc0.

Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, {useMemo} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as PolicyUtils from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import * as ReportInstance from '@userActions/Report';
import type {OnboardingPurposeType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Policy as PolicyType, Report} from '@src/types/onyx';

type OnboardingReportFooterMessageOnyxProps = {
/** Saved onboarding purpose selected by the user */
choice: OnyxEntry<OnboardingPurposeType>;

/** Collection of reports */
Copy link
Contributor

Choose a reason for hiding this comment

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

Which reports? All users reports?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, the app is looking for an admin chat to show in the banner.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is another way: to the active policyID by nvp_expensify_activePolicyID and find an admin chat. Let me try.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think about this approach - 90a72e9?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a problem when the user creates a new account, the active policy does not exist yet.

Copy link
Contributor Author

@rezkiy37 rezkiy37 Apr 26, 2024

Choose a reason for hiding this comment

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

Done - 907167c, 38cbb1b.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Test.mp4

reports: OnyxCollection<Report>;

/** The list of this user's policies */
policies: OnyxCollection<PolicyType>;
};
type OnboardingReportFooterMessageProps = OnboardingReportFooterMessageOnyxProps;
rezkiy37 marked this conversation as resolved.
Show resolved Hide resolved

function OnboardingReportFooterMessage({choice, reports, policies}: OnboardingReportFooterMessageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {isSmallScreenWidth} = useWindowDimensions();

const adminChatReport = useMemo(() => {
const adminsReports = Object.values(reports ?? {}).filter((report) => report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS);
const activePolicies = Object.values(policies ?? {}).filter((policy) => PolicyUtils.shouldShowPolicy(policy, false));

return adminsReports.find((report) => activePolicies.find((policy) => policy?.id === report?.policyID));
}, [policies, reports]);

const content = useMemo(() => {
switch (choice) {
case CONST.ONBOARDING_CHOICES.MANAGE_TEAM:
return (
<>
{translate('onboardingBottomMessage.newDotManageTeam.phrase1')}
<TextLink
style={styles.label}
onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(adminChatReport?.reportID ?? ''))}
>
{adminChatReport?.reportName ?? CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS}
</TextLink>
{translate('onboardingBottomMessage.newDotManageTeam.phrase2')}
</>
);
default:
return (
<>
{translate('onboardingBottomMessage.default.phrase1')}
<TextLink
style={styles.label}
onPress={() => ReportInstance.navigateToConciergeChat()}
>
{`${CONST?.CONCIERGE_CHAT_NAME}`}
</TextLink>
{translate('onboardingBottomMessage.default.phrase2')}
</>
);
}
}, [adminChatReport?.reportName, adminChatReport?.reportID, choice, styles.label, translate]);

return (
<View
style={[
styles.chatFooter,
isSmallScreenWidth ? styles.mb5 : styles.mb4,
styles.mh5,
styles.mt4,
styles.flexRow,
styles.alignItemsCenter,
styles.p4,
styles.borderRadiusComponentLarge,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this might be too large? cc @Expensify/design - I have a feeling we were using something like 8px in the mocks instead of 16px?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the component to reuse the Banner under the hood - #40010 (comment).

styles.hoveredComponentBG,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why hoveredComponentBG and not just a regular componentBG background color? What did we have in the mocks? cc @Expensify/design

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the component to reuse the Banner under the hood - #40010 (comment).

styles.breakWord,
styles.justifyContentCenter,
]}
>
<Text style={[styles.textSupporting, styles.label]}>{content}</Text>
</View>
);
}

OnboardingReportFooterMessage.displayName = 'OnboardingReportFooterMessage';

export default withOnyx<OnboardingReportFooterMessageProps, OnboardingReportFooterMessageOnyxProps>({
choice: {
key: ONYXKEYS.ONBOARDING_PURPOSE_SELECTED,
},
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
policies: {
key: ONYXKEYS.COLLECTION.POLICY,
},
})(OnboardingReportFooterMessage);
2 changes: 2 additions & 0 deletions src/pages/home/report/ReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import type * as OnyxTypes from '@src/types/onyx';
import type {OriginalMessageActionableMentionWhisper, OriginalMessageActionableTrackedExpenseWhisper, OriginalMessageJoinPolicyChangeLog} from '@src/types/onyx/OriginalMessage';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground';
import {RestrictedReadOnlyContextMenuActions} from './ContextMenu/ContextMenuActions';
import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu';
import {hideContextMenu} from './ContextMenu/ReportActionContextMenu';
Expand Down Expand Up @@ -914,6 +915,7 @@ function ReportActionItem({
originalReportID={originalReportID ?? ''}
isArchivedRoom={ReportUtils.isArchivedRoom(report)}
displayAsGroup={displayAsGroup}
disabledActions={ReportUtils.isReadOnly(report) ? RestrictedReadOnlyContextMenuActions : []}
isVisible={hovered && draftMessage === undefined && !hasErrors}
draftMessage={draftMessage}
isChronosReport={ReportUtils.chatIncludesChronos(originalReport)}
Expand Down
6 changes: 6 additions & 0 deletions src/pages/home/report/ReportFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
import type {PendingAction} from '@src/types/onyx/OnyxCommon';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import OnboardingReportFooterMessage from './OnboardingReportFooterMessage';
import ReportActionCompose from './ReportActionCompose/ReportActionCompose';

type ReportFooterOnyxProps = {
Expand Down Expand Up @@ -81,6 +82,7 @@ function ReportFooter({

const isSmallSizeLayout = windowWidth - (isSmallScreenWidth ? 0 : variables.sideBarWidth) < variables.anonymousReportFooterBreakpoint;
const hideComposer = !ReportUtils.canUserPerformWriteAction(report);
const isReadOnlyReport = ReportUtils.isReadOnly(report);

const allPersonalDetails = usePersonalDetails();

Expand Down Expand Up @@ -128,6 +130,10 @@ function ReportFooter({
[report.reportID, handleCreateTask],
);

if (isReadOnlyReport) {
return <OnboardingReportFooterMessage />;
}

return (
<>
{hideComposer && (
Expand Down
4 changes: 4 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ const styles = (theme: ThemeColors) =>
borderRadius: variables.buttonBorderRadius,
},

borderRadiusComponentLarge: {
borderRadius: variables.componentBorderRadiusLarge,
},

bottomTabBarContainer: {
flexDirection: 'row',
height: variables.bottomTabHeight,
Expand Down
2 changes: 2 additions & 0 deletions src/types/onyx/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback<
transactionThreadReportID?: string;

fieldList?: Record<string, PolicyReportField>;

permissions?: Array<ValueOf<typeof CONST.REPORT.PERMISSIONS>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

There should be a comment here

},
PolicyReportField['fieldID']
>;
Expand Down
Loading