From 22f6d3ed1b4d00ca8c2e4966807d0bd1452db89e Mon Sep 17 00:00:00 2001 From: mormaer Date: Sun, 17 Sep 2023 11:19:33 +0100 Subject: [PATCH] cherry pick of #612 --- Mlem.xcodeproj/project.pbxproj | 16 +++++++++ Mlem/API/Internal/LemmyURL.swift | 27 +++++++++++++++ Mlem/API/Models/Community/APICommunity.swift | 9 +++-- Mlem/API/Models/Person/APIPerson.swift | 12 +++++-- Mlem/API/Models/Posts/APIPost.swift | 9 +++-- Mlem/API/Models/Posts/APIPostReport.swift | 6 +++- Mlem/API/Models/Site/APISite.swift | 9 +++-- .../API Post View - Post Type.swift | 4 +-- Mlem/Models/Trackers/Post Tracker.swift | 8 ++--- .../Components/Thumbnail Image View.swift | 2 +- .../Shared/Links/Community Link View.swift | 2 +- .../Shared/Links/User Profile Label.swift | 2 +- Mlem/Views/Shared/Website Icon Complex.swift | 8 ++--- .../Tabs/Feeds/Components/Sidebar View.swift | 11 ++++--- Mlem/Views/Tabs/Profile/User View.swift | 10 +++--- .../Appearance/Post/PostSettingsView.swift | 4 +-- MlemTests/Mocks/APICommunity+Mock.swift | 4 +-- MlemTests/Model/LemmyURLTests.swift | 33 +++++++++++++++++++ 18 files changed, 139 insertions(+), 37 deletions(-) create mode 100644 Mlem/API/Internal/LemmyURL.swift create mode 100644 MlemTests/Model/LemmyURLTests.swift diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj index 6491e3386..950526f7f 100644 --- a/Mlem.xcodeproj/project.pbxproj +++ b/Mlem.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ 5016A2B32A67EC0700B257E8 /* NotificationDisplayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5016A2B22A67EC0700B257E8 /* NotificationDisplayer.swift */; }; 503A5D752A78EF3C00488C38 /* Encodable+Export.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503A5D742A78EF3C00488C38 /* Encodable+Export.swift */; }; 503BA26F2A2C94540052516C /* URL+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503BA26E2A2C94540052516C /* URL+Identifiable.swift */; }; + 504ECBAE2AB45B2A006C0B96 /* LemmyURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */; }; + 504ECBB12AB4B101006C0B96 /* LemmyURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */; }; 5064D03D2A6DE0AA00B22EE3 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5064D03C2A6DE0AA00B22EE3 /* Notifier.swift */; }; 5064D03F2A6DE0DB00B22EE3 /* Notifier+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5064D03E2A6DE0DB00B22EE3 /* Notifier+Dependency.swift */; }; 5064D0412A6E63E000B22EE3 /* Task+Notifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5064D0402A6E63E000B22EE3 /* Task+Notifiable.swift */; }; @@ -405,6 +407,8 @@ 5016A2B22A67EC0700B257E8 /* NotificationDisplayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDisplayer.swift; sourceTree = ""; }; 503A5D742A78EF3C00488C38 /* Encodable+Export.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+Export.swift"; sourceTree = ""; }; 503BA26E2A2C94540052516C /* URL+Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Identifiable.swift"; sourceTree = ""; }; + 504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURL.swift; sourceTree = ""; }; + 504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURLTests.swift; sourceTree = ""; }; 5064D03C2A6DE0AA00B22EE3 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; }; 5064D03E2A6DE0DB00B22EE3 /* Notifier+Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notifier+Dependency.swift"; sourceTree = ""; }; 5064D0402A6E63E000B22EE3 /* Task+Notifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Notifiable.swift"; sourceTree = ""; }; @@ -893,6 +897,14 @@ path = TabBar; sourceTree = ""; }; + 504ECBAF2AB4B0DF006C0B96 /* Model */ = { + isa = PBXGroup; + children = ( + 504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */, + ); + path = Model; + sourceTree = ""; + }; 5064D03B2A6DE05000B22EE3 /* Notifications */ = { isa = PBXGroup; children = ( @@ -1249,6 +1261,7 @@ 6363D5D927EE196A00E34822 /* MlemTests */ = { isa = PBXGroup; children = ( + 504ECBAF2AB4B0DF006C0B96 /* Model */, 50DBB8DE2A805770002870B1 /* Mocks */, 50C86AB82A7E507200277519 /* Persistence */, 6363D5DA27EE196A00E34822 /* MlemTests.swift */, @@ -1310,6 +1323,7 @@ isa = PBXGroup; children = ( 637218032A3A2AAD008C4816 /* HierarchicalComment.swift */, + 504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */, ); path = Internal; sourceTree = ""; @@ -2254,6 +2268,7 @@ 6D405B032A43E7DB00C65F9C /* Sidebar Header Label.swift in Sources */, 50C99B5E2A61F611005D57DD /* CommentRepository+Dependency.swift in Sources */, 632578182A29F83C00446A66 /* PostSortMenu.swift in Sources */, + 504ECBAE2AB45B2A006C0B96 /* LemmyURL.swift in Sources */, CDA217EA2A63093E00BDA173 /* ReportComment.swift in Sources */, CDA217E82A63029B00BDA173 /* ReportMention.swift in Sources */, 508845CF2A3641160088E483 /* JSONDecoder+Default.swift in Sources */, @@ -2487,6 +2502,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 504ECBB12AB4B101006C0B96 /* LemmyURLTests.swift in Sources */, 50DBB8E02A805836002870B1 /* MockErrorHandler.swift in Sources */, 6363D5DB27EE196A00E34822 /* MlemTests.swift in Sources */, 50DBB8E22A80F9E4002870B1 /* APICommunity+Mock.swift in Sources */, diff --git a/Mlem/API/Internal/LemmyURL.swift b/Mlem/API/Internal/LemmyURL.swift new file mode 100644 index 000000000..632641339 --- /dev/null +++ b/Mlem/API/Internal/LemmyURL.swift @@ -0,0 +1,27 @@ +// +// LemmyURL.swift +// Mlem +// +// Created by mormaer on 15/09/2023. +// +// + +import Foundation + +struct LemmyURL { + let url: URL + + init?(string: String?) { + guard let string else { + return nil + } + + if let url = URL(string: string) { + self.url = url + } else if let encoded = string.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let url = URL(string: encoded) { + self.url = url + } else { + return nil + } + } +} diff --git a/Mlem/API/Models/Community/APICommunity.swift b/Mlem/API/Models/Community/APICommunity.swift index 6cd87b38c..0c276dac2 100644 --- a/Mlem/API/Models/Community/APICommunity.swift +++ b/Mlem/API/Models/Community/APICommunity.swift @@ -20,8 +20,8 @@ struct APICommunity: Codable, Identifiable { let nsfw: Bool let actorId: URL let local: Bool - let icon: URL? - let banner: URL? + let icon: String? + let banner: String? let hidden: Bool let postingRestrictedToMods: Bool let instanceId: Int @@ -44,3 +44,8 @@ extension APICommunity: Comparable { return lhsFullCommunity < rhsFullCommunity } } + +extension APICommunity { + var iconUrl: URL? { LemmyURL(string: icon)?.url } + var bannerUrl: URL? { LemmyURL(string: banner)?.url } +} diff --git a/Mlem/API/Models/Person/APIPerson.swift b/Mlem/API/Models/Person/APIPerson.swift index a61b0f4ef..723a32800 100644 --- a/Mlem/API/Models/Person/APIPerson.swift +++ b/Mlem/API/Models/Person/APIPerson.swift @@ -12,16 +12,16 @@ struct APIPerson: Decodable, Identifiable, Hashable { let id: Int let name: String let displayName: String? - let avatar: URL? + let avatar: String? let banned: Bool let published: Date let updated: Date? let actorId: URL let bio: String? let local: Bool - let banner: URL? + let banner: String? let deleted: Bool - let sharedInboxUrl: URL? + let sharedInboxUrl: String? let matrixUserId: String? let admin: Bool? // this is no longer returned on beehaw... let botAccount: Bool @@ -34,3 +34,9 @@ extension APIPerson: Equatable { lhs.actorId == rhs.actorId } } + +extension APIPerson { + var avatarUrl: URL? { LemmyURL(string: avatar)?.url } + var bannerUrl: URL? { LemmyURL(string: banner)?.url } + var sharedInboxLink: URL? { LemmyURL(string: sharedInboxUrl)?.url } +} diff --git a/Mlem/API/Models/Posts/APIPost.swift b/Mlem/API/Models/Posts/APIPost.swift index f3401b8cc..5dff4ce74 100644 --- a/Mlem/API/Models/Posts/APIPost.swift +++ b/Mlem/API/Models/Posts/APIPost.swift @@ -11,7 +11,7 @@ import Foundation struct APIPost: Decodable { let id: Int let name: String - let url: URL? + let url: String? let body: String? let creatorId: Int let communityId: Int @@ -28,10 +28,15 @@ struct APIPost: Decodable { let nsfw: Bool let published: Date let removed: Bool - let thumbnailUrl: URL? + let thumbnailUrl: String? let updated: Date? } +extension APIPost { + var linkUrl: URL? { LemmyURL(string: url)?.url } + var thumbnailImageUrl: URL? { LemmyURL(string: thumbnailUrl)?.url } +} + extension APIPost: Equatable { static func == (lhs: APIPost, rhs: APIPost) -> Bool { lhs.id == rhs.id diff --git a/Mlem/API/Models/Posts/APIPostReport.swift b/Mlem/API/Models/Posts/APIPostReport.swift index a72792888..5a15e0392 100644 --- a/Mlem/API/Models/Posts/APIPostReport.swift +++ b/Mlem/API/Models/Posts/APIPostReport.swift @@ -13,7 +13,7 @@ struct APIPostReport: Decodable { let creatorId: Int let postId: Int let originalPostName: String - let originalPostUrl: URL? + let originalPostUrl: String? let originalPostBody: String? let reason: String let resolved: Bool @@ -21,3 +21,7 @@ struct APIPostReport: Decodable { let published: Date let updated: Date? } + +extension APIPostReport { + var originalUrl: URL? { LemmyURL(string: originalPostUrl)?.url } +} diff --git a/Mlem/API/Models/Site/APISite.swift b/Mlem/API/Models/Site/APISite.swift index 20dbc31b2..048be2b14 100644 --- a/Mlem/API/Models/Site/APISite.swift +++ b/Mlem/API/Models/Site/APISite.swift @@ -13,8 +13,8 @@ struct APISite: Decodable { let name: String let sidebar: String? let published: Date - let icon: URL? - let banner: URL? + let icon: String? + let banner: String? let description: String? let actorId: String? let lastRefreshedAt: Date @@ -22,3 +22,8 @@ struct APISite: Decodable { let publicKey: String let instanceId: Int } + +extension APISite { + var iconUrl: URL? { LemmyURL(string: icon)?.url } + var bannerUrl: URL? { LemmyURL(string: banner)?.url } +} diff --git a/Mlem/Extensions/API Post View - Post Type.swift b/Mlem/Extensions/API Post View - Post Type.swift index c1d503e11..6fd9a19de 100644 --- a/Mlem/Extensions/API Post View - Post Type.swift +++ b/Mlem/Extensions/API Post View - Post Type.swift @@ -10,9 +10,9 @@ import Foundation extension APIPostView { var postType: PostType { // post with URL: either image or link - if let postUrl = post.url { + if let postUrl = post.linkUrl { // if image, return image link, otherwise return thumbnail - return postUrl.isImage ? .image(postUrl) : .link(post.thumbnailUrl) + return postUrl.isImage ? .image(postUrl) : .link(post.thumbnailImageUrl) } // otherwise text, but post.body needs to be present, even if it's an empty string diff --git a/Mlem/Models/Trackers/Post Tracker.swift b/Mlem/Models/Trackers/Post Tracker.swift index 9db1cf216..0750afee7 100644 --- a/Mlem/Models/Trackers/Post Tracker.swift +++ b/Mlem/Models/Trackers/Post Tracker.swift @@ -100,11 +100,11 @@ class PostTracker: FeedTracker { for postView in newPosts { // preload user and community avatars--fetching both because we don't know which we'll need, but these are super tiny // so it's probably not an API crime, right? - if let communityAvatarLink = postView.community.icon { + if let communityAvatarLink = postView.community.iconUrl { imageRequests.append(ImageRequest(url: communityAvatarLink.withIcon64Parameters)) } - - if let userAvatarLink = postView.creator.avatar { + + if let userAvatarLink = postView.creator.avatarUrl { imageRequests.append(ImageRequest(url: userAvatarLink.withIcon64Parameters)) } @@ -114,7 +114,7 @@ class PostTracker: FeedTracker { imageRequests.append(ImageRequest(url: url, priority: .high)) case .link(let url): // websites: load image and favicon - if let baseURL = postView.post.url?.host, + if let baseURL = postView.post.linkUrl?.host, let favIconURL = URL(string: "https://www.google.com/s2/favicons?sz=64&domain=\(baseURL)") { imageRequests.append(ImageRequest(url: favIconURL)) } diff --git a/Mlem/Views/Shared/Components/Thumbnail Image View.swift b/Mlem/Views/Shared/Components/Thumbnail Image View.swift index dac4880a8..6e334b8ee 100644 --- a/Mlem/Views/Shared/Components/Thumbnail Image View.swift +++ b/Mlem/Views/Shared/Components/Thumbnail Image View.swift @@ -36,7 +36,7 @@ struct ThumbnailImageView: View { case .link(let url): CachedImage(url: url, shouldExpand: false, fixedSize: size) .onTapGesture { - if let url = postView.post.url { + if let url = postView.post.linkUrl { openURL(url) markPostAsRead() } diff --git a/Mlem/Views/Shared/Links/Community Link View.swift b/Mlem/Views/Shared/Links/Community Link View.swift index ea320d116..2fd1651a0 100644 --- a/Mlem/Views/Shared/Links/Community Link View.swift +++ b/Mlem/Views/Shared/Links/Community Link View.swift @@ -151,7 +151,7 @@ struct CommunityLabel: View { @ViewBuilder private var communityAvatar: some View { Group { - if let url = community.icon { + if let url = community.iconUrl { CachedImage(url: url.withIcon64Parameters, shouldExpand: false, fixedSize: avatarSize, diff --git a/Mlem/Views/Shared/Links/User Profile Label.swift b/Mlem/Views/Shared/Links/User Profile Label.swift index 16bd11711..bbcca4937 100644 --- a/Mlem/Views/Shared/Links/User Profile Label.swift +++ b/Mlem/Views/Shared/Links/User Profile Label.swift @@ -82,7 +82,7 @@ struct UserProfileLabel: View { @ViewBuilder private var userAvatar: some View { Group { - if let userAvatarLink = user.avatar { + if let userAvatarLink = user.avatarUrl { CachedImage(url: userAvatarLink, shouldExpand: false, fixedSize: avatarSize, diff --git a/Mlem/Views/Shared/Website Icon Complex.swift b/Mlem/Views/Shared/Website Icon Complex.swift index 52e2a0d0c..58a16d2e4 100644 --- a/Mlem/Views/Shared/Website Icon Complex.swift +++ b/Mlem/Views/Shared/Website Icon Complex.swift @@ -30,7 +30,7 @@ struct WebsiteIconComplex: View { var faviconURL: URL? { guard - let baseURL = post.url?.host, + let baseURL = post.linkUrl?.host, let imageURL = URL(string: "https://www.google.com/s2/favicons?sz=64&domain=\(baseURL)") else { return nil @@ -48,7 +48,7 @@ struct WebsiteIconComplex: View { } var linkHost: String { - if let url = post.url { + if let url = post.linkUrl { return url.host ?? "some website" } return "some website" @@ -56,7 +56,7 @@ struct WebsiteIconComplex: View { var body: some View { VStack(spacing: 0) { - if shouldShowWebsitePreviews, let thumbnailURL = post.thumbnailUrl { + if shouldShowWebsitePreviews, let thumbnailURL = post.thumbnailImageUrl { CachedImage(url: thumbnailURL, shouldExpand: false) .frame(maxHeight: 400) .applyNsfwOverlay(post.nsfw) @@ -97,7 +97,7 @@ struct WebsiteIconComplex: View { ) .contentShape(Rectangle()) .onTapGesture { - if let url = post.url { + if let url = post.linkUrl { openURL(url) if let onTapActions { onTapActions() diff --git a/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift b/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift index 2496d712a..2742caa7d 100644 --- a/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift +++ b/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift @@ -71,9 +71,10 @@ struct CommunitySidebarView: View { title: communityDetails.communityView.community.name, subtitle: "@\(communityDetails.communityView.community.name)@\(communityDetails.communityView.community.actorId.host()!)", avatarSubtext: .constant("Created \(getRelativeTime(date: communityDetails.communityView.community.published))"), - bannerURL: shouldShowCommunityHeaders ? communityDetails.communityView.community.banner : nil, - avatarUrl: communityDetails.communityView.community.icon, - label1: "\(communityDetails.communityView.counts.subscribers) Subscribers") + bannerURL: shouldShowCommunityHeaders ? communityDetails.communityView.community.bannerUrl : nil, + avatarUrl: communityDetails.communityView.community.iconUrl, + label1: "\(communityDetails.communityView.counts.subscribers) Subscribers" + ) Picker(selection: $selectionSection, label: Text("Profile Section")) { Text("Description").tag(0) @@ -149,8 +150,8 @@ struct SidebarPreview: PreviewProvider { nsfw: false, actorId: URL(string: "https://lemmy.foo.com/c/testcommunity")!, local: false, - icon: URL(string: "https://vlemmy.net/pictrs/image/190f2d6a-ac38-448d-ae9b-f6d751eb6e69.png?format=webp"), - banner: URL(string: "https://vlemmy.net/pictrs/image/719b61b3-8d8e-4aec-9f15-17be4a081f97.jpeg?format=webp"), + icon: "https://vlemmy.net/pictrs/image/190f2d6a-ac38-448d-ae9b-f6d751eb6e69.png?format=webp", + banner: "https://vlemmy.net/pictrs/image/719b61b3-8d8e-4aec-9f15-17be4a081f97.jpeg?format=webp", hidden: false, postingRestrictedToMods: false, instanceId: 0 diff --git a/Mlem/Views/Tabs/Profile/User View.swift b/Mlem/Views/Tabs/Profile/User View.swift index 5f95b6e44..18896d592 100644 --- a/Mlem/Views/Tabs/Profile/User View.swift +++ b/Mlem/Views/Tabs/Profile/User View.swift @@ -93,9 +93,9 @@ struct UserView: View { title: userDetails.person.displayName ?? userDetails.person.name, subtitle: "@\(userDetails.person.name)@\(userDetails.person.actorId.host()!)", avatarSubtext: $avatarSubtext, - avatarSubtextClicked: self.toggleCakeDayVisible, - bannerURL: shouldShowUserHeaders ? userDetails.person.banner : nil, - avatarUrl: userDetails.person.avatar, + avatarSubtextClicked: toggleCakeDayVisible, + bannerURL: shouldShowUserHeaders ? userDetails.person.bannerUrl : nil, + avatarUrl: userDetails.person.avatarUrl, label1: "\(userDetails.counts.commentCount) Comments", label2: "\(userDetails.counts.postCount) Posts") } @@ -443,14 +443,14 @@ struct UserViewPreview: PreviewProvider { id: name.hashValue, name: name, displayName: displayName, - avatar: URL(string: "https://lemmy.ml/pictrs/image/df86c06d-341c-4e79-9c80-d7c7eb64967a.jpeg?format=webp"), + avatar: "https://lemmy.ml/pictrs/image/df86c06d-341c-4e79-9c80-d7c7eb64967a.jpeg?format=webp", banned: false, published: Date.now.advanced(by: -10000), updated: nil, actorId: URL(string: "https://google.com")!, bio: "Just here for the good vibes!", local: false, - banner: URL(string: "https://i.imgur.com/wcayaCB.jpeg"), + banner: "https://i.imgur.com/wcayaCB.jpeg", deleted: false, sharedInboxUrl: nil, matrixUserId: nil, diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift index ee0f9c72f..4594efb69 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift @@ -106,7 +106,7 @@ struct PostSettingsView: View { WebsiteIconComplex(post: APIPost( id: 0, name: "", - url: URL(string: "https://lemmy.ml/post/1011734")!, + url: "https://lemmy.ml/post/1011734", body: "", creatorId: 0, communityId: 0, @@ -123,7 +123,7 @@ struct PostSettingsView: View { nsfw: false, published: .now, removed: false, - thumbnailUrl: URL(string: "https://lemmy.ml/pictrs/image/1b759945-6651-497c-bee0-9bdb68f4a829.png"), + thumbnailUrl: "https://lemmy.ml/pictrs/image/1b759945-6651-497c-bee0-9bdb68f4a829.png", updated: nil )) .padding(.horizontal) diff --git a/MlemTests/Mocks/APICommunity+Mock.swift b/MlemTests/Mocks/APICommunity+Mock.swift index 034a582bc..2d044230c 100644 --- a/MlemTests/Mocks/APICommunity+Mock.swift +++ b/MlemTests/Mocks/APICommunity+Mock.swift @@ -23,8 +23,8 @@ extension APICommunity { nsfw: Bool = false, actorId: URL = URL(string: "https://mlem.group")!, local: Bool = true, - icon: URL? = nil, - banner: URL? = nil, + icon: String? = nil, + banner: String? = nil, hidden: Bool = false, postingRestrictedToMods: Bool = false, instanceId: Int = 0 diff --git a/MlemTests/Model/LemmyURLTests.swift b/MlemTests/Model/LemmyURLTests.swift new file mode 100644 index 000000000..864597df5 --- /dev/null +++ b/MlemTests/Model/LemmyURLTests.swift @@ -0,0 +1,33 @@ +// +// LemmyURLTests.swift +// MlemTests +// +// Created by mormaer on 15/09/2023. +// +// + +@testable import Mlem +import XCTest + +final class LemmyURLTests: XCTestCase { + func testHandlesValidURL() throws { + let validUrl = "https://mlem.group" + let lemmyUrl = LemmyURL(string: validUrl) + // expectation is the URL will be unchanged as it's already valid + XCTAssertEqual(lemmyUrl?.url.absoluteString, validUrl) + } + + func testHandlesUnencodedURL() throws { + let unencodedUrl = "https://matrix.to/#/#space:lemmy.world" + let lemmyUrl = LemmyURL(string: unencodedUrl) + // expectation is that the # character will be encoded to %23 + XCTAssertEqual(lemmyUrl?.url.absoluteString, "https://matrix.to/%23/%23space:lemmy.world") + } + + func testHandlesEncodedURL() throws { + let encodedUrl = "https://matrix.to/%23/%23space:lemmy.world" + let lemmyUrl = LemmyURL(string: encodedUrl) + // expectation is that the URL will be unchanged as it's already valid/encoded + XCTAssertEqual(lemmyUrl?.url.absoluteString, "https://matrix.to/%23/%23space:lemmy.world") + } +}