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 a chat switcher to the LHN #282

Merged
merged 85 commits into from
Aug 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
fa0e2e5
Rename directories and move files around a little
tgolen Aug 21, 2020
a45f4e5
Refactor the sidebar into separate components
tgolen Aug 21, 2020
72b4bb9
Rename file
tgolen Aug 21, 2020
30a715d
Add initial search form
tgolen Aug 21, 2020
ba49562
Hide and show the report list on blur and focus
tgolen Aug 21, 2020
f5d8207
Remove color
tgolen Aug 21, 2020
804b5c5
Remove extra method
tgolen Aug 21, 2020
1d04aad
Get the search options to appear
tgolen Aug 21, 2020
989a4c2
Reset the view on tab and escape
tgolen Aug 21, 2020
94e9d7d
Show focused option
tgolen Aug 21, 2020
3578e59
Select the option and reset the component
tgolen Aug 21, 2020
3ced899
Add keyboard shortcut lib
tgolen Aug 21, 2020
c15b31e
Enable the command+k keyboard shortcut
tgolen Aug 21, 2020
5e33acc
Make the clear button do a full reset
tgolen Aug 21, 2020
a51e81b
Add some comments and shorten line
tgolen Aug 21, 2020
358a970
Add some styles and the avatar to the list
tgolen Aug 22, 2020
3527824
Merge branch 'master' into tgolen-chat-switcher
tgolen Aug 24, 2020
7cc9ebb
Fix paths
tgolen Aug 24, 2020
5c6ef87
Move focued index into the state
tgolen Aug 24, 2020
aa5ff2e
Make logic more explicit
tgolen Aug 24, 2020
972832e
DRY up proptypes for insets
tgolen Aug 24, 2020
632b596
Add more comments about using Set
tgolen Aug 24, 2020
fbe8937
More comment cleanup
tgolen Aug 24, 2020
21116f8
Enable key listening when typing a comment
tgolen Aug 24, 2020
8abe2c1
Rename and move inset prop types
tgolen Aug 24, 2020
3b98fc9
Fix prop types for personal details
tgolen Aug 24, 2020
9aa438e
Reference the color variables directly
tgolen Aug 25, 2020
91d6b62
Give a proper icon for the clear text button
tgolen Aug 25, 2020
fe8daee
Moving the logo into the chat switcher
tgolen Aug 25, 2020
f336455
Switch to the round logo
tgolen Aug 25, 2020
93cc7cd
Add indicator status styles
tgolen Aug 25, 2020
8a07b33
Remove duplicate file
tgolen Aug 25, 2020
dd16527
Add missing semicolon
tgolen Aug 25, 2020
0acc17c
Merge branch 'master' into tgolen-chat-switcher
tgolen Aug 25, 2020
133c06a
Put the logo on the same line as the chat switcher
tgolen Aug 25, 2020
34684ee
Reset the search when deleting all text
tgolen Aug 25, 2020
a6615aa
Fix some blur bugs
tgolen Aug 25, 2020
976b545
Remove extra semicolon
tgolen Aug 25, 2020
1b5813d
Fix display name
tgolen Aug 25, 2020
0501a9f
Update the logo asset
shawnborton Aug 25, 2020
f21dd6a
Align the sidebarHeader area better
shawnborton Aug 25, 2020
d158608
Updating some styles of the list items
shawnborton Aug 25, 2020
82dd0f0
Fix props warning
tgolen Aug 25, 2020
410a516
Improve display name
tgolen Aug 25, 2020
3af20fe
A few margin tweaks
shawnborton Aug 25, 2020
15d718b
Fix truncated text
tgolen Aug 25, 2020
cc8da28
Add inset margin
tgolen Aug 25, 2020
4961151
Merge branch 'tgolen-chat-switcher' of https://github.com/Expensify/R…
tgolen Aug 25, 2020
3edaae7
Make the blur work better
tgolen Aug 25, 2020
dd3aa46
Show the clear button on focus
tgolen Aug 25, 2020
8350c63
Fix sign in logo
shawnborton Aug 25, 2020
2202e24
Turn off outline for text input
tgolen Aug 25, 2020
935fc86
Change placeholder text color
shawnborton Aug 25, 2020
2b53a62
Add a component which allows for focus and blur styles
tgolen Aug 25, 2020
2dc1dcc
Merge branch 'tgolen-chat-switcher' of https://github.com/Expensify/R…
tgolen Aug 25, 2020
179098e
remove outline style because mobile doesn't support it
tgolen Aug 25, 2020
a5ac9fe
Merge branch 'master' into tgolen-chat-switcher
tgolen Aug 26, 2020
51c5146
Move the chat switcher list into a stateless component
tgolen Aug 26, 2020
33b56ba
Move the search form into it's own stateless component
tgolen Aug 26, 2020
f4fe554
Fix lint errors and fix proptypes
tgolen Aug 26, 2020
6c851d1
Focus styles for the search input
shawnborton Aug 26, 2020
2197aa7
Use a background color for the images while they are loading
shawnborton Aug 26, 2020
e5009e4
Fix sidebarHeader height on mobile
shawnborton Aug 26, 2020
5358226
Hide bottom of the sidebar when the chat switcher has focus
tgolen Aug 26, 2020
0502d85
Fix some flex grow weirdness
shawnborton Aug 26, 2020
5a9cda1
Simplify keyboard shortcut
tgolen Aug 26, 2020
2f388d5
Use a state property for if the component is focused
tgolen Aug 26, 2020
6e052d7
Use a variable for focused input
tgolen Aug 26, 2020
3dbd102
Improve style merging
tgolen Aug 26, 2020
f259284
Merge branch 'master' into tgolen-chat-switcher
tgolen Aug 26, 2020
28d7e02
Fix casing for HOC
tgolen Aug 26, 2020
f7bba83
Lint
tgolen Aug 26, 2020
b950723
Add a method for getting a chat report
tgolen Aug 27, 2020
e9ac597
Implement fetching the chat report and redirecting to it
tgolen Aug 27, 2020
9aa1820
Put in the proper API params
tgolen Aug 27, 2020
075ed9d
Improve comments and fix lint
tgolen Aug 27, 2020
a6efbfe
Some last minute testing fixes
tgolen Aug 27, 2020
90e9c80
Merge branch 'master' into tgolen-chat-switcher
tgolen Aug 27, 2020
a359f1b
Fix some lint errors
tgolen Aug 27, 2020
6ec2ffa
Get the report name for the new report
tgolen Aug 27, 2020
26c8b5d
Remove redundant request
tgolen Aug 27, 2020
a8732f8
Always merge personal details
tgolen Aug 27, 2020
9b3d307
Remove extra API call
tgolen Aug 27, 2020
5fbde0a
lint
tgolen Aug 27, 2020
6ca354b
Fix report name
tgolen Aug 27, 2020
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
Binary file added assets/images/expensify-logo-round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/icon-x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Expensify.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import PropTypes from 'prop-types';

// import {Beforeunload} from 'react-beforeunload';
import SignInPage from './page/SignInPage';
import HomePage from './page/HomePage/HomePage';
import HomePage from './page/home/HomePage';
import Ion from './lib/Ion';
import * as ActiveClientManager from './lib/ActiveClientManager';
import {verifyAuthToken} from './lib/actions/Session';
Expand Down
24 changes: 12 additions & 12 deletions src/components/AnchorForCommentsOnly/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {StyleSheet} from 'react-native';

/**
* Text based component that is passed a URL to open onPress
Expand Down Expand Up @@ -38,18 +38,18 @@ const AnchorForCommentsOnly = ({
children,
style,
...props
}) => {
// If the style prop is an array of styles, we need to mix them all together
const mergedStyles = !_.isArray(style) ? style : _.reduce(style, (finalStyles, s) => ({
...finalStyles,
...s
}), {});

return (
}) => (
<a
style={StyleSheet.flatten(style)}
href={href}
rel={rel}
target={target}
// eslint-disable-next-line react/jsx-props-no-spreading
<a style={mergedStyles} href={href} rel={rel} target={target} {...props}>{children}</a>
);
};
{...props}
>
{children}
</a>
);

AnchorForCommentsOnly.propTypes = propTypes;
AnchorForCommentsOnly.defaultProps = defaultProps;
Expand Down
19 changes: 5 additions & 14 deletions src/components/AnchorForCommentsOnly/index.native.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {Linking, Text} from 'react-native';
import {Linking, StyleSheet, Text} from 'react-native';

/**
* Text based component that is passed a URL to open onPress
Expand Down Expand Up @@ -40,18 +39,10 @@ const AnchorForCommentsOnly = ({
children,
style,
...props
}) => {
// If the style prop is an array of styles, we need to mix them all together
const mergedStyles = !_.isArray(style) ? style : _.reduce(style, (finalStyles, s) => ({
...finalStyles,
...s
}), {});

return (
// eslint-disable-next-line react/jsx-props-no-spreading
<Text style={[mergedStyles]} onPress={() => Linking.openURL(href)} {...props}>{children}</Text>
);
};
}) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<Text style={StyleSheet.flatten(style)} onPress={() => Linking.openURL(href)} {...props}>{children}</Text>
);

AnchorForCommentsOnly.propTypes = propTypes;
AnchorForCommentsOnly.defaultProps = defaultProps;
Expand Down
87 changes: 87 additions & 0 deletions src/components/TextInputWithFocusStyles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
import {TextInput, StyleSheet} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';

const propTypes = {
// A ref to forward to the text input
forwardedRef: PropTypes.func.isRequired,

// Styles to apply to the text input when it has focus
// eslint-disable-next-line react/forbid-prop-types
styleFocusIn: PropTypes.any,
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 you can:

import {ViewPropTypes} from 'react-native';

and then:

styleFocusIn: PropTypes.arrayOf(ViewPropTypes.style),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I tried that, and ViewPropTypes is undefined... Any idea why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe because it's deprecated and not exported from react-native-web: reactrondev/react-native-web-swiper#41

Copy link
Contributor

Choose a reason for hiding this comment

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

hmmm... maybe PropTypes.instanceOf(StyleSheet) ?

Copy link
Contributor

Choose a reason for hiding this comment

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

NAB if you can't find a good way to typecheck this, but I'd be shocked if there wasn't


// Styles to apply to the text input when it does not have focus
// eslint-disable-next-line react/forbid-prop-types
styleFocusOut: PropTypes.any,

// General styles to apply to the text input
// eslint-disable-next-line react/forbid-prop-types
style: PropTypes.any,

// A function to call when the input has been blurred
onBlur: PropTypes.func.isRequired,

// A function to call when the input has gotten focus
onFocus: PropTypes.func.isRequired,
};
const defaultProps = {
styleFocusIn: null,
styleFocusOut: null,
style: null,
};

class TextInputWithFocusStyles extends React.Component {
constructor(props) {
super(props);

this.state = {
isFocused: false,
};
}

render() {
// Make full objects out of both the style coming from props, and the style we have in the state
const propStyles = StyleSheet.flatten(this.props.style);
const focusedStyle = this.state.isFocused
? StyleSheet.flatten(this.props.styleFocusIn)
: StyleSheet.flatten(this.props.styleFocusOut);

// Merge the two styles together
const style = StyleSheet.compose(propStyles, focusedStyle);

// Omit the props that are used in this intermediary component and only pass down the props that
// are necessary
const propsToPassToTextInput = _.omit(this.props, [
'focusInStyle',
'focusOutStyle',
'onFocus',
'onBlur',
'style',
]);
return (
<TextInput
ref={this.props.forwardedRef}
style={style}
onFocus={() => {
this.setState({isFocused: true});
this.props.onFocus();
}}
onBlur={() => {
this.setState({isFocused: false});
this.props.onBlur();
}}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...propsToPassToTextInput}
/>
);
}
}

TextInputWithFocusStyles.propTypes = propTypes;
TextInputWithFocusStyles.defaultProps = defaultProps;

export default React.forwardRef((props, ref) => (
/* eslint-disable-next-line react/jsx-props-no-spreading */
<TextInputWithFocusStyles {...props} forwardedRef={ref} />
));
2 changes: 1 addition & 1 deletion src/components/withIon.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export default function (mapIonToState) {
}
}

withIon.displayName = `WithIon(${getDisplayName(WrappedComponent)})`;
withIon.displayName = `withIon(${getDisplayName(WrappedComponent)})`;
return withIon;
};
}
93 changes: 93 additions & 0 deletions src/lib/KeyboardShortcut/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import _ from 'underscore';

const events = {};

/**
* Checks if an event for that key is configured and if so, runs it.
* @param {Event} event
*/
function bindHandlerToKeyupEvent(event) {
if (events[event.keyCode] === undefined) {
return;
}

// The active callback is the last element in the array
const eventCallbacks = events[event.keyCode];
const callback = eventCallbacks[eventCallbacks.length - 1];

const pressedModifiers = _.all(callback.modifiers, (modifier) => {
if (modifier === 'shift' && !event.shiftKey) {
return false;
}
if (modifier === 'control' && !event.ctrlKey) {
return false;
}
if (modifier === 'alt' && !event.altKey) {
return false;
}
if (modifier === 'meta' && !event.metaKey) {
return false;
}
return true;
});

if (!pressedModifiers) {
return;
}

// If configured to do so, prevent input text control to trigger this event
if (!callback.captureOnInputs && (
event.target.nodeName === 'INPUT'
|| event.target.nodeName === 'TEXTAREA'
|| event.target.contentEditable === 'true'
)) {
return;
}

if (_.isFunction(callback.callback)) {
callback.callback(event);
}
event.preventDefault();
}

// Make sure we don't add multiple listeners
document.removeEventListener('keydown', bindHandlerToKeyupEvent);
document.addEventListener('keydown', bindHandlerToKeyupEvent);
roryabraham marked this conversation as resolved.
Show resolved Hide resolved

/**
* Module storing the different keyboard shortcut
*
* We are using a push/pop model where new event are pushed at the end of an
* array of events. When the event occur, we trigger the callback of the last
* element. This allow us to replace shortcut from a page to a dialog without
* having the page having to handle that logic.
*
* This is also following the convention of the PubSub module.
* The "subClass" is used by pages to bind /unbind with no worries
*/
const KeyboardShortcut = {
/**
* Subscribes to a keyboard event.
* @param {string} key The key code to watch
* @param {function} callback The callback to call
* @param {string|array} modifiers Can either be shift or control
* @param {bool} captureOnInputs Should we capture the event on inputs too?
*/
subscribe(key, callback, modifiers = 'shift', captureOnInputs = false) {
const keyCode = key.charCodeAt(0);
if (events[keyCode] === undefined) {
events[keyCode] = [];
}
events[keyCode].push({callback, modifiers: _.isArray(modifiers) ? modifiers : [modifiers], captureOnInputs});
},

/**
* Unsubscribes to a keyboard event.
* @param {int} keyCode The key code to stop watching
*/
unsubscribe(keyCode) {
delete events[keyCode];
}
};

export default KeyboardShortcut;
10 changes: 10 additions & 0 deletions src/lib/KeyboardShortcut/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* This is a no-op component for native devices because they wouldn't be able to support keyboard shortcuts like
* a website.
*/
const KeyboardShortcut = {
subscribe() {},
unsubscribe() {},
};

export default KeyboardShortcut;
11 changes: 11 additions & 0 deletions src/lib/Str.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ const Str = {
generateDeviceLoginID() {
return `react-native-chat-${Guid()}`;
},

/**
* Escapes all special RegExp characters from a string
*
* @param {String} string The subject
*
* @returns {String} The escaped string
*/
escapeForRegExp(string) {
return string.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
},
};

export default Str;
2 changes: 1 addition & 1 deletion src/lib/actions/PersonalDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function fetch() {
// Get my personal details so they can be easily accessed and subscribed to on their own key
myPersonalDetails = allPersonalDetails[currentLogin] || {};

return Ion.set(IONKEYS.PERSONAL_DETAILS, allPersonalDetails);
return Ion.merge(IONKEYS.PERSONAL_DETAILS, allPersonalDetails);
})
.then(() => Ion.merge(IONKEYS.MY_PERSONAL_DETAILS, myPersonalDetails))
.catch((error) => {
Expand Down
Loading