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
#3118 add persistent properties to replay search page #3166
base: develop
Are you sure you want to change the base?
#3118 add persistent properties to replay search page #3166
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.
Instead of passing in a property to the add*Filter methods and adding setters to the Filter controllers it would be better to instead expose the relevant value properties of the Filter controllers with appropriate getters and setter so that the creators can then do with as they choose.
Additionally in the vaultPrefs object it is probably better to create sub objects like ReplaySearchPrefs to represent the search 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.
One thing we should probably additionally do is set a max date range for the replays. We currently initialize the range to be one year from today due to server load and probably don't want the persistence to undo that.
private final ObjectProperty<ObservableList<String>> featuredModFilter = new SimpleObjectProperty<ObservableList<String>>( | ||
FXCollections.emptyObservableList()); | ||
private final ObjectProperty<ObservableList<String>> leaderboardFilter = new SimpleObjectProperty<ObservableList<String>>( | ||
FXCollections.emptyObservableList()); |
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.
These should be ListProperties
public javafx.beans.property.DoubleProperty lowValueProperty() { | ||
return rangeSlider.lowValueProperty(); | ||
} | ||
|
||
public javafx.beans.property.DoubleProperty highValueProperty() { | ||
return rangeSlider.highValueProperty(); | ||
} |
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.
import DoubleProperty instead of using the full name
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 one I had to write the full name as DoubleProperty clashes with the existing com.github.rutledgepaulv.qbuilders.properties.concrete.DoubleProperty import
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.
got it I missed that
public javafx.beans.property.StringProperty textFieldProperty() { | ||
return textField.textProperty(); | ||
} |
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.
import instead of full name
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 here as well
ReplaySearchPrefs replaySearchPrefs = vaultPrefs.getReplaySearch(); | ||
TextFilterController textFilterController = searchController.addTextFilter("playerStats.player.login", i18n.get("game.player.username"), true); | ||
textFilterController.textFieldProperty().bindBidirectional(replaySearchPrefs.playerNameFieldProperty()); | ||
textFilterController = searchController.addTextFilter("mapVersion.map.displayName", i18n.get("game.map.displayName"), false); | ||
textFilterController.textFieldProperty().bindBidirectional(replaySearchPrefs.mapNameFieldProperty()); | ||
textFilterController = searchController.addTextFilter("mapVersion.map.author.login", i18n.get("game.map.author"), false); | ||
textFilterController.textFieldProperty().bindBidirectional(replaySearchPrefs.mapAuthorFieldProperty()); | ||
textFilterController = searchController.addTextFilter("name", i18n.get("game.title"), false); | ||
textFilterController.textFieldProperty().bindBidirectional(replaySearchPrefs.titleFieldProperty()); | ||
textFilterController = searchController.addTextFilter("id", i18n.get("game.id"), true); | ||
textFilterController.textFieldProperty().bindBidirectional(replaySearchPrefs.replayIDFieldProperty()); |
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.
Rather than binding bidirectionally we should just set the initial value and then bind the searchPrefs property to the filter property. Additionally since the replaySearch properties are long lived we need to limit the binding to the lifetime of the VaultController by adding .when(showing). This will cause the binding to automatically stop whenever the vault is not showing. This will prevent memory leaks
replaySearchPrefs.featuredModFilterProperty().get().stream().forEach((item) -> featuredModFilterController.checkItem(item)); | ||
replaySearchPrefs.featuredModFilterProperty().bind(Bindings.createObjectBinding(() -> featuredModFilterController.getCheckedItems())); |
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 a utility method Bindings.bindContent that can be used here. Although we have to be more careful about unbinding when the vault controller is closed. This could be easily done by adding the unsubscription with the addSubscription method of the controller
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 dont understand whats needed about unbinding
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.
ahh, to prevent memory leaks as above
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 don't understand how to add an unsubscription
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 a method addShownSubscription that you add a subscripton to where a subscription is just a function interface. So just pass in a function that performs the unbinding
RangeFilterController rangeFilterController = searchController.addRangeFilter("playerStats.ratingChanges.meanBefore", i18n.get("game.rating"), | ||
MIN_RATING, MAX_RATING, 10, 4, 0, value -> value + 300); | ||
rangeFilterController.lowValueProperty().bindBidirectional(replaySearchPrefs.ratingMinProperty()); | ||
rangeFilterController.highValueProperty().bindBidirectional(replaySearchPrefs.ratingMaxProperty()); | ||
|
||
searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"),0, 5, 10, 4, 1); | ||
rangeFilterController = searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"),0, 5, 10, 4, 1); | ||
rangeFilterController.lowValueProperty().bindBidirectional(replaySearchPrefs.averageReviewScoresMinProperty()); | ||
rangeFilterController.highValueProperty().bindBidirectional(replaySearchPrefs.averageReviewScoresMaxProperty()); | ||
|
||
searchController.addDateRangeFilter("endTime", i18n.get("game.date"), 1); | ||
searchController.addRangeFilter("replayTicks", i18n.get("game.duration"), 0, 60, 12, 4, 0, value -> (int) (value * 60 * 10)); | ||
searchController.addToggleFilter("validity", i18n.get("game.onlyRanked"), "VALID"); | ||
DateRangeFilterController dateRangerFilterController = searchController.addDateRangeFilter("endTime", i18n.get("game.date"), 1); | ||
dateRangerFilterController.beforeDateProperty().bindBidirectional(replaySearchPrefs.gameBeforeDateProperty()); | ||
dateRangerFilterController.afterDateProperty().bindBidirectional(replaySearchPrefs.gameAfterDateProperty()); |
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 binding comments as above
Any ideas on how you want this to look? |
Yeah I was thinking it would either be defaulting the preferences everytime or setting a limit on the range controller. Setting a limit might be easier. |
…fix state issue with category filter and add test
.subscribe((mods) -> { | ||
featuredModFilterController.setItems(mods); | ||
replaySearchPrefs.featuredModFilterProperty().forEach((item) -> featuredModFilterController.checkItem(item)); | ||
Bindings.bindContent(replaySearchPrefs.featuredModFilterProperty(), featuredModFilterController.getCheckedItems()); |
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 would do the actual binding of the content in the onShow method as well to ensure if it is made visible and invisible the binding will still be there
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 we do this then there is a race condition where the binding will sometimes get set to the empty controller items before checked items getting set here, therefore overwriting the preferences.
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 could create a copy of the list during initialization and use that in the subscribe.
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 dont love 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.
Well as it is right now there are cases where the selection would not be saved to the prefs
.subscribe((leaderboards) -> { | ||
leaderboardFilterController.setItems(leaderboards); | ||
replaySearchPrefs.leaderboardFilterProperty().forEach((item) -> leaderboardFilterController.checkItem(item)); | ||
Bindings.bindContent(replaySearchPrefs.leaderboardFilterProperty(), leaderboardFilterController.getCheckedItems()); |
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 binding comment above
The code looks good but there are some failing tests |
Adding a null check is not the way to fix the tests. Rather you need to properly mock the filter builder in the test code so it returns mock versions of the controllers |
There is still a minor issue, if no preferences have been set (i.e. empty client.prefs file) the range slider low and high values default to 0 |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #3166 +/- ##
=============================================
- Coverage 58.83% 58.34% -0.50%
- Complexity 3984 4021 +37
=============================================
Files 576 579 +3
Lines 19296 19585 +289
Branches 1022 1030 +8
=============================================
+ Hits 11353 11426 +73
- Misses 7447 7658 +211
- Partials 496 501 +5
... and 14 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
In that case if null would be better we can make them Object property and that will be nullable |
#3119
Every field except for the game date filter when searching are now remembered after closing/opening the client and navigating away/to the replay search page.