-
Notifications
You must be signed in to change notification settings - Fork 394
Match vision tab overhaul #469
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
Conversation
remove the usage of redux's Connect from the vision page by using the PlainTable instead of the default Table (TableWithOptions). there are still performance issues, buut it is usable.
i've decided to not use a <table> anymore to host this data as it seems the material-ui table causes some issues with transitions and animating table row is hacky at best a simple <ul> implementation seems to be more performant
# Conflicts: # src/components/Match/Match.css # src/components/Match/VisionMap.jsx # src/components/Match/matchColumns.jsx # src/components/TabBar/TabBar.jsx # src/components/Table/Table.css # src/components/Table/Table.jsx # src/components/Visualizations/Table/HeroImage.jsx # webpack.config.js
enabledIndex: {}, | ||
}); | ||
shouldComponentUpdate(newProps) { | ||
if (newProps.wardsLog.length == this.props.wardsLog.length) return false; |
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.
use ===
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 boolean so you can return it directly
enabledIndex: {}, | ||
}); | ||
shouldComponentUpdate(newProps) { | ||
if (newProps.wardsLog.length == this.props.wardsLog.length) return false; |
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 boolean so you can return it directly
this.updateMap = this.updateMap.bind(this); | ||
|
||
componentDidMount() { | ||
window.addEventListener("resize", () => this.forceUpdate()); |
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 @mike-robertson will advise that you use redux-responsive's state.browser.width for this
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.
Let's iterate on this once the component is shipped.
class PlayerFilter extends React.PureComponent { | ||
constructor(props) { | ||
super(props); | ||
this.getObserverCount = () => this.props.player.obs_log.length; |
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'd advise making these functions outside the class, and take props as a parameter.
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 component will be refactored.
<Col xs> | ||
<Button {...this.getMuiThemeProps()} | ||
label={obs_count} | ||
disabled={obs_count == 0} |
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.
use !obs_count
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.
Will do
backgroundColor={this.generateFilterKey("observer") in this.props.activeFilters ? filterOff : filterOn} | ||
style={{opacity: obs_count > 0 ? opacityOn : opacityOff}} | ||
onClick={() => onFilterClick(this.generateFilterKey("observer"), player.player_slot, "observer")} | ||
icon={<Avatar size={24} src="http://a19a1164.ngrok.io/apps/dota2/images/items/ward_observer_lg.png" />} |
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.
use API_HOST
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.
Prototype left-overs.
|
||
class VisionPage extends React.Component { | ||
componentWillMount() { | ||
Perf.start(); |
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.
should this be disabled for production?
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.
Isn't react-addons-perf already doing this under the hood?
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.
Could be removed.
return _.rangeStep(interval, 0, this.props.match.duration); | ||
} | ||
|
||
_handleViewportChange(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.
I think eslint will complain about the use of underscores since javascript doesn't really have any idea of private properties.
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.
Yeah that was to test quickly the debounce, I'll figure out a better name
|
||
// a simple functor that will call the correct function depending on value | ||
const threshold = _.curry((start, limits, values, value) => { | ||
if (limits.length != values.length) throw "Limits must be the same as functions."; |
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.
I need to extract this functor in utility
} | ||
}; | ||
|
||
export const Pure = (Component) => class extends React.PureComponent { |
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.
what's the difference between this and a function component?
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 is no difference, it just reads better when using an existing component.
const PureRow = Pure(Row);
@@ -0,0 +1,16 @@ | |||
import React from 'react'; | |||
|
|||
export const Fixed = (Component) => class extends React.Component { |
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.
Doesn't the Redux store take care of this kind of comparison already?
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.
The Vision filters are in the local state r/n.
# Conflicts: # src/components/Match/VisionMap.jsx # src/utility/index.jsx
I must lint this. |
I think this is ready to ship. |
Reviewers: |
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.
Also it wont work for me VisionPage.jsx?6a5e:62 - Uncaught TypeError: Cannot assign to read only property 'key' of object '#<Object>'
@@ -1,5 +1,16 @@ | |||
@import "../palette.css"; | |||
|
|||
:root { | |||
--ward-log-height: 50px; | |||
--slider-ticks-color: #757575; |
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 guess this colors should be defined in palette.css
"material-ui": "0.16.1", | ||
"moment-duration-format": "^1.3.0", |
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.
Do we need this new 7 dependencies?
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.
Let me explain why those have been added:
- Lodash: I don't feel like implementing my how flow() to compose my filters. I suggest we use the 'lodash/fp' import that is more geared toward functional programming. (I also use: inRange, sortByIndex, map/filter).
- moment-duration-format: I don't think I'm using it, so I guess I'll remove it. Good catch.
- react-addons-css-transition-group: de facto transition component for React
- react-measure: This is to deal with the resizing of the page. @howardchung suggested I use the Redux store size, but I don't see how that can integrate with the flexgrid sizing. @mike-robertson you are welcome to comment.
- seamless-immutable: okay that is a subject of its own, but this manage immutable Object/Array and make it so deeply nested values can be compared with a simple reference equality check. Having immutable objects in the Redux store is considered a good practice.
- react-addons-perf: That is very helpful to dig down React rendering and find performance issues
- react-perf-tool: I think that expose it to the Chrome extension
@@ -148,6 +159,117 @@ | |||
vertical-align: middle; | |||
} | |||
|
|||
.ward-log { | |||
width: 100%; | |||
font-size: .8em; |
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.
We have some defined font sizes in palette.css. I guess 0.8em is somehing like 12px so u can use --fontSizeSmall
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'll oblige but I think that using px as font size is a bad practice for mobility. I'd rather leave it that way or refactor to use em/rem all around.
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.
We should link it to --fontSizeSmall then convert that em then
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.
👍 Some things that we should fix someday:
- team headings
<Heading title.. icon../>
ex: https://github.com/odota/ui/blob/master/src/components/Match/matchPages.jsx#L55 - wrap this into table (maybe) and fix red
Also it can be done in this pr
const [opacityOn, opacityOff] = [1, 0.4]; | ||
return ( | ||
<Row | ||
className={styles['filter-row']} |
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.
We use camelcase in the css files so we don't need to do things like this. I know camelCase isn't standard css, but for css-modules, it is.
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.
Will do
}); | ||
} | ||
|
||
generateFilterKey(type) { |
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.
don't you need to bind this in the constructor so you don't lose 'this' context?
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 could be static.
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.
Doesn't seems so.
{props.ticks.map((tick) => { | ||
const [t, min, max] = [tick, props.min, props.max]; | ||
const percent = 100 * ((t - min) / (max - min)); | ||
const cls = [styles['slider-tick']]; |
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.
use camelCase to avoid this
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.
Will do
); | ||
|
||
const PipelineFilter = (filters, data, iter = Array.prototype.filter) => { | ||
const filtered = filters.map(f => iter.call(data, f)) |
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.
const filteredData = filters.reduce(
(arr, filt) => arr.filter(filt),
data
);
return data.filter(obj => !filteredData.includes(obj));
Doesn't this do the same thing but is easier to understand? Plus no lodash, and you don't need to have a reference to the Array.prototype.filter function.
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'm not 100% sure the implementation is does exactly the same thing.
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 can remove the prototype reference.
this.ticks = this.computeTick(); | ||
this.findPivot = value => _.flow(_.map(x => x.entered.time), | ||
_.sortedIndex(value))(this.state.wardsLog); | ||
this.handleViewportChange = _.debounce(50, this.viewportChange); |
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.
why not just do:
this.handleViewportChange = () => setTimeout(this.viewportChange, 50);
No reason to drag in a third party library for that, right?
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 not debouncing, this is deferring. You'll still call on every change event, 50ms after the call stack has been emptied.
|
||
const observers = extractWardLog('observer', player.obs_log, player.obs_left_log); | ||
const sentries = extractWardLog('sentry', player.sen_log, player.sen_left_log); | ||
return _.concat(observers, sentries); |
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.
observers.concat(sentries)
_.flatten, | ||
_.sortBy(xs => xs.entered.time), | ||
imap((x, i) => ({ ...x, key: i })), | ||
); |
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.
If this can avoid using lodash, I would prefer that, but I don't know exactly what 'convert' is doing here. It seems weird to me that we would need to flatten this array one level, but I don't know what the data looks like.
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.
The fp flavor cap the iteratee to a certain arrity, this removes that cap. It is basically to have the index in the iteratee function.
newPlayer.objective_damage[identifier] + player.damage[key] : | ||
player.damage[key]; | ||
newPlayer.objective_damage[identifier] + player.damage[key] : | ||
player.damage[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.
not sure I am a fan of this tabbing, but if others are I won't fight it.
@@ -360,3 +373,12 @@ export function unpackPositionData(input) { | |||
} | |||
return input; | |||
} | |||
|
|||
export const threshold = _.curry((start, limits, values, 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.
Same comment here, you could just invoke this function like threshold()()()() and manually curry the function definition but it you are against doing that (so you can do threshold(1, 2, 3)(4)), I'll defer to your judgement.
|
||
const limitsWithStart = limits.slice(0); | ||
limitsWithStart.unshift(start); | ||
return findLast(values, (v, i) => _.inRange(limitsWithStart[i], limitsWithStart[i + 1], 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.
useful method, but I don't know if it is worth bringing in all of lodash for. Though if we can get away with the 4kb gzipped lodash and you want to use these, I'm down with that. I'm mostly against using lodash when we could instead use native implementations at essentially no extra cost.
will this integrate #445 ? |
Let's merge this first then iterate on it if you guys don't mind. #445 is easy to add after. |
I didn't remove lodash usage but I tried to use native methods when available and not changing the implementation. |
}) | ||
; | ||
|
||
const observers = extractWardLog('observer', player.obs_log, player.obs_left_log); |
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.
will this crash if !player.obs_log
(unparsed match?)
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 fixed this, I really thought this was handled up in the Component hierarchy.
Match vision tab overhaul
fixes #342
This PR addresses #342 and will add a bunch of new features to the Vision Tab, including:
Keep in mind that this is a work in progress.
I've also refactored certain components while working on this page.
There are still refactoring/extracting that needs to be done.
Comments welcome.