From 61096142d5dc07a7738f5f2daa57c998f9b096a4 Mon Sep 17 00:00:00 2001 From: Burning_TNT <88144530+burningtnt@users.noreply.github.com> Date: Sat, 20 Jul 2024 05:03:08 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20#3065:=20=E4=BF=AE=E5=A4=8D=20CurseForge?= =?UTF-8?q?=20=E6=90=9C=E7=B4=A2=20API=20=E5=AF=B9=E7=BF=BB=E9=A1=B5?= =?UTF-8?q?=E6=80=BB=E9=87=8F=E8=AE=A1=E7=AE=97=E4=B8=8D=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E7=9A=84=20Bug=20(#3066)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #3065 * Fix * Fix. --- .../hmcl/ui/versions/DownloadListPage.java | 116 +++++++++++------- .../curse/CurseForgeRemoteModRepository.java | 8 +- .../modrinth/ModrinthRemoteModRepository.java | 2 +- 3 files changed, 82 insertions(+), 44 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index 46df504b1..ceb0c9ed3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -43,7 +43,6 @@ import org.jackhuang.hmcl.mod.RemoteModRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.WeakListenerHolder; @@ -52,6 +51,7 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.AggregatedObservableList; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.i18n.I18n; @@ -64,10 +64,10 @@ import java.util.Locale; import java.util.Optional; import java.util.stream.Collectors; +import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; -import static org.jackhuang.hmcl.ui.FXUtils.runInFX; public class DownloadListPage extends Control implements DecoratorPage, VersionPage.VersionLoadable { protected final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); @@ -87,7 +87,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP protected final ListProperty downloadSources = new SimpleListProperty<>(this, "downloadSources", FXCollections.observableArrayList()); protected final StringProperty downloadSource = new SimpleStringProperty(); private final WeakListenerHolder listenerHolder = new WeakListenerHolder(); - private TaskExecutor executor; + private int searchID = 0; protected RemoteModRepository repository; private Runnable retrySearch; @@ -163,11 +163,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP setLoading(true); setFailed(false); - if (executor != null && !executor.isCancelled()) { - executor.cancel(); - } - - executor = Task.supplyAsync(() -> { + int currentSearchID = searchID = searchID + 1; + Task.supplyAsync(() -> { Profile.ProfileVersion version = this.version.get(); if (StringUtils.isBlank(version.getVersion())) { return userGameVersion; @@ -176,9 +173,11 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP ? version.getProfile().getRepository().getGameVersion(version.getVersion()).orElse("") : ""; } - }).thenApplyAsync(gameVersion -> - repository.search(gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC) - ).whenComplete(Schedulers.javafx(), (result, exception) -> { + }).thenApplyAsync(gameVersion -> repository.search(gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC)).whenComplete(Schedulers.javafx(), (result, exception) -> { + if (searchID != currentSearchID) { + return; + } + setLoading(false); if (exception == null) { items.setAll(result.getResults().collect(Collectors.toList())); @@ -196,7 +195,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP return i18n("curse.category." + category); } - protected String getLocalizedCategoryIndent(ModDownloadListPageSkin.CategoryIndented category) { + private String getLocalizedCategoryIndent(ModDownloadListPageSkin.CategoryIndented category) { return StringUtils.repeats(' ', category.indent * 4) + (category.getCategory() == null ? i18n("curse.category.0") @@ -344,13 +343,14 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP sortComboBox.getSelectionModel().select(0); searchPane.addRow(rowIndex++, new Label(i18n("mods.category")), categoryStackPane, new Label(i18n("search.sort")), sortStackPane); - StringProperty previousSearchFilter = new SimpleStringProperty(this, "Previous Seach Filter", ""); + IntegerProperty filterID = new SimpleIntegerProperty(this, "Filter ID", 0); + IntegerProperty currentFilterID = new SimpleIntegerProperty(this, "Current Filter ID", -1); EventHandler searchAction = e -> { - if (!previousSearchFilter.get().equals(nameField.getText())) { + if (currentFilterID.get() != filterID.get()) { control.pageOffset.set(0); } + currentFilterID.set(filterID.get()); - previousSearchFilter.set(nameField.getText()); getSkinnable().search(gameVersionField.getSelectionModel().getSelectedItem(), Optional.ofNullable(categoryComboBox.getSelectionModel().getSelectedItem()) .map(CategoryIndented::getCategory) @@ -360,60 +360,93 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP sortComboBox.getSelectionModel().getSelectedItem()); }; + control.listenerHolder.add(FXUtils.observeWeak( + () -> filterID.set(filterID.get() + 1), + + control.downloadSource, + gameVersionField.getSelectionModel().selectedItemProperty(), + categoryComboBox.getSelectionModel().selectedItemProperty(), + nameField.textProperty(), + sortComboBox.getSelectionModel().selectedItemProperty() + )); + HBox actionsBox = new HBox(8); GridPane.setColumnSpan(actionsBox, 4); actionsBox.setAlignment(Pos.CENTER); { AggregatedObservableList actions = new AggregatedObservableList<>(); + Holder changeButton = new Holder<>(); + JFXButton firstPageButton = FXUtils.newBorderButton(i18n("search.first_page")); firstPageButton.setOnAction(event -> { control.pageOffset.set(0); + changeButton.value.run(); searchAction.handle(event); }); - firstPageButton.setDisable(true); - control.pageCount.addListener((observable, oldValue, newValue) -> firstPageButton.setDisable(control.pageCount.get() == -1)); JFXButton previousPageButton = FXUtils.newBorderButton(i18n("search.previous_page")); previousPageButton.setOnAction(event -> { - if (control.pageOffset.get() > 0) { - control.pageOffset.set(control.pageOffset.get() - 1); + int pageOffset = control.pageOffset.get(); + if (pageOffset > 0) { + control.pageOffset.set(pageOffset - 1); + changeButton.value.run(); searchAction.handle(event); } }); - previousPageButton.setDisable(true); - control.pageOffset.addListener((observable, oldValue, newValue) -> previousPageButton.setDisable( - control.pageCount.get() == -1 || control.pageOffset.get() == 0 - )); - Label pageOffset = new Label(i18n("search.page_n", 0, "-")); - control.pageOffset.addListener((observable, oldValue, newValue) -> pageOffset.setText(i18n( - "search.page_n", control.pageOffset.get() + 1, control.pageCount.get() == -1 ? "-" : control.pageCount.getValue().toString() - ))); - control.pageCount.addListener((observable, oldValue, newValue) -> pageOffset.setText(i18n( - "search.page_n", control.pageOffset.get() + 1, control.pageCount.get() == -1 ? "-" : control.pageCount.getValue().toString() - ))); + Label pageDescription = new Label(); + pageDescription.textProperty().bind(Bindings.createStringBinding(() -> { + int pageCount = control.pageCount.get(); + return i18n("search.page_n", control.pageOffset.get() + 1, pageCount == -1 ? "-" : String.valueOf(pageCount)); + }, control.pageOffset, control.pageCount)); JFXButton nextPageButton = FXUtils.newBorderButton(i18n("search.next_page")); nextPageButton.setOnAction(event -> { - control.pageOffset.set(control.pageOffset.get() + 1); - searchAction.handle(event); + int nv = control.pageOffset.get() + 1; + if (nv < control.pageCount.get()) { + control.pageOffset.set(nv); + changeButton.value.run(); + searchAction.handle(event); + } }); - nextPageButton.setDisable(true); - control.pageOffset.addListener((observable, oldValue, newValue) -> nextPageButton.setDisable( - control.pageCount.get() == -1 || control.pageOffset.get() >= control.pageCount.get() - 1 - )); - control.pageCount.addListener((observable, oldValue, newValue) -> nextPageButton.setDisable( - control.pageCount.get() == -1 || control.pageOffset.get() >= control.pageCount.get() - 1 - )); JFXButton lastPageButton = FXUtils.newBorderButton(i18n("search.last_page")); lastPageButton.setOnAction(event -> { control.pageOffset.set(control.pageCount.get() - 1); + changeButton.value.run(); searchAction.handle(event); }); + + firstPageButton.setDisable(true); + previousPageButton.setDisable(true); lastPageButton.setDisable(true); - control.pageCount.addListener((observable, oldValue, newValue) -> lastPageButton.setDisable(control.pageCount.get() == -1 || control.pageOffset.get() >= control.pageCount.get() - 1)); + nextPageButton.setDisable(true); + + changeButton.value = () -> { + int pageOffset = control.pageOffset.get(); + int pageCount = control.pageCount.get(); + + boolean disablePrevious = pageOffset == 0; + firstPageButton.setDisable(disablePrevious); + previousPageButton.setDisable(disablePrevious); + + boolean disableNext = pageOffset == pageCount - 1; + nextPageButton.setDisable(disableNext); + lastPageButton.setDisable(disableNext || pageCount == -1); + }; + + FXUtils.onChange(control.pageCount, pageCountN -> { + int pageCount = pageCountN.intValue(); + + if (pageCount != -1) { + if (control.pageOffset.get() + 1 >= pageCount) { + control.pageOffset.set(pageCount - 1); + } + } + + changeButton.value.run(); + }); Pane placeholder = new Pane(); HBox.setHgrow(placeholder, Priority.SOMETIMES); @@ -421,13 +454,14 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP JFXButton searchButton = FXUtils.newRaisedButton(i18n("search")); searchButton.setOnAction(searchAction); - actions.appendList(FXCollections.observableArrayList(firstPageButton, previousPageButton, pageOffset, nextPageButton, lastPageButton, placeholder, searchButton)); + actions.appendList(FXCollections.observableArrayList(firstPageButton, previousPageButton, pageDescription, nextPageButton, lastPageButton, placeholder, searchButton)); actions.appendList(control.actions); Bindings.bindContent(actionsBox.getChildren(), actions.getAggregatedList()); } searchPane.addRow(rowIndex++, actionsBox); + FXUtils.onChange(control.downloadSource, v -> searchAction.handle(null)); nameField.setOnAction(searchAction); gameVersionField.setOnAction(searchAction); categoryComboBox.setOnAction(searchAction); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java index b716020ec..5bda19051 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java @@ -94,6 +94,10 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository return "asc"; } + private int calculateTotalPages(Response> response, int pageSize) { + return (int) Math.ceil((double) Math.min(response.pagination.totalCount, 10000) / pageSize); + } + @Override public SearchResult search(String gameVersion, @Nullable RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException { int categoryId = 0; @@ -112,7 +116,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository .getJson(new TypeToken>>() { }.getType()); if (searchFilter.isEmpty()) { - return new SearchResult(response.getData().stream().map(CurseAddon::toMod), (int)Math.ceil((double)response.pagination.totalCount / pageSize)); + return new SearchResult(response.getData().stream().map(CurseAddon::toMod), calculateTotalPages(response, pageSize)); } // https://github.com/HMCL-dev/HMCL/issues/1549 @@ -135,7 +139,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository } return pair(remoteMod, diff); - }).sorted(Comparator.comparingInt(Pair::getValue)).map(Pair::getKey), response.getData().stream().map(CurseAddon::toMod), response.pagination.totalCount); + }).sorted(Comparator.comparingInt(Pair::getValue)).map(Pair::getKey), response.getData().stream().map(CurseAddon::toMod), calculateTotalPages(response, pageSize)); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java index 4cc9e0632..d20a542ac 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java @@ -95,7 +95,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository { Response response = HttpRequest.GET(NetworkUtils.withQuery(PREFIX + "/v2/search", query)) .getJson(new TypeToken>() { }.getType()); - return new SearchResult(response.getHits().stream().map(ProjectSearchResult::toMod), (int)Math.ceil((double)response.totalHits / pageSize)); + return new SearchResult(response.getHits().stream().map(ProjectSearchResult::toMod), (int) Math.ceil((double) response.totalHits / pageSize)); } @Override