Skip to content
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
15 changes: 15 additions & 0 deletions assets/images/task.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const CONST = {
POLICY_ROOMS: 'policyRooms',
POLICY_EXPENSE_CHAT: 'policyExpenseChat',
PASSWORDLESS: 'passwordless',
TASKS: 'tasks',
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down
1 change: 1 addition & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export default {
HOME_ADDRESS_FORM: 'homeAddressForm',
NEW_ROOM_FORM: 'newRoomForm',
ROOM_SETTINGS_FORM: 'roomSettingsForm',
NEW_TASK_FORM: 'newTaskForm',
MONEY_REQUEST_DESCRIPTION_FORM: 'moneyRequestDescriptionForm',
},

Expand Down
5 changes: 5 additions & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const IOU_DETAILS = 'iou/details';
const IOU_REQUEST_CURRENCY = `${IOU_REQUEST}/currency`;
const IOU_BILL_CURRENCY = `${IOU_BILL}/currency`;
const IOU_SEND_CURRENCY = `${IOU_SEND}/currency`;
const NEW_TASK = 'new/task';
const SETTINGS_PERSONAL_DETAILS = 'settings/profile/personal-details';
const SETTINGS_CONTACT_METHODS = 'settings/profile/contact-methods';

Expand Down Expand Up @@ -55,6 +56,7 @@ export default {
SETTINGS_NEW_CONTACT_METHOD: `${SETTINGS_CONTACT_METHODS}/new`,
NEW_GROUP: 'new/group',
NEW_CHAT: 'new/chat',
NEW_TASK,
REPORT,
REPORT_WITH_ID: 'r/:reportID',
getReportRoute: reportID => `r/${reportID}`,
Expand Down Expand Up @@ -89,6 +91,9 @@ export default {
IOU_DETAILS_ENABLE_PAYMENTS: `${IOU_DETAILS}/enable-payments`,
IOU_DETAILS_WITH_IOU_REPORT_ID: `${IOU_DETAILS}/:chatReportID/:iouReportID/`,
getIouDetailsRoute: (chatReportID, iouReportID) => `iou/details/${chatReportID}/${iouReportID}`,
getNewTaskRoute: reportID => `${NEW_TASK}/${reportID}`,
NEW_TASK_WITH_REPORT_ID: `${NEW_TASK}/:reportID?`,
getTaskDetailsRoute: taskID => `task/details/${taskID}`,
SEARCH: 'search',
SET_PASSWORD_WITH_VALIDATE_CODE: 'setpassword/:accountID/:validateCode',
DETAILS: 'details',
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import Podcast from '../../../assets/images/social-podcast.svg';
import Linkedin from '../../../assets/images/social-linkedin.svg';
import Instagram from '../../../assets/images/social-instagram.svg';
import AddReaction from '../../../assets/images/add-reaction.svg';
import Task from '../../../assets/images/task.svg';

export {
ActiveRoomAvatar,
Expand Down Expand Up @@ -204,6 +205,7 @@ export {
Send,
Shield,
Sync,
Task,
ThreeDots,
Transfer,
Trashcan,
Expand Down
7 changes: 7 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,13 @@ export default {
public_announce: 'Public Announce',
},
},
newTaskPage: {
assignTask: 'Assign task',
title: 'Title',
description: 'Description',
shareIn: 'Share in',
pleaseEnterTaskName: 'Please enter a title',
},
statementPage: {
generatingPDF: 'We\'re generating your PDF right now. Please come back later!',
},
Expand Down
7 changes: 7 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,13 @@ export default {
public_announce: 'Anuncio Público',
},
},
newTaskPage: {
assignTask: 'Asignar tarea',
title: 'Título',
description: 'Descripción',
shareIn: 'Compartir en',
pleaseEnterTaskName: 'Por favor introduce un título',
},
statementPage: {
generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!',
},
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/AppNavigator/AuthScreens.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ class AuthScreens extends React.Component {
component={ModalStackNavigators.IOURequestModalStackNavigator}
listeners={modalScreenListeners}
/>
<RootStack.Screen
name="NewTask"
options={modalScreenOptions}
component={ModalStackNavigators.NewTaskModalStackNavigator}
listeners={modalScreenListeners}
/>
<RootStack.Screen
name="IOU_Bill"
options={modalScreenOptions}
Expand Down
9 changes: 9 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ const NewChatModalStackNavigator = createModalStackNavigator([{
name: 'NewChat_Root',
}]);

const NewTaskModalStackNavigator = createModalStackNavigator([{
getComponent: () => {
const NewTaskPage = require('../../../pages/NewTaskPage').default;
return NewTaskPage;
},
name: 'NewTask_Root',
}]);

const SettingsModalStackNavigator = createModalStackNavigator([
{
getComponent: () => {
Expand Down Expand Up @@ -545,6 +553,7 @@ export {
SearchModalStackNavigator,
NewGroupModalStackNavigator,
NewChatModalStackNavigator,
NewTaskModalStackNavigator,
SettingsModalStackNavigator,
EnablePaymentsStackNavigator,
AddPersonalBankAccountModalStackNavigator,
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ export default {
NewChat_Root: ROUTES.NEW_CHAT,
},
},
NewTask: {
screens: {
NewTask_Root: ROUTES.NEW_TASK_WITH_REPORT_ID,
},
},
Search: {
screens: {
Search_Root: ROUTES.SEARCH,
Expand Down
9 changes: 9 additions & 0 deletions src/libs/Permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ function canUsePasswordlessLogins(betas) {
return _.contains(betas, CONST.BETAS.PASSWORDLESS) || _.contains(betas, CONST.BETAS.ALL);
}

/**
* @param {Array<String>} betas
* @returns {Boolean}
*/
function canUseTasks(betas) {
return _.contains(betas, CONST.BETAS.TASKS) || _.contains(betas, CONST.BETAS.ALL);
}

export default {
canUseChronos,
canUseIOU,
Expand All @@ -105,4 +113,5 @@ export default {
canUsePolicyRooms,
canUsePolicyExpenseChat,
canUsePasswordlessLogins,
canUseTasks,
};
96 changes: 96 additions & 0 deletions src/pages/NewTaskPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
import HeaderWithCloseButton from '../components/HeaderWithCloseButton';
import Navigation from '../libs/Navigation/Navigation';
import ScreenWrapper from '../components/ScreenWrapper';
import styles from '../styles/styles';
import ONYXKEYS from '../ONYXKEYS';
import * as ErrorUtils from '../libs/ErrorUtils';
import Form from '../components/Form';
import TextInput from '../components/TextInput';
import Permissions from '../libs/Permissions';

const propTypes = {
/** List of betas available to current user */
betas: PropTypes.arrayOf(PropTypes.string),

...withLocalizePropTypes,
};
const defaultProps = {
betas: [],
};

// NOTE: This page is going to be updated in https://github.com/Expensify/App/issues/16855, this is just a placeholder for now
const NewTaskPage = (props) => {
/**
Comment thread
thienlnam marked this conversation as resolved.
* @param {Object} values - form input values passed by the Form component
* @returns {Boolean}
*/
function validate(values) {
Comment thread
thienlnam marked this conversation as resolved.
const errors = {};

if (!values.taskTitle) {
// We error if the user doesn't enter a room name
ErrorUtils.addErrorMessage(errors, 'taskTitle', props.translate('newTaskPage.pleaseEnterTaskName'));
}

return errors;
}

function onSubmit() {
Comment thread
thienlnam marked this conversation as resolved.

}

if (!Permissions.canUseTasks(props.betas)) {
Navigation.dismissModal();
return null;
}

return (
<ScreenWrapper includeSafeAreaPaddingBottom={false}>
<HeaderWithCloseButton
title={props.translate('newTaskPage.assignTask')}
onCloseButtonPress={() => Navigation.dismissModal()}
/>
<Form
formID={ONYXKEYS.FORMS.NEW_TASK_FORM}
submitButtonText={props.translate('newTaskPage.assignTask')}
style={[styles.mh5, styles.mt5, styles.flexGrow1]}
validate={values => validate(values)}
onSubmit={() => onSubmit()}
enabledWhenOffline
>
<View style={styles.mb5}>
<TextInput
autoFocus
inputID="taskTitle"
label={props.translate('newTaskPage.title')}
/>
</View>
<View style={styles.mb5}>
<TextInput
inputID="taskDescription"
label={props.translate('newTaskPage.description')}
/>
</View>
</Form>
</ScreenWrapper>
);
};

NewTaskPage.displayName = 'NewTaskPage';
NewTaskPage.propTypes = propTypes;
NewTaskPage.defaultProps = defaultProps;

export default compose(
withOnyx({
betas: {
key: ONYXKEYS.BETAS,
},
}),
withLocalize,
)(NewTaskPage);
27 changes: 26 additions & 1 deletion src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import withKeyboardState, {keyboardStatePropTypes} from '../../../components/wit
import ArrowKeyFocusManager from '../../../components/ArrowKeyFocusManager';
import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
import KeyboardShortcut from '../../../libs/KeyboardShortcut';
import Permissions from '../../../libs/Permissions';

const propTypes = {
/** Beta features list */
Expand Down Expand Up @@ -173,6 +174,7 @@ class ReportActionCompose extends React.Component {
this.setTextInputRef = this.setTextInputRef.bind(this);
this.getInputPlaceholder = this.getInputPlaceholder.bind(this);
this.getMoneyRequestOptions = this.getMoneyRequestOptions.bind(this);
this.getTaskOption = this.getTaskOption.bind(this);
this.addAttachment = this.addAttachment.bind(this);
this.insertSelectedEmoji = this.insertSelectedEmoji.bind(this);
this.setExceededMaxCommentLength = this.setExceededMaxCommentLength.bind(this);
Expand Down Expand Up @@ -391,6 +393,29 @@ class ReportActionCompose extends React.Component {
}
}

/**
* Determines if we can show the task option
* @param {Array} reportParticipants
* @returns {Boolean}
*/
getTaskOption(reportParticipants) {
// We only prevent the task option from showing if it's a DM and the other user is an Expensify default email
if (!Permissions.canUseTasks(this.props.betas) || (lodashGet(this.props.report, 'participants', []).length === 1 && _.some(reportParticipants, email => _.contains(
CONST.EXPENSIFY_EMAILS,
email,
)))) {
return [];
}

return [
{
icon: Expensicons.Task,
text: this.props.translate('newTaskPage.assignTask'),
onSelected: () => Navigation.navigate(ROUTES.getNewTaskRoute(this.props.reportID)),
},
];
}

/**
* Clean data related to EmojiSuggestions
*/
Expand Down Expand Up @@ -798,7 +823,7 @@ class ReportActionCompose extends React.Component {
onClose={() => this.setMenuVisibility(false)}
onItemSelected={() => this.setMenuVisibility(false)}
anchorPosition={styles.createMenuPositionReportActionCompose}
menuItems={[...this.getMoneyRequestOptions(reportParticipants),
menuItems={[...this.getMoneyRequestOptions(reportParticipants), ...this.getTaskOption(reportParticipants),
{
icon: Expensicons.Paperclip,
text: this.props.translate('reportActionCompose.addAttachment'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@ class FloatingActionButtonAndPopover extends React.Component {
onSelected: () => Navigation.navigate(ROUTES.IOU_BILL),
},
] : []),
...(Permissions.canUseTasks(this.props.betas) ? [
{
icon: Expensicons.Task,
text: this.props.translate('newTaskPage.assignTask'),
onSelected: () => Navigation.navigate(ROUTES.NEW_TASK),
},
] : []),
...(!this.props.isLoading && !Policy.hasActiveFreePolicy(this.props.allPolicies) ? [
{
icon: Expensicons.NewWorkspace,
Expand Down