diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/browsepage/components/ResourceCard.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/browsepage/components/ResourceCard.kt index 122ec8f..5ce38ae 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/browsepage/components/ResourceCard.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/browsepage/components/ResourceCard.kt @@ -24,7 +24,7 @@ import dev.dediamondpro.resourcify.services.IService import dev.dediamondpro.resourcify.services.ProjectType import dev.dediamondpro.resourcify.util.localize import dev.dediamondpro.resourcify.util.ofResourceCustom -import dev.dediamondpro.resourcify.util.ofURL +import dev.dediamondpro.resourcify.util.ofURLCustom import gg.essential.elementa.components.* import gg.essential.elementa.constraints.CenterConstraint import gg.essential.elementa.constraints.ChildBasedSizeConstraint @@ -60,7 +60,7 @@ class ResourceCard(service: IService, project: IProject, type: ProjectType, down } effect ScissorEffect() childOf this project.getBannerUrl()?.let { - UIImage.ofURL(it, false) + UIImage.ofURLCustom(it, false) .constrain { x = CenterConstraint() y = CenterConstraint() @@ -78,10 +78,10 @@ class ResourceCard(service: IService, project: IProject, type: ProjectType, down } childOf this val iconUrl = project.getIconUrl() - if (iconUrl.isNullOrBlank()) { + if (iconUrl == null) { UIImage.ofResourceCustom("/assets/resourcify/pack.png") } else { - UIImage.ofURL(iconUrl) + UIImage.ofURLCustom(iconUrl) }.constrain { width = ImageFillConstraint(ImageFillConstraint.FillType.CROP) height = ImageFillConstraint(ImageFillConstraint.FillType.CROP) diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/ProjectScreen.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/ProjectScreen.kt index 7d358ab..ce09302 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/ProjectScreen.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/ProjectScreen.kt @@ -117,7 +117,7 @@ class ProjectScreen( val version = versions.firstOrNull { it.getMinecraftVersions().contains(Platform.getMcVersion()) } ?: return@thenAccept - val url = version.getDownloadUrl().toURL() + val url = version.getDownloadUrl() ?: return@thenAccept var installed = packHashes.get().contains(version.getSha1()) val buttonText = if (installed) "${ChatColor.BOLD}${localize("resourcify.version.installed")}" else version.getVersionNumber()?.let { @@ -221,15 +221,15 @@ class ProjectScreen( height = ChildBasedSizeConstraint() + 4.pixels() } childOf sideContainer val bannerUrl = project.getBannerUrl() - if (bannerUrl != null) UIImage.ofURL(bannerUrl, false).constrain { + if (bannerUrl != null) UIImage.ofURLCustom(bannerUrl, false).constrain { x = 0.pixels() y = 0.pixels() width = 100.percent() height = ImageAspectConstraint() } childOf sideBox val iconUrl = project.getIconUrl() - (if (iconUrl.isNullOrBlank()) UIImage.ofResourceCustom("/assets/resourcify/pack.png") - else UIImage.ofURL(iconUrl)) + (if (iconUrl == null) UIImage.ofResourceCustom("/assets/resourcify/pack.png") + else UIImage.ofURLCustom(iconUrl)) .constrain { x = 4.pixels() y = SiblingConstraint(padding = 4f) diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/VersionsPage.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/VersionsPage.kt index 474b3d5..abdb650 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/VersionsPage.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/VersionsPage.kt @@ -94,10 +94,12 @@ class VersionsPage(private val screen: ProjectScreen) : UIContainer() { y = 8.pixels() } childOf changeLogHolder VersionCard.createDownloadButton(version, screen.packHashes.get(), screen.downloadFolder, screen.type) - .constrain { - x = 4.pixels(true) - y = 4.pixels() - } childOf changeLogHolder + ?.let { + it.constrain { + x = 4.pixels(true) + y = 4.pixels() + } childOf changeLogHolder + } version.getChangeLog().thenApply { Window.enqueueRenderOperation { var changelog = it @@ -125,10 +127,10 @@ class VersionsPage(private val screen: ProjectScreen) : UIContainer() { UScreen.displayScreen(ConfirmLinkScreen(project.getBrowserUrl(), screen)) } childOf changeLogHolder val iconUrl = project.getIconUrl() - if (iconUrl.isNullOrBlank()) { + if (iconUrl == null) { UIImage.ofResourceCustom("/assets/resourcify/pack.png") } else { - UIImage.ofURL( + UIImage.ofURLCustom( iconUrl, width = 24f, height = 24f, diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/GalleryCard.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/GalleryCard.kt index 5df85fd..6d2c766 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/GalleryCard.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/GalleryCard.kt @@ -20,7 +20,8 @@ package dev.dediamondpro.resourcify.gui.projectpage.components import dev.dediamondpro.resourcify.constraints.ChildLocationSizeConstraint import dev.dediamondpro.resourcify.constraints.ImageFillConstraint import dev.dediamondpro.resourcify.services.IGalleryImage -import dev.dediamondpro.resourcify.util.ofURL +import dev.dediamondpro.resourcify.util.ofURLCustom +import dev.dediamondpro.resourcify.util.toURL import gg.essential.elementa.components.UIBlock import gg.essential.elementa.components.UIImage import gg.essential.elementa.components.UIWrappedText @@ -57,19 +58,23 @@ class GalleryCard(gallery: IGalleryImage) : UIBlock(color = Color(0, 0, 0, 100)) } childOf Window.of(this) background.setFloating(true) background.grabWindowFocus() - UIImage.ofURL(gallery.url, true).constrain { - x = CenterConstraint() - y = CenterConstraint() - width = ImageFillConstraint() - height = ImageFillConstraint() - } childOf background + gallery.url.toURL()?.let { image -> + UIImage.ofURLCustom(image, true).constrain { + x = CenterConstraint() + y = CenterConstraint() + width = ImageFillConstraint() + height = ImageFillConstraint() + } childOf background + } + } + gallery.getThumbnailUrlIfEnabled()?.let { image -> + UIImage.ofURLCustom(image, false).constrain { + x = 0.pixels() + y = 0.pixels() + width = 100.percent() + height = ImageAspectConstraint() + } childOf this } - UIImage.ofURL(gallery.getThumbnailUrlIfEnabled(), false).constrain { - x = 0.pixels() - y = 0.pixels() - width = 100.percent() - height = ImageAspectConstraint() - } childOf this if (!gallery.title.isNullOrBlank()) UIWrappedText(gallery.title ?: "").constrain { x = 4.pixels() y = SiblingConstraint(padding = 4f) diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/MemberCard.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/MemberCard.kt index a73c2a5..5592f39 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/MemberCard.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/MemberCard.kt @@ -1,6 +1,6 @@ /* * This file is part of Resourcify - * Copyright (C) 2023 DeDiamondPro + * Copyright (C) 2023-2024 DeDiamondPro * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,7 +19,8 @@ package dev.dediamondpro.resourcify.gui.projectpage.components import dev.dediamondpro.resourcify.services.IMember import dev.dediamondpro.resourcify.util.capitalizeAll -import dev.dediamondpro.resourcify.util.ofURL +import dev.dediamondpro.resourcify.util.ofURLCustom +import dev.dediamondpro.resourcify.util.toURL import gg.essential.elementa.components.UIContainer import gg.essential.elementa.components.UIImage import gg.essential.elementa.components.UIText @@ -42,8 +43,8 @@ class MemberCard(member: IMember) : UIContainer() { if (it.mouseButton != 0) return@onMouseClick UDesktop.browse(URI(member.url)) } - member.avatarUrl?.let { - UIImage.ofURL(it).constrain { + member.avatarUrl?.toURL()?.let { + UIImage.ofURLCustom(it).constrain { x = 0.pixels() y = 0.pixels() width = 32.pixels() diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/VersionCard.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/VersionCard.kt index ed94040..4d4df56 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/VersionCard.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/components/VersionCard.kt @@ -116,10 +116,13 @@ class VersionCard( color = Color.LIGHT_GRAY.toConstraint() } childOf statsContainer - val button = createDownloadButton(version, hashes, downloadFolder, type) childOf this - onMouseClick { - if (button.isPointInside(it.absoluteX, it.absoluteY)) return@onMouseClick - parent.showChangelog(version) + val button = createDownloadButton(version, hashes, downloadFolder, type) + if (button != null) { + button childOf this + onMouseClick { + if (button.isPointInside(it.absoluteX, it.absoluteY)) return@onMouseClick + parent.showChangelog(version) + } } } @@ -177,8 +180,8 @@ class VersionCard( } companion object { - fun createDownloadButton(version: IVersion, hashes: List, downloadFolder: File, type: ProjectType): UIComponent { - val url = version.getDownloadUrl().toURL() + fun createDownloadButton(version: IVersion, hashes: List, downloadFolder: File, type: ProjectType): UIComponent? { + val url = version.getDownloadUrl() ?: return null var installed = hashes.contains(version.getSha1()) val buttonText = "${ChatColor.BOLD}${if (installed) "resourcify.version.installed".localize() else "resourcify.version.install".localize()}" diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/UpdateGui.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/UpdateGui.kt index a312a0a..2cdc67b 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/UpdateGui.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/UpdateGui.kt @@ -191,7 +191,7 @@ class UpdateGui(val type: ProjectType, private val folder: File) : PaginatedScre y = CenterConstraint() } childOf topBar - cards.addAll(projects.map { (project, newVersion) -> + cards.addAll(projects.filter { it.value.getDownloadUrl() != null }.map { (project, newVersion) -> UpdateCard(project, newVersion, hashes.get()[updates.get()[newVersion]]!!, this).constrain { y = SiblingConstraint(padding = 2f) width = 100.percent() diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/components/UpdateCard.kt b/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/components/UpdateCard.kt index 3a728f7..597d55a 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/components/UpdateCard.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/gui/update/components/UpdateCard.kt @@ -48,7 +48,7 @@ class UpdateCard( val file: File, private val gui: UpdateGui ) : UIBlock(color = Color(0, 0, 0, 100)) { - private val updateUrl = newVersion.getDownloadUrl().toURL() + private val updateUrl = newVersion.getDownloadUrl()!! private var progressBox: UIBlock? = null private var text: UIText? = null @@ -58,10 +58,10 @@ class UpdateCard( } val iconUrl = project.getIconUrl() - if (iconUrl.isNullOrBlank()) { + if (iconUrl == null) { UIImage.ofResourceCustom("/assets/resourcify/pack.png") } else { - UIImage.ofURL(iconUrl) + UIImage.ofURLCustom(iconUrl) }.constrain { x = 4.pixels() y = 4.pixels() diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/IGalleryImage.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/IGalleryImage.kt index 1523723..16bc368 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/IGalleryImage.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/IGalleryImage.kt @@ -18,6 +18,8 @@ package dev.dediamondpro.resourcify.services import dev.dediamondpro.resourcify.config.Config +import dev.dediamondpro.resourcify.util.toURL +import java.net.URL interface IGalleryImage { val url: String @@ -25,8 +27,8 @@ interface IGalleryImage { val title: String? val description: String? - fun getThumbnailUrlIfEnabled(): String { - if (Config.instance.fullResThumbnail) return url - return thumbnailUrl ?: url + fun getThumbnailUrlIfEnabled(): URL? { + if (Config.instance.fullResThumbnail) return url.toURL() + return thumbnailUrl?.toURL() ?: url.toURL() } } \ No newline at end of file diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/IProject.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/IProject.kt index c160f01..84751ed 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/IProject.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/IProject.kt @@ -18,6 +18,7 @@ package dev.dediamondpro.resourcify.services import java.awt.Color +import java.net.URL import java.util.concurrent.CompletableFuture interface IProject { @@ -25,8 +26,8 @@ interface IProject { fun getId(): String fun getSummary(): String fun getAuthor(): String - fun getIconUrl(): String? = null - fun getBannerUrl(): String? = null + fun getIconUrl(): URL? = null + fun getBannerUrl(): URL? = null fun getBannerColor(): Color? = null fun getBrowserUrl(): String fun getDescription(): CompletableFuture diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/IVersion.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/IVersion.kt index 7c789fd..ab6b75a 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/IVersion.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/IVersion.kt @@ -17,13 +17,14 @@ package dev.dediamondpro.resourcify.services +import java.net.URL import java.util.concurrent.CompletableFuture interface IVersion { fun getName(): String fun getVersionNumber(): String? fun getProjectId(): String - fun getDownloadUrl(): String + fun getDownloadUrl(): URL? fun getFileName(): String fun getSha1(): String fun getChangeLog(): CompletableFuture diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeProject.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeProject.kt index c8110c7..43cfbd5 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeProject.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeProject.kt @@ -46,8 +46,8 @@ data class CurseForgeProject( override fun getId(): String = id.toString() override fun getSummary(): String = summary override fun getAuthor(): String = authors.firstOrNull()?.name ?: "" - override fun getIconUrl(): String? = logo?.let { it.thumbnailUrl ?: it.url } - override fun getBannerUrl(): String? = screenshots.firstOrNull()?.getThumbnailUrlIfEnabled() + override fun getIconUrl(): URL? = logo?.let { it.thumbnailUrl?.toURL() ?: it.url.toURL() } + override fun getBannerUrl(): URL? = screenshots.firstOrNull()?.getThumbnailUrlIfEnabled() override fun getDescription(): CompletableFuture { return descriptionRequest ?: supplyAsync { diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeVersion.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeVersion.kt index 07dd194..b9f5682 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeVersion.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/curseforge/CurseForgeVersion.kt @@ -45,7 +45,7 @@ data class CurseForgeVersion( override fun getVersionNumber(): String? = null override fun getProjectId(): String = modId.toString() fun hasDownloadUrl(): Boolean = downloadUrl != null - override fun getDownloadUrl(): String = downloadUrl ?: error("No download URL.") + override fun getDownloadUrl(): URL? = downloadUrl?.toURL() override fun getFileName(): String = fileName override fun getSha1(): String = hashes.firstOrNull { it.algo == 1 }?.value ?: "" @@ -84,7 +84,7 @@ data class CurseForgeVersion( return (dependenciesRequest ?: supplyAsync { val deps = dependencies.filter { DependencyType.fromCurseForgeId(it.relationType) != null } val projects: ModsResponse = "${CurseForgeService.API}/mods".toURL() - .postAndGetJson( + ?.postAndGetJson( GetByIdProperty(deps.map { it.modId }), headers = mapOf("x-api-key" to CurseForgeService.API_KEY) ) ?: error("Failed to fetch dependencies.") diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/FullModrinthProject.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/FullModrinthProject.kt index cda59ec..6808a39 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/FullModrinthProject.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/FullModrinthProject.kt @@ -70,9 +70,9 @@ data class FullModrinthProject( override fun getBrowserUrl(): String = "https://modrinth.com/$projectType/$slug" override fun getDescription(): CompletableFuture = supply { body } - override fun getIconUrl(): String? = iconUrl + override fun getIconUrl(): URL? = iconUrl?.toURL() - override fun getBannerUrl(): String? = gallery.minByOrNull { it.ordering }?.getThumbnailUrlIfEnabled() + override fun getBannerUrl(): URL? = gallery.minByOrNull { it.ordering }?.getThumbnailUrlIfEnabled() override fun getBannerColor(): Color? = color?.let { Color(it) } diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/ModrinthVersion.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/ModrinthVersion.kt index 32acd30..399eb8f 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/ModrinthVersion.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/ModrinthVersion.kt @@ -22,7 +22,9 @@ import dev.dediamondpro.resourcify.services.* import dev.dediamondpro.resourcify.util.getJson import dev.dediamondpro.resourcify.util.supply import dev.dediamondpro.resourcify.util.supplyAsync +import dev.dediamondpro.resourcify.util.toURL import org.apache.http.client.utils.URIBuilder +import java.net.URL import java.util.concurrent.CompletableFuture data class ModrinthVersion( @@ -46,7 +48,7 @@ data class ModrinthVersion( override fun getProjectId(): String = projectId fun hasFile() = files.isNotEmpty() private fun getPrimaryFile() = files.firstOrNull { it.primary } ?: files.first() - override fun getDownloadUrl(): String = getPrimaryFile().url + override fun getDownloadUrl(): URL? = getPrimaryFile().url.toURL() override fun getFileName(): String = getPrimaryFile().filename override fun getSha1(): String = getPrimaryFile().hashes.sha1 override fun getChangeLog(): CompletableFuture = supply { changelog } diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/PartialModrinthProject.kt b/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/PartialModrinthProject.kt index 4baf38b..582fc60 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/PartialModrinthProject.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/services/modrinth/PartialModrinthProject.kt @@ -52,8 +52,8 @@ data class PartialModrinthProject( override fun getId(): String = id override fun getSummary(): String = summary override fun getAuthor(): String = author - override fun getIconUrl(): String? = iconUrl - override fun getBannerUrl(): String? = featuredGallery ?: gallery.firstOrNull() + override fun getIconUrl(): URL? = iconUrl?.toURL() + override fun getBannerUrl(): URL? = featuredGallery?.toURL() ?: gallery.firstOrNull()?.toURL() override fun getBannerColor(): Color? = color?.let { Color(it) } override fun getDescription(): CompletableFuture = fetchProject().thenApply { it?.getDescription()?.getNow(null) ?: error("Failed to fetch description.") } diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/util/ElementaUtils.kt b/src/main/kotlin/dev/dediamondpro/resourcify/util/ElementaUtils.kt index 0f3f5d6..d16e9dd 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/util/ElementaUtils.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/util/ElementaUtils.kt @@ -36,12 +36,12 @@ import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension import org.commonmark.ext.gfm.tables.TablesExtension import java.awt.Color import java.awt.image.BufferedImage -import java.util.concurrent.CompletableFuture +import java.net.URL import javax.imageio.ImageIO import kotlin.math.min -fun UIImage.Companion.ofURL( - source: String, +fun UIImage.Companion.ofURLCustom( + url: URL, loadingImage: Boolean = true, width: Float? = null, height: Float? = null, @@ -51,7 +51,6 @@ fun UIImage.Companion.ofURL( minFilter: UIImage.TextureScalingMode = UIImage.TextureScalingMode.LINEAR, magFilter: UIImage.TextureScalingMode = UIImage.TextureScalingMode.LINEAR, ): UIImage { - val url = source.toURL() val image = UIImage( url.getImageAsync( useCache = useCache, diff --git a/src/main/kotlin/dev/dediamondpro/resourcify/util/NetworkUtil.kt b/src/main/kotlin/dev/dediamondpro/resourcify/util/NetworkUtil.kt index 5ff1b0c..c2cd030 100644 --- a/src/main/kotlin/dev/dediamondpro/resourcify/util/NetworkUtil.kt +++ b/src/main/kotlin/dev/dediamondpro/resourcify/util/NetworkUtil.kt @@ -308,4 +308,8 @@ fun String.toURI(): URI = try { URI(this.encodeUrl()) } -fun String.toURL(): URL = this.toURI().toURL() \ No newline at end of file +fun String.toURL(): URL? = try { + this.toURI().toURL() +} catch (_: Exception) { + null +} \ No newline at end of file