Skip to content

Commit

Permalink
fix - handle unencoded urls coming from API (mlemgroup#612)
Browse files Browse the repository at this point in the history
  • Loading branch information
mormaer authored Sep 15, 2023
1 parent 8db5af4 commit 6911243
Show file tree
Hide file tree
Showing 24 changed files with 152 additions and 106 deletions.
20 changes: 16 additions & 4 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
503422582AAB798600EFE88D /* AppFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503422572AAB798600EFE88D /* AppFlow.swift */; };
503BA26F2A2C94540052516C /* URL+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503BA26E2A2C94540052516C /* URL+Identifiable.swift */; };
504106CD2A744D7F000AAEF8 /* CommentRepository+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504106CC2A744D7F000AAEF8 /* CommentRepository+Dependency.swift */; };
504ECBAE2AB45B2A006C0B96 /* LemmyURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */; };
504ECBB12AB4B101006C0B96 /* LemmyURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */; };
505240E32A86916500EA4558 /* FavoriteCommunitiesTracker+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505240E22A86916500EA4558 /* FavoriteCommunitiesTracker+Dependency.swift */; };
505240E52A86E32700EA4558 /* CommunityListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505240E42A86E32700EA4558 /* CommunityListModel.swift */; };
505240E72A88D36D00EA4558 /* SectionIndexTitles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505240E62A88D36D00EA4558 /* SectionIndexTitles.swift */; };
Expand Down Expand Up @@ -94,7 +96,6 @@
50D61E5B2AA32B9400A926EC /* APISession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D61E5A2AA32B9400A926EC /* APISession.swift */; };
50D61E5D2AA4904F00A926EC /* FeedTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D61E5C2AA4904F00A926EC /* FeedTracking.swift */; };
50DBB8E02A805836002870B1 /* MockErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBB8DF2A805836002870B1 /* MockErrorHandler.swift */; };
50DBB8E22A80F9E4002870B1 /* APICommunity+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBB8E12A80F9E4002870B1 /* APICommunity+Mock.swift */; };
50EC39B22A346DDC00E014C2 /* URLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EC39B12A346DDC00E014C2 /* URLHandler.swift */; };
50F2851C2A5C5C1500CF8865 /* TokenRefreshView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F2851B2A5C5C1500CF8865 /* TokenRefreshView.swift */; };
50F830F82A4C92BF00D67099 /* FeedTrackerItemProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F830F72A4C92BF00D67099 /* FeedTrackerItemProviding.swift */; };
Expand Down Expand Up @@ -477,6 +478,8 @@
503422572AAB798600EFE88D /* AppFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFlow.swift; sourceTree = "<group>"; };
503BA26E2A2C94540052516C /* URL+Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Identifiable.swift"; sourceTree = "<group>"; };
504106CC2A744D7F000AAEF8 /* CommentRepository+Dependency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CommentRepository+Dependency.swift"; sourceTree = "<group>"; };
504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURL.swift; sourceTree = "<group>"; };
504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURLTests.swift; sourceTree = "<group>"; };
505240E22A86916500EA4558 /* FavoriteCommunitiesTracker+Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FavoriteCommunitiesTracker+Dependency.swift"; sourceTree = "<group>"; };
505240E42A86E32700EA4558 /* CommunityListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityListModel.swift; sourceTree = "<group>"; };
505240E62A88D36D00EA4558 /* SectionIndexTitles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionIndexTitles.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -530,7 +533,6 @@
50D61E5A2AA32B9400A926EC /* APISession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APISession.swift; sourceTree = "<group>"; };
50D61E5C2AA4904F00A926EC /* FeedTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedTracking.swift; sourceTree = "<group>"; };
50DBB8DF2A805836002870B1 /* MockErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockErrorHandler.swift; sourceTree = "<group>"; };
50DBB8E12A80F9E4002870B1 /* APICommunity+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APICommunity+Mock.swift"; sourceTree = "<group>"; };
50EC39B12A346DDC00E014C2 /* URLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLHandler.swift; sourceTree = "<group>"; };
50F2851B2A5C5C1500CF8865 /* TokenRefreshView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenRefreshView.swift; sourceTree = "<group>"; };
50F830F72A4C92BF00D67099 /* FeedTrackerItemProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedTrackerItemProviding.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1046,6 +1048,14 @@
path = TabBar;
sourceTree = "<group>";
};
504ECBAF2AB4B0DF006C0B96 /* Model */ = {
isa = PBXGroup;
children = (
504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */,
);
path = Model;
sourceTree = "<group>";
};
5064D03B2A6DE05000B22EE3 /* Notifications */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1163,7 +1173,6 @@
isa = PBXGroup;
children = (
50DBB8DF2A805836002870B1 /* MockErrorHandler.swift */,
50DBB8E12A80F9E4002870B1 /* APICommunity+Mock.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -1460,6 +1469,7 @@
6363D5D927EE196A00E34822 /* MlemTests */ = {
isa = PBXGroup;
children = (
504ECBAF2AB4B0DF006C0B96 /* Model */,
50CC4A802AA0D5F90074C845 /* Parsers */,
50CC4A7B2A9CFF840074C845 /* Supporting Files */,
50BC1AB72A89741000E3C48B /* Community List */,
Expand Down Expand Up @@ -1526,6 +1536,7 @@
isa = PBXGroup;
children = (
637218032A3A2AAD008C4816 /* HierarchicalComment.swift */,
504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */,
);
path = Internal;
sourceTree = "<group>";
Expand Down Expand Up @@ -2534,6 +2545,7 @@
6D405B032A43E7DB00C65F9C /* Sidebar Header Label.swift in Sources */,
50785F762A9A684300117245 /* SavedAccountTracker+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 */,
Expand Down Expand Up @@ -2808,11 +2820,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504ECBB12AB4B101006C0B96 /* LemmyURLTests.swift in Sources */,
50DBB8E02A805836002870B1 /* MockErrorHandler.swift in Sources */,
6363D5DB27EE196A00E34822 /* MlemTests.swift in Sources */,
50BC1AB92A89744200E3C48B /* CommunityListModelTests.swift in Sources */,
50CC4A782A9CBDF70074C845 /* TimestampedValueTests.swift in Sources */,
50DBB8E22A80F9E4002870B1 /* APICommunity+Mock.swift in Sources */,
50CC4A822AA0D61F0074C845 /* InstanceMetadataParserTests.swift in Sources */,
50C86ABC2A7E50E200277519 /* PersistenceRepositoryTests.swift in Sources */,
);
Expand Down
27 changes: 27 additions & 0 deletions Mlem/API/Internal/LemmyURL.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
}
9 changes: 7 additions & 2 deletions Mlem/API/Models/Community/APICommunity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }
}
12 changes: 9 additions & 3 deletions Mlem/API/Models/Person/APIPerson.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }
}
9 changes: 7 additions & 2 deletions Mlem/API/Models/Posts/APIPost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion Mlem/API/Models/Posts/APIPostReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ 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
let resolverId: Int?
let published: Date
let updated: Date?
}

extension APIPostReport {
var originalUrl: URL? { LemmyURL(string: originalPostUrl)?.url }
}
9 changes: 7 additions & 2 deletions Mlem/API/Models/Site/APISite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ 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
let inboxUrl: String
let publicKey: String
let instanceId: Int
}

extension APISite {
var iconUrl: URL? { LemmyURL(string: icon)?.url }
var bannerUrl: URL? { LemmyURL(string: banner)?.url }
}
4 changes: 2 additions & 2 deletions Mlem/Extensions/Mocks/APICommunity+Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ extension APICommunity {
nsfw: Bool = false,
actorId: URL = .mock,
local: Bool = false,
icon: URL? = nil,
banner: URL? = nil,
icon: String? = nil,
banner: String? = nil,
hidden: Bool = false,
postingRestrictedToMods: Bool = false,
instanceId: Int = 0
Expand Down
6 changes: 3 additions & 3 deletions Mlem/Extensions/Mocks/APIPerson+Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ extension APIPerson {
id: Int = 0,
name: String = "Mock Person",
displayName: String? = nil,
avatar: URL? = nil,
avatar: String? = nil,
banned: Bool = false,
published: Date = .mock,
updated: Date? = nil,
actorId: URL = .mock,
bio: String? = nil,
local: Bool = false,
banner: URL? = nil,
banner: String? = nil,
deleted: Bool = false,
sharedInboxUrl: URL? = nil,
sharedInboxUrl: String? = nil,
matrixUserId: String? = nil,
admin: Bool = false,
botAccount: Bool = false,
Expand Down
4 changes: 2 additions & 2 deletions Mlem/Extensions/Mocks/APIPost+Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ extension APIPost {
static func mock(
id: Int = 0,
name: String = "Mock Post",
url: URL? = nil,
url: String? = nil,
body: String? = nil,
creatorId: Int = 0,
communityId: Int = 0,
Expand All @@ -29,7 +29,7 @@ extension APIPost {
nsfw: Bool = false,
published: Date = .mock,
removed: Bool = false,
thumbnailUrl: URL? = nil,
thumbnailUrl: String? = nil,
updated: Date? = nil
) -> APIPost {
.init(
Expand Down
4 changes: 2 additions & 2 deletions Mlem/Extensions/Mocks/APISite+Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ extension APISite {
name: String = "Mock Site",
sidebar: String? = nil,
published: Date = .mock,
icon: URL? = nil,
banner: URL? = nil,
icon: String? = nil,
banner: String? = nil,
description: String? = nil,
actorId: String? = nil,
lastRefreshedAt: Date = .mock,
Expand Down
4 changes: 2 additions & 2 deletions Mlem/Models/Content/Post Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ struct PostModel {

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
Expand Down
6 changes: 3 additions & 3 deletions Mlem/Models/Trackers/Post Tracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -416,11 +416,11 @@ class PostTracker: ObservableObject {
for post 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 = post.community.icon {
if let communityAvatarLink = post.community.iconUrl {
imageRequests.append(ImageRequest(url: communityAvatarLink.withIcon64Parameters))
}

if let userAvatarLink = post.creator.avatar {
if let userAvatarLink = post.creator.avatarUrl {
imageRequests.append(ImageRequest(url: userAvatarLink.withIcon64Parameters))
}

Expand All @@ -430,7 +430,7 @@ class PostTracker: ObservableObject {
imageRequests.append(ImageRequest(url: url, priority: .high))
case let .link(url):
// websites: load image and favicon
if let baseURL = post.post.url?.host,
if let baseURL = post.post.linkUrl?.host,
let favIconURL = URL(string: "https://www.google.com/s2/favicons?sz=64&domain=\(baseURL)") {
imageRequests.append(ImageRequest(url: favIconURL))
}
Expand Down
2 changes: 1 addition & 1 deletion Mlem/Views/Shared/Accounts/Add Account View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ struct AddSavedInstanceView: View {
instanceLink: instanceURL,
accessToken: response.jwt,
username: username,
avatarUrl: user.avatar
avatarUrl: user.avatarUrl
)

// MARK: - Save the account's credentials into the keychain
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct InstanceSummary: View {
if let siteData {
VStack(spacing: AppConstants.postAndCommentSpacing) {
HStack(alignment: .top, spacing: AppConstants.postAndCommentSpacing) {
instanceIcon(url: siteData.site.icon)
instanceIcon(url: siteData.site.iconUrl)
.padding(.leading, 1)

Spacer()
Expand Down
2 changes: 1 addition & 1 deletion Mlem/Views/Shared/Components/Thumbnail Image View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct ThumbnailImageView: View {
contentMode: .fill
)
.onTapGesture {
if let url = post.post.url {
if let url = post.post.linkUrl {
openURL(url)
markPostAsRead()
}
Expand Down
2 changes: 1 addition & 1 deletion Mlem/Views/Shared/Composer/PostComposerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct PostComposerView: View {
self.editModel = editModel

self._postTitle = State(initialValue: editModel.editPost?.post.name ?? "")
self._postURL = State(initialValue: editModel.editPost?.post.url?.description ?? "")
self._postURL = State(initialValue: editModel.editPost?.post.linkUrl?.description ?? "")
self._postBody = State(initialValue: editModel.editPost?.post.body ?? "")
self._isNSFW = State(initialValue: editModel.editPost?.post.nsfw ?? false)
}
Expand Down
Loading

0 comments on commit 6911243

Please sign in to comment.