Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Convert the FilePatchController hierarchy to React #617

Merged
merged 31 commits into from
Apr 1, 2017

Conversation

smashwilson
Copy link
Contributor

@smashwilson smashwilson commented Mar 21, 2017

Continuing the path in #381. I'm also starting to convert the selection models to be immutable along the way to avoid a bunch of this.selection.selectItem(); this.forceUpdate(); awkwardness.

Stuff I don't want to forget to do, in this PR or follow-on ones:

  • 🔥 the registerHunkView props; Enzyme selectors work much more cleanly.
  • PaneItem Portal stuff
    • Unit test that fails when the PaneItem doesn't render and mount properly in the workspace
    • Introduce a Portal.getView() method that returns a Proxy that (a) returns the root DOM node of the portal'd content for ViewRegistry purposes, and (b) acts like the root component instance for all other methods to support getTitle(), onDidDestroy, and so on.
  • Deal with this warning:
    warning.js:36 Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `metaKey` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See https://fb.me/react-event-pooling for more information.
    

@smashwilson smashwilson requested a review from kuychaco March 28, 2017 19:56
@smashwilson
Copy link
Contributor Author

No test failures here, move along...

Jedi mind trick

Copy link
Contributor

@BinaryMuse BinaryMuse left a comment

Choose a reason for hiding this comment

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

Mostly just scanned through the tests portion of this, but they looked good. Some comments below. Great job on this! 🎉

@kuychaco reviewed with me so this can count for both of us ;)

hunks={hunks}
filePath={filePath}
stagingStatus={this.props.stagingStatus}
isPartiallyStaged={this.props.isPartiallyStaged}
registerHunkView={this.props.registerHunkView}
Copy link
Contributor

Choose a reason for hiding this comment

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

Very happy to see we can drop this 🙇

lib/helpers.js Outdated

export function forceUpdatePromise(component) {
return new Promise(resolve => component.forceUpdate(resolve));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Apparently not! 🔥

export default class FilePatchView {
constructor(props) {
this.props = props;
this.selection = new FilePatchSelection(this.props.hunks);
this.mouseSelectionInProgress = false;

window.addEventListener('mouseup', this.mouseup);
this.disposables = new CompositeDisposable();
this.disposables.add(new Disposable(() => window.removeEventListener('mouseup', this.mouseup)));
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably move the event handler stuff to componentDidMount - not sure why this was listed above etch.initialize(this) in the old version.

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, that's better; done.

});
} else if (mode === 'line' && !this.state.selection.getSelectedLines().has(line)) {
await setStatePromise(prevState => {
return {selection: prevState.selection.selectLine(line, event.shiftKey)};
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have an idea of the performance characteristics of the immutable data models compared to the mutable version for large diffs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the React docs this should move us toward being able to use PureComponent which will prevent unnecessary rendering when the selection hasn't changed. I haven't, like, measured it, though.

I was cautious to keep ListSelection.copy() from doing an O(n) copy of the items Array. Right now it is linear with respect to the number of non-coalesced selections, but once ListSelection is immutable, too, it should be able to do a shallow-copy of that as well and be nice and snappy in that regard.

this.selection.selectLine(hunkLines[hunkLines.length - 1], true);

this.mouseSelectionInProgress = true;
event.persist && event.persist();
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this event need to be persisted? I don't see any async operations in which it's used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does! this.setState can be asynchronous, although it never is in enzyme. I only knew about this because it was throwing warnings in the console telling me to do this 😉

stageOrUnstageAll() {
this.selectAll();
async stageOrUnstageAll() {
await this.selectAll();
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be async?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does because the setState call in selectAll can be asynchronous, which means that the this.didConfirm() call would fire before the selection is updated.

Buuut it turns out that selectAll() wasn't returning a Promise any more, so I should probably fix that 😉

@@ -65,7 +65,14 @@ export default class PaneItem extends React.Component {
componentWillUnmount() {
this.subscriptions && this.subscriptions.dispose();
if (this.paneItem && !this.didCloseItem) {
this.paneItem.destroy();
if (this.paneItem.destroy) {
this.paneItem.destroy && this.paneItem.destroy();
Copy link
Contributor

@BinaryMuse BinaryMuse Mar 30, 2017

Choose a reason for hiding this comment

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

There's a duplicate check for destroy here

} else {
const activePane = this.props.workspace.getActivePane();
if (activePane) {
activePane.destroyItem(this.paneItem);
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm, am I wrong in my understanding that we can call destroyItem here and the pane item will have its destroy method called?

Also, can we assume that getting the active pane will be correct? Or should we look for the item in all panes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hah. This is actually me not understanding how Pane.addItem() works and forgetting to implement onDidDestroy on FilePatchController. Oops.

@@ -76,4 +76,31 @@ export default class Portal extends React.Component {
getElement() {
return this.node;
}

getView() {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

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 was wondering what you'd think about this one 😁

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, I figured I'd make the React-compatible getItem version the default, since we're moving everything to React.

new HunkLine('line-7', 'added', -1, 8),
new HunkLine('line-8', 'added', -1, 9),
new HunkLine('line-7', 'added', -1, 8), //
new HunkLine('line-8', 'added', -1, 9), //
Copy link
Contributor

Choose a reason for hiding this comment

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

You forgot your //

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops 😅

@smashwilson smashwilson dismissed BinaryMuse’s stale review April 1, 2017 16:40

Changes made. Thanks 💯 for the review :-D

@smashwilson smashwilson removed the request for review from kuychaco April 1, 2017 16:41
@smashwilson smashwilson merged commit a2a1c61 into master Apr 1, 2017
@smashwilson smashwilson deleted the aw-file-patch-to-react branch April 1, 2017 16:51
@maxbrunsfeld
Copy link
Contributor

It looks like this caused an issue with the file patch view's height; I can't scroll vertically in the file patch view anymore.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants