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

Update the chat UI to reflect drag-and-drop #12056

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
01c1842
Initial
vladamx Oct 16, 2022
0758179
Rename to drop zone overlay
vladamx Oct 17, 2022
c1b9509
Rename to DropZone
vladamx Oct 17, 2022
71460f3
Add styles and refactor
vladamx Oct 20, 2022
7fcda67
State machine for dragging detection
vladamx Oct 20, 2022
84bb0e9
Cleanup
vladamx Oct 21, 2022
9bc383f
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Oct 21, 2022
411f8f5
Remove old count approach
vladamx Oct 21, 2022
0d5f39f
Revert text medium
vladamx Oct 21, 2022
1d434eb
Rename to dragAndDropBackground and apply opacity
vladamx Oct 24, 2022
5f003de
Removed checks on document and drop zone; added comments
vladamx Oct 25, 2022
9f08a8b
Extract inline styles
vladamx Oct 25, 2022
1773538
Update drop to upload translation
vladamx Oct 25, 2022
46ba48a
Use clientBoundingRect to detect dragleave on dropzone
vladamx Oct 27, 2022
f7cf158
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Oct 27, 2022
c40edc4
Use gorhom portal, cleanup ui
vladamx Nov 2, 2022
9de5e0d
Fix client bound rect when under responsive breakpoint
vladamx Nov 4, 2022
fc3955d
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 4, 2022
5b711b3
Rename styles
vladamx Nov 7, 2022
dff0b37
Extract drag and drop of logic
vladamx Nov 7, 2022
e78d327
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 7, 2022
2fd78ed
Explicit dragleave detection checks on target element
vladamx Nov 8, 2022
f993781
Make DragAndDrop component dropZone agnostic
vladamx Nov 8, 2022
af18a84
Make DragAndDrop platform agnostic and introduce canceling mechanism
vladamx Nov 9, 2022
75d6fa2
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 9, 2022
d52f7f6
Add transparent overlay on top of dropzone to block events on content
vladamx Nov 12, 2022
2cfdac3
Throttle dragover and make it optional, code cleanup
vladamx Nov 14, 2022
2ae2283
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 14, 2022
9d4a08a
Revert DropZoneViewHolder and cleanup
vladamx Nov 16, 2022
21e91d0
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 16, 2022
bb38bf9
Provide defaultProps for dragover
vladamx Nov 16, 2022
f871dd2
Migrate to Expensicons, move propTypes to separate file and mount dra…
vladamx Nov 16, 2022
671cc2b
Reorder drag icon export, remove proptypes export, rename bg color
vladamx Nov 16, 2022
c6efec0
Rename listeners, cancel existing dragging over state for non handled…
vladamx Nov 16, 2022
988a207
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 22, 2022
9b1097b
Add guard for files in the drop zone
vladamx Nov 23, 2022
4373518
Remove drag n drop from composer story
vladamx Nov 23, 2022
6167642
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 23, 2022
cf7b3a6
Add drag and drop story
vladamx Nov 29, 2022
4f1a80f
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Nov 29, 2022
936ead2
Add image upload in a story and change background color to transparen…
vladamx Nov 30, 2022
f1b191d
Move colors to theme
vladamx Dec 1, 2022
d8103e1
Merge remote-tracking branch 'origin/main' into vladamx-proposal-1182…
vladamx Dec 1, 2022
8f81203
Add comments
vladamx Dec 2, 2022
5e12ced
Update drop icon
vladamx Dec 2, 2022
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/drag-and-drop.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 74 additions & 35 deletions src/components/Composer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ const IMAGE_EXTENSIONS = {
};

const COPY_DROP_EFFECT = 'copy';
const NONE_DROP_EFFECT = 'none';

/**
* Enable Markdown parsing.
Expand All @@ -130,8 +131,20 @@ class Composer extends React.Component {
end: initialValue.length,
},
};
this.dragNDropListener = this.dragNDropListener.bind(this);
this.paste = this.paste.bind(this);

this.dropZone = document.getElementById(CONST.REPORT.DROP_NATIVE_ID);
this.dropZoneRect = this.dropZone.getBoundingClientRect();
this.dragNDropListener = this.dragNDropListener.bind(this);
this.dropZoneDragHandler = this.dropZoneDragHandler.bind(this);
this.dropZoneDragListener = this.dropZoneDragListener.bind(this);

/*
Last detected drag state on the dropzone -> we start with dragleave since user is not dragging initially.
This state is updated when drop zone is left/entered entirely(not taking the children in the account) or entire window is left
*/
this.dropZoneDragState = 'dragleave';

this.handlePaste = this.handlePaste.bind(this);
this.handlePastedHTML = this.handlePastedHTML.bind(this);
this.handleWheel = this.handleWheel.bind(this);
Expand Down Expand Up @@ -197,46 +210,72 @@ class Composer extends React.Component {
}

/**
* Handles all types of drag-N-drop events on the composer
*
* @param {Object} e native Event
* @param {Object} event native Event
*/
dragNDropListener(e) {
let isOriginComposer = false;
const handler = () => {
// Setting dropEffect for dragover is required for '+' icon on certain platforms/browsers (eg. Safari)
switch (e.type) {
case 'dragover':
e.preventDefault();
e.dataTransfer.dropEffect = COPY_DROP_EFFECT;
this.props.onDragOver(e, isOriginComposer);
break;
case 'dragenter':
e.dataTransfer.dropEffect = COPY_DROP_EFFECT;
this.props.onDragEnter(e, isOriginComposer);
break;
case 'dragleave':
this.props.onDragLeave(e, isOriginComposer);
break;
case 'drop':
this.props.onDrop(e, isOriginComposer);
break;
default: break;
}
};

// We first check if drop target is composer so that it can be highlighted
if (this.textInput.contains(e.target)) {
isOriginComposer = true;
handler();
return;
dropZoneDragHandler(event) {
// Setting dropEffect for dragover is required for '+' icon on certain platforms/browsers (eg. Safari)
switch (event.type) {
case 'dragover':
// Continuous event -> can hurt performance, be careful when subscribing
// eslint-disable-next-line no-param-reassign
event.dataTransfer.dropEffect = COPY_DROP_EFFECT;
this.dropZoneDragState = 'dragover';
this.props.onDragOver(event);
break;
case 'dragenter':
// Avoid reporting onDragEnter for children views -> not performant
Copy link
Member

Choose a reason for hiding this comment

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

Can you please explain how it is avoiding children dragEnter event?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By using dropZoneDragState which is last detected drag state on the dropzone -> we start with dragleave since user is not dragging initially.
This state is updated when drop zone is left/entered entirely(not taking the children in the account) or entire window is left

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I meant in the comment. 😄

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 did that as well :)

if (this.dropZoneDragState === 'dragleave') {
// eslint-disable-next-line no-param-reassign
event.dataTransfer.dropEffect = COPY_DROP_EFFECT;
this.dropZoneDragState = 'dragenter';
this.props.onDragEnter(event);
}
break;
case 'dragleave':
if (this.dropZoneDragState === 'dragenter' || this.dropZoneDragState === 'dragover') {
if (
event.clientY < this.dropZoneRect.top
|| event.clientY >= this.dropZoneRect.bottom
|| event.clientX < this.dropZoneRect.left
|| event.clientX >= this.dropZoneRect.right
) {
this.dropZoneDragState = 'dragleave';
this.props.onDragLeave(event);
}
}
break;
case 'drop':
this.dropZoneDragState = 'dragleave';
this.props.onDrop(event);
break;
default: break;
}
}

if (document.getElementById(CONST.REPORT.DROP_NATIVE_ID).contains(e.target)) {
handler();
/**
* Handles all types of drag-N-drop events on the drop zone associated with composer
*
* @param {Object} event native Event
*/
dropZoneDragListener(event) {
event.preventDefault();
if (this.dropZone.contains(event.target)) {
this.dropZoneDragHandler(event);
} else {
// eslint-disable-next-line no-param-reassign
event.dataTransfer.dropEffect = NONE_DROP_EFFECT;
}
}

/**
* Handles all types of drag-N-drop events on the composer and associated drop zone if it exists
*
* @param {Object} event native Event
*/
dragNDropListener(event) {
this.dropZoneDragListener(event);
}

/**
* Set pasted text to clipboard
* @param {String} text
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export default {
},
reportActionCompose: {
addAction: 'Actions',
dropToUpload: 'Drop to upload',
sendAttachment: 'Send attachment',
addAttachment: 'Add attachment',
writeSomething: 'Write something...',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export default {
},
reportActionCompose: {
addAction: 'Acción',
dropToUpload: 'Suelta el archivo aquí para compartirlo',
sendAttachment: 'Enviar adjunto',
addAttachment: 'Agregar archivo adjunto',
writeSomething: 'Escribe algo...',
Expand Down
17 changes: 5 additions & 12 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import ExceededCommentLength from '../../../components/ExceededCommentLength';
import withNavigationFocus from '../../../components/withNavigationFocus';
import * as EmojiUtils from '../../../libs/EmojiUtils';
import reportPropTypes from '../../reportPropTypes';
import ReportDropUI from './ReportDropUI';

const propTypes = {
/** Beta features list */
Expand Down Expand Up @@ -618,21 +619,12 @@ class ReportActionCompose extends React.Component {
placeholderTextColor={themeColors.placeholderText}
onChangeText={comment => this.updateComment(comment, true)}
onKeyPress={this.triggerHotkeyActions}
onDragEnter={(e, isOriginComposer) => {
if (!isOriginComposer) {
return;
}

onDragEnter={() => {
this.setState({isDraggingOver: true});
}}
onDragOver={(e, isOriginComposer) => {
if (!isOriginComposer) {
return;
}

this.setState({isDraggingOver: true});
onDragLeave={() => {
this.setState({isDraggingOver: false});
}}
onDragLeave={() => this.setState({isDraggingOver: false})}
onDrop={(e) => {
e.preventDefault();

Expand Down Expand Up @@ -703,6 +695,7 @@ class ReportActionCompose extends React.Component {
<ReportTypingIndicator reportID={this.props.reportID} />
<ExceededCommentLength commentLength={this.comment.length} />
</View>
{this.state.isDraggingOver && <ReportDropUI />}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would require larger restructuring since we depend on displayFileInModal inside onDrop which is provided from AttachmentModal

Copy link
Member

@parasharrajat parasharrajat Nov 9, 2022

Choose a reason for hiding this comment

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

Then you can put the whole merged component in the ReportActionCompose component.

</View>
);
}
Expand Down
37 changes: 37 additions & 0 deletions src/pages/home/report/ReportDropUI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import {createPortal} from 'react-dom';
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
import {View} from 'react-native';
import CONST from '../../../CONST';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import DragAndDropIcon from '../../../../assets/images/drag-and-drop.svg';
vladamx marked this conversation as resolved.
Show resolved Hide resolved
import styles from '../../../styles/styles';
import Text from '../../../components/Text';

const propTypes = {
...withLocalizePropTypes,
};

const DropZone = ({children}) => createPortal(
children,
document.getElementById(CONST.REPORT.DROP_NATIVE_ID),
);

const ReportDropUI = props => (
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
<DropZone>
<View style={[styles.dragAndDropOverlay, styles.alignItemsCenter, styles.justifyContentCenter]}>
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
<View style={[styles.mb3]}>
<DragAndDropIcon width={100} height={100} />
</View>
<Text style={[styles.h1]}>
{props.translate('reportActionCompose.dropToUpload')}
</Text>
</View>
</View>
</DropZone>
);

ReportDropUI.displayName = 'ReportDropUI';
ReportDropUI.propTypes = propTypes;

export default withLocalize(ReportDropUI);
13 changes: 13 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2699,6 +2699,19 @@ const styles = {
alignSelf: 'center',
},

dragAndDropOverlay: {
vladamx marked this conversation as resolved.
Show resolved Hide resolved
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: themeColors.dragAndDropBackground,
opacity: 0.96,
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO this opacity should be defined in the theme file

Copy link
Member

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

zIndex: 2,
},

textPill: {
ellipsizeMode: 'end',
backgroundColor: colors.gray2,
Expand Down
1 change: 1 addition & 0 deletions src/styles/themes/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ export default {
placeholderText: colors.gray3,
heroCard: colors.blue,
uploadPreviewActivityIndicator: colors.gray1,
dragAndDropBackground: colors.white,
};