From a659894499453a11443f6fce51e3a5361f2b0f8a Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Tue, 19 Sep 2023 03:50:06 -0400 Subject: [PATCH] Eric/master hotfix (#626) Co-authored-by: mormaer --- Brewfile.lock.json | 96 +++++++++++++++++++ Mlem.xcodeproj/project.pbxproj | 20 +++- Mlem/API/Internal/LemmyURL.swift | 27 ++++++ Mlem/API/Models/Community/APICommunity.swift | 9 +- Mlem/API/Models/Person/APIPerson.swift | 14 ++- 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/Extensions/JSONDecoder+Default.swift | 29 +++--- 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 | 4 +- .../Posts/Post Sizes/Headline Post.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 +++++++ 21 files changed, 254 insertions(+), 57 deletions(-) create mode 100644 Brewfile.lock.json create mode 100644 Mlem/API/Internal/LemmyURL.swift create mode 100644 MlemTests/Model/LemmyURLTests.swift diff --git a/Brewfile.lock.json b/Brewfile.lock.json new file mode 100644 index 000000000..c6487bfa8 --- /dev/null +++ b/Brewfile.lock.json @@ -0,0 +1,96 @@ +{ + "entries": { + "brew": { + "swiftlint": { + "version": "0.52.4", + "bottle": { + "rebuild": 1, + "root_url": "https://ghcr.io/v2/homebrew/core", + "files": { + "arm64_ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:718f5989e0909ea736848e0d6f32d07cfa20bb23cab5fb9989803872e88d1a29", + "sha256": "718f5989e0909ea736848e0d6f32d07cfa20bb23cab5fb9989803872e88d1a29" + }, + "arm64_monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:bcc2c13aa6c01634b382991df048bb331caa5eec230fff33ddbab19258a30c22", + "sha256": "bcc2c13aa6c01634b382991df048bb331caa5eec230fff33ddbab19258a30c22" + }, + "ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:5aec3b872abfead26c702334f0cffddb8ea653481720d7c1248ad836be37a975", + "sha256": "5aec3b872abfead26c702334f0cffddb8ea653481720d7c1248ad836be37a975" + }, + "monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:e3171413a8649150601f777176af1dae458cb4be35aa1185e31636db1bb7124a", + "sha256": "e3171413a8649150601f777176af1dae458cb4be35aa1185e31636db1bb7124a" + }, + "x86_64_linux": { + "cellar": "/home/linuxbrew/.linuxbrew/Cellar", + "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:e8ec45e149d2e33295ddcd87a142389492176b51d0c2216127ae00d3ade33a6b", + "sha256": "e8ec45e149d2e33295ddcd87a142389492176b51d0c2216127ae00d3ade33a6b" + } + } + } + }, + "swiftformat": { + "version": "0.52.2", + "bottle": { + "rebuild": 0, + "root_url": "https://ghcr.io/v2/homebrew/core", + "files": { + "arm64_ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:365be8eeb9ac865a41f06e0a42934fff1ab9a4c7d4eeb425542188c8af523ecf", + "sha256": "365be8eeb9ac865a41f06e0a42934fff1ab9a4c7d4eeb425542188c8af523ecf" + }, + "arm64_monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:19fed428d1201e6211c687604982ce288d4b87dd7ff4da1fd5db01efaaada8ae", + "sha256": "19fed428d1201e6211c687604982ce288d4b87dd7ff4da1fd5db01efaaada8ae" + }, + "arm64_big_sur": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:a410d04988d771f4022b910e3d3a68429d4cd91e402d97885a70999fc5dffa7a", + "sha256": "a410d04988d771f4022b910e3d3a68429d4cd91e402d97885a70999fc5dffa7a" + }, + "ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:6e09bc4b333edebebd6eb01d5e5a7d370b1f252933b55da5ab2b1c6755c7dd57", + "sha256": "6e09bc4b333edebebd6eb01d5e5a7d370b1f252933b55da5ab2b1c6755c7dd57" + }, + "monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:cb38dffa5947928e8f84b70cf280d63f1d740a4ca46c7b91764cd88582acb57d", + "sha256": "cb38dffa5947928e8f84b70cf280d63f1d740a4ca46c7b91764cd88582acb57d" + }, + "big_sur": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:d03cfd926f3ca6dc5e0851d46def2fbb9a1639a197bd9ab5f8108064d6e40621", + "sha256": "d03cfd926f3ca6dc5e0851d46def2fbb9a1639a197bd9ab5f8108064d6e40621" + }, + "x86_64_linux": { + "cellar": "/home/linuxbrew/.linuxbrew/Cellar", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:601170208546fac087ed96fa9fdddf26e46b5e45bd10a8bfa3248fbe4480a09d", + "sha256": "601170208546fac087ed96fa9fdddf26e46b5e45bd10a8bfa3248fbe4480a09d" + } + } + } + } + } + }, + "system": { + "macos": { + "ventura": { + "HOMEBREW_VERSION": "4.1.6", + "HOMEBREW_PREFIX": "/opt/homebrew", + "Homebrew/homebrew-core": "api", + "CLT": "14.2.0.0.1.1668646533", + "Xcode": "14.3.1", + "macOS": "13.1" + } + } + } +} diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj index 6491e3386..ddd1ef8bb 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 */, @@ -2663,7 +2679,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.hanners.Mlem; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -2704,7 +2720,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.hanners.Mlem; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; 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 abfa464de..723a32800 100644 --- a/Mlem/API/Models/Person/APIPerson.swift +++ b/Mlem/API/Models/Person/APIPerson.swift @@ -12,18 +12,18 @@ 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 + let admin: Bool? // this is no longer returned on beehaw... let botAccount: Bool let banExpires: Date? let instanceId: Int @@ -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/Extensions/JSONDecoder+Default.swift b/Mlem/Extensions/JSONDecoder+Default.swift index 5eefff44a..13c6ba7d9 100644 --- a/Mlem/Extensions/JSONDecoder+Default.swift +++ b/Mlem/Extensions/JSONDecoder+Default.swift @@ -11,35 +11,34 @@ extension JSONDecoder { static var defaultDecoder: JSONDecoder { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - - let formatter = DateFormatter() - - formatter.timeZone = .gmt - formatter.locale = Locale(identifier: "en_US_POSIX") - + let formats = [ + "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSSSSS", - "yyyy-MM-dd'T'HH:mm:ss.SSSSZ", - "yyyy-MM-dd'T'HH:mm:ss.SSSS", - "yyyy-MM-dd'T'HH:mm:ss", - "yyyy-MM-dd HH:mm:ss.SSSSSS", - "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-dd" + "yyyy-MM-dd'T'HH:mm:ssZ", + "yyyy-MM-dd'T'HH:mm:ss" ] + + let formatters = formats.map { format in + let formatter = DateFormatter() + formatter.timeZone = .gmt + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = format + return formatter + } decoder.dateDecodingStrategy = .custom({ decoder in let container = try decoder.singleValueContainer() let string = try container.decode(String.self) - for format in formats { - formatter.dateFormat = format + for formatter in formatters { if let date = formatter.date(from: string) { return date } } // after some discussion we've agreed to fail the modelling if the date - // does match either of the above, as based on the current API source code + // does match _any_ of the above, as based on the current API source code // it should be one of those throw Swift.DecodingError.dataCorrupted( .init(codingPath: container.codingPath, 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 ea2130e9f..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, @@ -175,7 +175,7 @@ struct UserProfileLabel: View { return UserProfileLabel.flairDeveloper } } - if user.admin { + if user.admin ?? false { return UserProfileLabel.flairAdmin } if user.botAccount { diff --git a/Mlem/Views/Shared/Posts/Post Sizes/Headline Post.swift b/Mlem/Views/Shared/Posts/Post Sizes/Headline Post.swift index 3ed9511aa..20ac0cb50 100644 --- a/Mlem/Views/Shared/Posts/Post Sizes/Headline Post.swift +++ b/Mlem/Views/Shared/Posts/Post Sizes/Headline Post.swift @@ -24,7 +24,7 @@ struct HeadlinePost: View { // computed var usernameColor: Color { - if postView.creator.admin { + if postView.creator.admin ?? false { return .red } if postView.creator.botAccount { 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") + } +}