-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good so far! Just left some initial comments mostly around the ChatSwitcher
which I'm guessing has some copy/paste stuff going on
|
||
// All of the personal details for everyone | ||
// Eslint is disabled here because personalDetails is an object where they keys are email addresses | ||
// so it can't be documented in proptypes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's what PropTypes.objectOf()
is for
constructor(props) { | ||
super(props); | ||
|
||
this.maxSearchResults = 10; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a constant can we move it outside the component?
* @param {number} oldFocusedIndex | ||
* @param {number} newFocusedIndex | ||
*/ | ||
switchFocusedIndex(oldFocusedIndex, newFocusedIndex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we could track the focused index in state
and re-render the options. Seems simpler to do that vs. having an option with a focused
property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. if index === this.state.focusedIndex
do whatever we need to do on render or pass focused
as a prop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, the web-e component puts this in the state too, and I actually felt it was better to leave it out of the state. If you look at that component, there is a bunch of stuff in state, and I was trying to clean that up. However, I think putting it into state will be fine and I'd like to keep it closely aligned with web-e.
} | ||
|
||
/** | ||
* Every time the text changes in the TextInput, update the search value in the state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does a lot more than this
* @param {string} value | ||
*/ | ||
updateSearch(value) { | ||
this.setState({search: value}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this setState
call be combined with the one below?
new RegExp(Str.escapeForRegExp(value), 'i'), | ||
]; | ||
|
||
// Use a Set so that duplicate values are automatically removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure this statement is false if your Set
values are objects. You could use a Map
and use something unique like the login
as the key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, you're right. I thought this cleared up a bug, but maybe it was something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I was presuming that we would not want to add two objects that had the same shape and values. But maybe that is wrong?
|
||
// Set the first option to be focused | ||
if (options.length) { | ||
options[0].focused = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More extra logic needed to accommodate the focused
property
ref={el => this.textInput = el} | ||
style={[styles.textInput, styles.textInputReversed, styles.flex1]} | ||
value={this.state.search} | ||
onBlur={() => this.state.search === '' && this.props.onBlur()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting. But I think might look cleaner with brackets and a if
for early return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High-level, I have three main comments here:
- It's not compatible with desktop, but fixing those issues to get React Native to play nice with Electron might be challenging, and probably warrants its own issue/PR. That being said, I do think it's doable: there's an early-stage npm project addressing some of the challenging pieces, such as hotkeys, links, dialogs/alerts, and clipboard. While it doesn't offer stability yet, it's open-source I'm sure the developer would welcome PR's. If we don't like that idea, maybe we could also just work-around it by having separate files when we need to use some Electron-specific code for desktop (i.e:
index.desktop.js
) - Beyond the actual functionality, the download links need to detect the current platform to show the other three.
- There were a handful of instances where you used underscore functions for which there is a native-javascript equivalent, and I think it might be best to stick with plain JS whenever convenient. In some cases I think it's more performant too, i.e: not extending an object with all the underscore methods like
_(myObject)
, just to use one function.
src/lib/KeyboardShortcut/index.js
Outdated
let cb = events[event.keyCode]; | ||
cb = cb[cb.length - 1]; | ||
|
||
const pressedModifiers = _.all(cb.modifiers, (modifier) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: AFAIK we don't have style guide recommendations on this matter, but I would prefer the pure-js alternative to underscore:
cb.modifiers.every((modifier) => { ... });
src/lib/KeyboardShortcut/index.js
Outdated
} | ||
|
||
// The active callback is the last element in the array | ||
let cb = events[event.keyCode]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer const
:
const cb = _.last(events[event.keyCode]);
|
||
const propTypes = { | ||
// Safe area insets required for mobile devices margins | ||
// eslint-disable-next-line react/forbid-prop-types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could do this (see here):
insets: PropTypes.objectOf(PropTypes.shape(
top: PropTypes.number,
left: PropTypes.number,
right: PropTypes.number,
bottom: PropTypes.number,
)),
But this prop is drilled down through several layers, so another alternative that I've used in the past is saving some special or complex prop types that are reused multiple times into a file/module of their own (usually just a directory called types and export a PropType object from each file). Not sure whether that's a good choice here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call
I think you should include the Android link at least, because on a desktop
web browser if you click through to an Android app, you can trigger it to
be installed onto your Android phone even from a desktop browser.
…On Sun, Aug 23, 2020, 9:46 PM Rory Abraham ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/page/home/sidebar/AppLinks.js
<#282 (comment)>
:
> +import Anchor from '../../../components/Anchor';
+
+const AppLinks = () => {
+ // Give instructions for downloading the desktop app
+ function alertInstallInstructions() {
+ alert('To install Chat Desktop App:\n\n'
+ + '1. Double click "Chat.app.zip"\n'
+ + '2. If you get a "Cannot be opened because the developer cannot be verified" error:\n'
+ + ' 1. Hit Cancel\n'
+ + ' 2. Go to System Preferences > Security & Privacy > General\n'
+ + ' 3. Click Open Anyway at the bottom\n'
+ + ' 4. When it re-prompts you, click Open');
+ Linking.openURL('https://chat.expensify.com/Chat.app.zip');
+ }
+
+ return (
Side note: does it make much sense for a desktop app to include links to
iOS/Android? Probably not. I think we should only show this on web, noop it
for desktop (we're already nooping it for iOS/Android)
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#282 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEMNUVQZAVC4YWPBEFAEZ3SCHWATANCNFSM4QHVQD5A>
.
|
Updated with conflic fixes and improvements from Marc's review. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, just the one PropTypes concern!
|
||
// Styles to apply to the text input when it has focus | ||
// eslint-disable-next-line react/forbid-prop-types | ||
styleFocusIn: PropTypes.any, |
There was a problem hiding this comment.
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),
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, meant to request the small typechecking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing to NAB
LGTM and doesn't seem to be any blockers now so I am merging. |
Bump JS-Libs version for PR #282
Fixes #269
Tests
Things left to do