From 2f11b6852366b89c4756bfc34217b28fb092fb21 Mon Sep 17 00:00:00 2001 From: Fredrik Marjoni Date: Sun, 5 Apr 2026 14:38:52 +0200 Subject: [PATCH] update: update speed of rendering OrgCards and Org image with ParallelStream --- .../organization/OrganizationService.java | 33 ++++++------- .../app/view/causespage/CausesPageView.java | 49 ++++++++++++------- .../app/view/causespage/OrganizationCard.java | 43 ++++++++++++++-- .../OrganizationPageView.java | 2 + 4 files changed, 88 insertions(+), 39 deletions(-) diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java index 9785040..8ebd625 100644 --- a/src/main/java/edu/group5/app/model/organization/OrganizationService.java +++ b/src/main/java/edu/group5/app/model/organization/OrganizationService.java @@ -3,6 +3,7 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import java.util.stream.Collectors; import java.util.HashMap; import java.util.Map; @@ -90,7 +91,9 @@ public String fetchLogoUrl(String pageUrl) { } try { - Document doc = Jsoup.connect(pageUrl).get(); + Document doc = Jsoup.connect(pageUrl) + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + .timeout(5000).get(); Element img = doc.selectFirst("div.logo img"); if (img != null) { @@ -116,23 +119,17 @@ public String fetchLogoUrl(String pageUrl) { */ public Map getTrustedOrganizationsWithLogos() { Map original = getTrustedOrganizations(); - Map trustedOrgsWithLogos = new HashMap<>(); - - for (Organization org : original.values()) { - String logoUrl = fetchLogoUrl(org.websiteUrl()); - - Organization newOrg = new Organization( - org.orgNumber(), - org.name(), - org.trusted(), - org.websiteUrl(), - org.isPreApproved(), - org.description(), - logoUrl - ); - trustedOrgsWithLogos.put(newOrg.orgNumber(), newOrg); - } - return trustedOrgsWithLogos; + return original.values().parallelStream() + .map(org -> new Organization( + org.orgNumber(), + org.name(), + org.trusted(), + org.websiteUrl(), + org.isPreApproved(), + org.description(), + fetchLogoUrl(org.websiteUrl()) + )) + .collect(Collectors.toMap(Organization::orgNumber, org -> org)); } /** diff --git a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java index 1cd6e83..5cfc197 100644 --- a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java +++ b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java @@ -41,11 +41,41 @@ private ScrollPane createBody() { vBox.setStyle("-fx-padding: 10;"); vBox.setSpacing(10); vBox.setMaxWidth(Double.MAX_VALUE); + + // Load organizations INSTANTLY from cache + allOrganizations = orgController.getTrustedOrgs(); + vBox.getChildren().addAll( createSearchSection(), createOrganizationSection(null) ); body.setContent(vBox); + + // Build a map of org ID -> card for quick lookup + Map cardMap = new HashMap<>(); + for (var node : organizationGrid.getChildren()) { + if (node instanceof OrganizationCard card) { + cardMap.put(card.getOrganization().orgNumber(), card); + } + } + + // Fetch logos and update existing cards (don't rebuild grid) + orgController.getOrganizationsWithLogosAsync() + .thenAccept(orgs -> {this.allOrganizations = orgs; + Platform.runLater(() -> { + for (var entry : orgs.entrySet()) { + OrganizationCard card = cardMap.get(entry.getKey()); + if (card != null && entry.getValue().logoUrl() != null) { + card.updateLogo(entry.getValue().logoUrl()); + } + } + Organization currentOrg = appState.getCurrentOrganization(); + if (currentOrg != null && orgs.containsKey(currentOrg.orgNumber())) { + appState.setCurrentOrganization(orgs.get(currentOrg.orgNumber())); + } + }); + }); + return body; } @@ -76,25 +106,8 @@ private GridPane createOrganizationSection(String searchTerm) { organizationGrid = grid; } - if (allOrganizations == null) { - allOrganizations = orgController.getTrustedOrgs(); - - //Show loading text while organizations and logos are fetched - grid.add(new javafx.scene.control.Label("Loading..."), 0, 0); - - //Fetch trusted organizations with logos asynchronously (runs in background) - orgController.getOrganizationsWithLogosAsync() - .thenAccept(orgs -> { - this.allOrganizations = orgs; - - // Update UI when data is ready - Platform.runLater(() -> updateOrganizationGrid("")); - }); - return grid; - } - Map organizations = new HashMap<>(); - if (searchTerm != null) { + if (searchTerm != null && !searchTerm.isEmpty()) { // Filter organizations by search term organizations = filterOrganizations(searchTerm); } else { diff --git a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java index 31b25ce..2114d17 100644 --- a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java +++ b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java @@ -14,6 +14,8 @@ public class OrganizationCard extends VBox { private final AppState appState; private final Organization organization; private final NavigationController nav; + private StackPane imageContainer; + private String currentLogoUrl; public OrganizationCard(AppState appstate, NavigationController nav, Organization org, String img) { this.appState = appstate; @@ -22,14 +24,15 @@ public OrganizationCard(AppState appstate, NavigationController nav, Organizatio setId("mainContainer"); getStylesheets().add(getClass().getResource("/browsepage/browse_org.css").toExternalForm()); + imageContainer = createImageContainer(img); getChildren().addAll( - imageContainer(img), + imageContainer, orgName(org.name()), checkMarkContainer() ); setOnMouseClicked(e -> { - appstate.setCurrentOrganization(organization); + appstate.setCurrentOrganization(getOrganizationWithCurrentLogo()); nav.showOrganizationPage(); }); @@ -38,7 +41,41 @@ public OrganizationCard(AppState appstate, NavigationController nav, Organizatio setAlignment(Pos.CENTER); } - private StackPane imageContainer(String img) { + public Organization getOrganization() { + return organization; + } + + public void updateLogo(String logoUrl) { + this.currentLogoUrl = logoUrl; + if (imageContainer == null) return; + imageContainer.getChildren().clear(); + if (logoUrl != null && !logoUrl.isBlank()) { + ImageView logo = new ImageView(new Image(logoUrl, true)); + logo.setId("logo"); + logo.setSmooth(true); + logo.setPreserveRatio(true); + logo.setFitHeight(80); + logo.setFitWidth(80); + imageContainer.getChildren().add(logo); + } + } + + private Organization getOrganizationWithCurrentLogo() { + if (currentLogoUrl == null) { + return organization; + } + return new Organization( + organization.orgNumber(), + organization.name(), + organization.trusted(), + organization.websiteUrl(), + organization.isPreApproved(), + organization.description(), + currentLogoUrl + ); + } + + private StackPane createImageContainer(String img) { StackPane imageContainer = new StackPane(); imageContainer.setId("imageContainer"); diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java index da8d1df..1ac2fa6 100644 --- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java +++ b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java @@ -68,6 +68,8 @@ private StackPane createImageContainer() { logo.setId("logo"); logo.setSmooth(true); logo.setPreserveRatio(true); + logo.setFitHeight(120); + logo.setFitWidth(120); imageContainer.getChildren().add(logo); } else { StackPane placeholder = new StackPane();