From 9552305a7833393f73d5ee7e1be2231daae5669f Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Sun, 28 Mar 2021 23:04:14 -0700 Subject: [PATCH] Escape unicode in all URLs from API --- DB/Sources/DB/Content/AccountRecord.swift | 8 ++-- DB/Sources/DB/Content/FeaturedTagRecord.swift | 2 +- .../DB/Content/IdentityProofRecord.swift | 4 +- DB/Sources/DB/Content/InstanceRecord.swift | 2 +- DB/Sources/DB/Entities/Identity.swift | 14 +++---- Extensions/CollectionItem+Extensions.swift | 20 +++++----- ...NSMutableAttributedString+Extensions.swift | 11 ++---- .../Sources/Mastodon/Entities/Account.swift | 16 ++++---- .../Entities/AnnouncementReaction.swift | 4 +- .../Mastodon/Entities/Attachment.swift | 6 +-- Mastodon/Sources/Mastodon/Entities/Card.swift | 4 +- .../Sources/Mastodon/Entities/Emoji.swift | 4 +- .../Mastodon/Entities/FeaturedTag.swift | 4 +- .../Mastodon/Entities/IdentityProof.swift | 10 +++-- .../Sources/Mastodon/Entities/Instance.swift | 6 +-- .../Sources/Mastodon/Entities/Mention.swift | 2 +- .../Mastodon/Entities/PushNotification.swift | 2 +- .../Mastodon/Entities/PushSubscription.swift | 2 +- Mastodon/Sources/Mastodon/Entities/Tag.swift | 2 +- .../Mastodon/Entities/UnicodeURL.swift | 37 +++++++++++++++++++ .../NotificationService.swift | 2 +- .../Services/NavigationService.swift | 6 ++- .../AddIdentityViewController.swift | 2 +- .../EditAttachmentViewController.swift | 6 +-- View Controllers/ImageViewController.swift | 6 +-- View Controllers/TableViewController.swift | 4 +- .../View Models/AccountViewModel.swift | 8 ++-- .../View Models/CardViewModel.swift | 4 +- .../View Models/EmojiViewModel.swift | 6 +-- .../View Models/StatusViewModel.swift | 10 ++--- Views/UIKit/AttachmentView.swift | 4 +- Views/UIKit/CompositionView.swift | 2 +- .../Content Configurations/EmojiView.swift | 11 +----- .../Content Views/AutocompleteItemView.swift | 2 +- Views/UIKit/Content Views/InstanceView.swift | 2 +- Views/UIKit/EditThumbnailView.swift | 6 +-- .../UIKit/SecondaryNavigationTitleView.swift | 2 +- 37 files changed, 136 insertions(+), 107 deletions(-) create mode 100644 Mastodon/Sources/Mastodon/Entities/UnicodeURL.swift diff --git a/DB/Sources/DB/Content/AccountRecord.swift b/DB/Sources/DB/Content/AccountRecord.swift index d827a5b..edc1927 100644 --- a/DB/Sources/DB/Content/AccountRecord.swift +++ b/DB/Sources/DB/Content/AccountRecord.swift @@ -16,10 +16,10 @@ struct AccountRecord: ContentDatabaseRecord, Hashable { let statusesCount: Int let note: HTML let url: String - let avatar: URL - let avatarStatic: URL - let header: URL - let headerStatic: URL + let avatar: UnicodeURL + let avatarStatic: UnicodeURL + let header: UnicodeURL + let headerStatic: UnicodeURL let fields: [Account.Field] let emojis: [Emoji] let bot: Bool diff --git a/DB/Sources/DB/Content/FeaturedTagRecord.swift b/DB/Sources/DB/Content/FeaturedTagRecord.swift index c577a7e..1f2aab5 100644 --- a/DB/Sources/DB/Content/FeaturedTagRecord.swift +++ b/DB/Sources/DB/Content/FeaturedTagRecord.swift @@ -7,7 +7,7 @@ import Mastodon struct FeaturedTagRecord: ContentDatabaseRecord, Hashable { let id: FeaturedTag.Id let name: String - let url: URL + let url: UnicodeURL let statusesCount: Int let lastStatusAt: Date let accountId: Account.Id diff --git a/DB/Sources/DB/Content/IdentityProofRecord.swift b/DB/Sources/DB/Content/IdentityProofRecord.swift index 2583e86..2f4688e 100644 --- a/DB/Sources/DB/Content/IdentityProofRecord.swift +++ b/DB/Sources/DB/Content/IdentityProofRecord.swift @@ -8,8 +8,8 @@ struct IdentityProofRecord: ContentDatabaseRecord, Hashable { let accountId: Account.Id let provider: String let providerUsername: String - let profileUrl: URL - let proofUrl: URL + let profileUrl: UnicodeURL + let proofUrl: UnicodeURL let updatedAt: Date } diff --git a/DB/Sources/DB/Content/InstanceRecord.swift b/DB/Sources/DB/Content/InstanceRecord.swift index 68aed44..b8bb300 100644 --- a/DB/Sources/DB/Content/InstanceRecord.swift +++ b/DB/Sources/DB/Content/InstanceRecord.swift @@ -17,7 +17,7 @@ struct InstanceRecord: ContentDatabaseRecord, Hashable { let invitesEnabled: Bool let urls: Instance.URLs let stats: Instance.Stats - let thumbnail: URL? + let thumbnail: UnicodeURL? let contactAccountId: Account.Id? let maxTootChars: Int? } diff --git a/DB/Sources/DB/Entities/Identity.swift b/DB/Sources/DB/Entities/Identity.swift index 7f0a583..444be8e 100644 --- a/DB/Sources/DB/Entities/Identity.swift +++ b/DB/Sources/DB/Entities/Identity.swift @@ -21,9 +21,9 @@ public extension Identity { struct Instance: Codable, Hashable { public let uri: String - public let streamingAPI: URL + public let streamingAPI: UnicodeURL public let title: String - public let thumbnail: URL? + public let thumbnail: UnicodeURL? public let version: String public let maxTootChars: Int? } @@ -34,10 +34,10 @@ public extension Identity { public let username: String public let displayName: String public let url: String - public let avatar: URL - public let avatarStatic: URL - public let header: URL - public let headerStatic: URL + public let avatar: UnicodeURL + public let avatarStatic: UnicodeURL + public let header: UnicodeURL + public let headerStatic: UnicodeURL public let emojis: [Emoji] public let followRequestCount: Int } @@ -59,7 +59,7 @@ public extension Identity { return instance?.title ?? url.host ?? url.absoluteString } - var image: URL? { account?.avatar ?? instance?.thumbnail } + var image: URL? { (account?.avatar ?? instance?.thumbnail)?.url } } public extension Identity.Preferences { diff --git a/Extensions/CollectionItem+Extensions.swift b/Extensions/CollectionItem+Extensions.swift index ddba36c..c3f2bce 100644 --- a/Extensions/CollectionItem+Extensions.swift +++ b/Extensions/CollectionItem+Extensions.swift @@ -89,15 +89,14 @@ extension CollectionItem { private extension Account { func mediaPrefetchURLs(identityContext: IdentityContext) -> Set { - var urls = Set(emojis.compactMap { - identityContext.appPreferences.animateCustomEmojis ? $0.url : $0.staticUrl - } - .compactMap(URL.init(string:))) + var urls = Set(emojis.map { + (identityContext.appPreferences.animateCustomEmojis ? $0.url : $0.staticUrl).url + }) if identityContext.appPreferences.animateAvatars == .everywhere { - urls.insert(avatar) + urls.insert(avatar.url) } else { - urls.insert(avatarStatic) + urls.insert(avatarStatic.url) } return urls @@ -107,10 +106,9 @@ private extension Account { private extension Status { func mediaPrefetchURLs(identityContext: IdentityContext) -> Set { displayStatus.account.mediaPrefetchURLs(identityContext: identityContext) - .union(displayStatus.mediaAttachments.compactMap(\.previewUrl)) - .union(displayStatus.emojis.compactMap { - identityContext.appPreferences.animateCustomEmojis ? $0.url : $0.staticUrl - } - .compactMap(URL.init(string:))) + .union(displayStatus.mediaAttachments.compactMap(\.previewUrl?.url)) + .union(displayStatus.emojis.map { + (identityContext.appPreferences.animateCustomEmojis ? $0.url : $0.staticUrl).url + }) } } diff --git a/Extensions/NSMutableAttributedString+Extensions.swift b/Extensions/NSMutableAttributedString+Extensions.swift index 8dd661e..abe63bf 100644 --- a/Extensions/NSMutableAttributedString+Extensions.swift +++ b/Extensions/NSMutableAttributedString+Extensions.swift @@ -12,15 +12,12 @@ extension NSMutableAttributedString { while let tokenRange = string.range(of: token) { let attachment = AnimatedTextAttachment() - let imageURL: URL? + let imageURL: URL - if identityContext.appPreferences.animateCustomEmojis, - let urlString = emoji.url { - imageURL = URL(stringEscapingPath: urlString) - } else if let staticURLString = emoji.staticUrl { - imageURL = URL(stringEscapingPath: staticURLString) + if identityContext.appPreferences.animateCustomEmojis { + imageURL = emoji.url.url } else { - imageURL = nil + imageURL = emoji.staticUrl.url } attachment.imageView.sd_setImage(with: imageURL) { image, _, _, _ in diff --git a/Mastodon/Sources/Mastodon/Entities/Account.swift b/Mastodon/Sources/Mastodon/Entities/Account.swift index f1c5b7d..05c0059 100644 --- a/Mastodon/Sources/Mastodon/Entities/Account.swift +++ b/Mastodon/Sources/Mastodon/Entities/Account.swift @@ -14,10 +14,10 @@ public final class Account: Codable, Identifiable { public let statusesCount: Int public let note: HTML public let url: String - public let avatar: URL - public let avatarStatic: URL - public let header: URL - public let headerStatic: URL + public let avatar: UnicodeURL + public let avatarStatic: UnicodeURL + public let header: UnicodeURL + public let headerStatic: UnicodeURL public let fields: [Field] public let emojis: [Emoji] @DecodableDefault.False public private(set) var bot: Bool @@ -36,10 +36,10 @@ public final class Account: Codable, Identifiable { statusesCount: Int, note: HTML, url: String, - avatar: URL, - avatarStatic: URL, - header: URL, - headerStatic: URL, + avatar: UnicodeURL, + avatarStatic: UnicodeURL, + header: UnicodeURL, + headerStatic: UnicodeURL, fields: [Account.Field], emojis: [Emoji], bot: Bool, diff --git a/Mastodon/Sources/Mastodon/Entities/AnnouncementReaction.swift b/Mastodon/Sources/Mastodon/Entities/AnnouncementReaction.swift index 51c3558..649ceed 100644 --- a/Mastodon/Sources/Mastodon/Entities/AnnouncementReaction.swift +++ b/Mastodon/Sources/Mastodon/Entities/AnnouncementReaction.swift @@ -6,6 +6,6 @@ public struct AnnouncementReaction: Codable, Hashable { public let name: String public let count: Int public let me: Bool - public let url: URL? - public let staticUrl: URL? + public let url: UnicodeURL? + public let staticUrl: UnicodeURL? } diff --git a/Mastodon/Sources/Mastodon/Entities/Attachment.swift b/Mastodon/Sources/Mastodon/Entities/Attachment.swift index 80e3668..93c77db 100644 --- a/Mastodon/Sources/Mastodon/Entities/Attachment.swift +++ b/Mastodon/Sources/Mastodon/Entities/Attachment.swift @@ -34,9 +34,9 @@ public struct Attachment: Codable, Hashable { public let id: Id public let type: AttachmentType - public let url: URL - public let remoteUrl: URL? - public let previewUrl: URL? + public let url: UnicodeURL + public let remoteUrl: UnicodeURL? + public let previewUrl: UnicodeURL? public let meta: Meta? public let description: String? public let blurhash: String? diff --git a/Mastodon/Sources/Mastodon/Entities/Card.swift b/Mastodon/Sources/Mastodon/Entities/Card.swift index 2c22dd2..d7b0ac3 100644 --- a/Mastodon/Sources/Mastodon/Entities/Card.swift +++ b/Mastodon/Sources/Mastodon/Entities/Card.swift @@ -9,7 +9,7 @@ public struct Card: Codable, Hashable { public static var unknownCase: Self { .unknown } } - public let url: URL + public let url: UnicodeURL public let title: String public let description: String public let type: CardType @@ -20,6 +20,6 @@ public struct Card: Codable, Hashable { public let html: String? public let width: Int? public let height: Int? - public let image: URL? + public let image: UnicodeURL? public let embedUrl: String? } diff --git a/Mastodon/Sources/Mastodon/Entities/Emoji.swift b/Mastodon/Sources/Mastodon/Entities/Emoji.swift index e18da90..a4b4d90 100644 --- a/Mastodon/Sources/Mastodon/Entities/Emoji.swift +++ b/Mastodon/Sources/Mastodon/Entities/Emoji.swift @@ -4,8 +4,8 @@ import Foundation public struct Emoji: Codable, Hashable { public let shortcode: String - public let staticUrl: String? - public let url: String? + public let staticUrl: UnicodeURL + public let url: UnicodeURL public let visibleInPicker: Bool public let category: String? } diff --git a/Mastodon/Sources/Mastodon/Entities/FeaturedTag.swift b/Mastodon/Sources/Mastodon/Entities/FeaturedTag.swift index df0275a..6aa0b06 100644 --- a/Mastodon/Sources/Mastodon/Entities/FeaturedTag.swift +++ b/Mastodon/Sources/Mastodon/Entities/FeaturedTag.swift @@ -5,11 +5,11 @@ import Foundation public struct FeaturedTag: Codable, Hashable { public let id: Id public let name: String - public let url: URL + public let url: UnicodeURL public let statusesCount: Int public let lastStatusAt: Date - public init(id: FeaturedTag.Id, name: String, url: URL, statusesCount: Int, lastStatusAt: Date) { + public init(id: FeaturedTag.Id, name: String, url: UnicodeURL, statusesCount: Int, lastStatusAt: Date) { self.id = id self.name = name self.url = url diff --git a/Mastodon/Sources/Mastodon/Entities/IdentityProof.swift b/Mastodon/Sources/Mastodon/Entities/IdentityProof.swift index d6c071b..4c478bb 100644 --- a/Mastodon/Sources/Mastodon/Entities/IdentityProof.swift +++ b/Mastodon/Sources/Mastodon/Entities/IdentityProof.swift @@ -5,11 +5,15 @@ import Foundation public struct IdentityProof: Codable, Hashable { public let provider: String public let providerUsername: String - public let profileUrl: URL - public let proofUrl: URL + public let profileUrl: UnicodeURL + public let proofUrl: UnicodeURL public let updatedAt: Date - public init(provider: String, providerUsername: String, profileUrl: URL, proofUrl: URL, updatedAt: Date) { + public init(provider: String, + providerUsername: String, + profileUrl: UnicodeURL, + proofUrl: UnicodeURL, + updatedAt: Date) { self.provider = provider self.providerUsername = providerUsername self.profileUrl = profileUrl diff --git a/Mastodon/Sources/Mastodon/Entities/Instance.swift b/Mastodon/Sources/Mastodon/Entities/Instance.swift index 06a4adc..9a6dec2 100644 --- a/Mastodon/Sources/Mastodon/Entities/Instance.swift +++ b/Mastodon/Sources/Mastodon/Entities/Instance.swift @@ -4,7 +4,7 @@ import Foundation public struct Instance: Codable, Hashable { public struct URLs: Codable, Hashable { - public let streamingApi: URL + public let streamingApi: UnicodeURL } public struct Stats: Codable, Hashable { @@ -25,7 +25,7 @@ public struct Instance: Codable, Hashable { @DecodableDefault.False public private(set) var invitesEnabled: Bool public let urls: URLs public let stats: Stats - public let thumbnail: URL? + public let thumbnail: UnicodeURL? public let contactAccount: Account? public let maxTootChars: Int? @@ -37,7 +37,7 @@ public struct Instance: Codable, Hashable { version: String, urls: Instance.URLs, stats: Instance.Stats, - thumbnail: URL?, + thumbnail: UnicodeURL?, contactAccount: Account?, maxTootChars: Int?) { self.uri = uri diff --git a/Mastodon/Sources/Mastodon/Entities/Mention.swift b/Mastodon/Sources/Mastodon/Entities/Mention.swift index 1a81b50..f719398 100644 --- a/Mastodon/Sources/Mastodon/Entities/Mention.swift +++ b/Mastodon/Sources/Mastodon/Entities/Mention.swift @@ -3,7 +3,7 @@ import Foundation public struct Mention: Codable, Hashable { - public let url: URL + public let url: UnicodeURL public let username: String public let acct: String public let id: Account.Id diff --git a/Mastodon/Sources/Mastodon/Entities/PushNotification.swift b/Mastodon/Sources/Mastodon/Entities/PushNotification.swift index 7e4a527..c02329a 100644 --- a/Mastodon/Sources/Mastodon/Entities/PushNotification.swift +++ b/Mastodon/Sources/Mastodon/Entities/PushNotification.swift @@ -6,7 +6,7 @@ public struct PushNotification: Codable { public let accessToken: String public let body: String public let title: String - public let icon: URL + public let icon: UnicodeURL public let notificationId: Int public let notificationType: MastodonNotification.NotificationType public let preferredLocale: String diff --git a/Mastodon/Sources/Mastodon/Entities/PushSubscription.swift b/Mastodon/Sources/Mastodon/Entities/PushSubscription.swift index 6e82805..b719a56 100644 --- a/Mastodon/Sources/Mastodon/Entities/PushSubscription.swift +++ b/Mastodon/Sources/Mastodon/Entities/PushSubscription.swift @@ -13,7 +13,7 @@ public struct PushSubscription: Codable { @DecodableDefault.True public var status: Bool } - public let endpoint: URL + public let endpoint: UnicodeURL public let alerts: Alerts public let serverKey: String } diff --git a/Mastodon/Sources/Mastodon/Entities/Tag.swift b/Mastodon/Sources/Mastodon/Entities/Tag.swift index 266ae87..6f6aa40 100644 --- a/Mastodon/Sources/Mastodon/Entities/Tag.swift +++ b/Mastodon/Sources/Mastodon/Entities/Tag.swift @@ -4,7 +4,7 @@ import Foundation public struct Tag: Codable, Hashable { public let name: String - public let url: URL + public let url: UnicodeURL public let history: [History]? } diff --git a/Mastodon/Sources/Mastodon/Entities/UnicodeURL.swift b/Mastodon/Sources/Mastodon/Entities/UnicodeURL.swift new file mode 100644 index 0000000..6c9725d --- /dev/null +++ b/Mastodon/Sources/Mastodon/Entities/UnicodeURL.swift @@ -0,0 +1,37 @@ +// Copyright © 2021 Metabolist. All rights reserved. + +import Foundation + +public struct UnicodeURL: Hashable { + public let raw: String + public let url: URL +} + +extension UnicodeURL: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + raw = try container.decode(String.self) + + if let url = URL(string: raw) { + self.url = url + } else if let escaped = raw.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) { + let colonUnescaped = escaped.replacingOccurrences( + of: "%3A", + with: ":", + range: escaped.range(of: "%3A")) + + guard let url = URL(string: colonUnescaped) else { throw URLError(.badURL) } + + self.url = url + } else { + throw URLError(.badURL) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + try container.encode(raw) + } +} diff --git a/Notification Service Extension/NotificationService.swift b/Notification Service Extension/NotificationService.swift index f7ecc8b..8114883 100644 --- a/Notification Service Extension/NotificationService.swift +++ b/Notification Service Extension/NotificationService.swift @@ -54,7 +54,7 @@ final class NotificationService: UNNotificationServiceExtension { bestAttemptContent.subtitle = handle } - Self.attachment(imageURL: pushNotification.icon) + Self.attachment(imageURL: pushNotification.icon.url) .map { [$0] } .replaceError(with: []) .handleEvents(receiveOutput: { bestAttemptContent.attachments = $0 }) diff --git a/ServiceLayer/Sources/ServiceLayer/Services/NavigationService.swift b/ServiceLayer/Sources/ServiceLayer/Services/NavigationService.swift index edbc0ce..689a79a 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/NavigationService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/NavigationService.swift @@ -122,7 +122,7 @@ public extension NavigationService { private extension NavigationService { func tag(url: URL) -> String? { - if status?.tags.first(where: { $0.url.path.lowercased() == url.path.lowercased() }) != nil { + if status?.tags.first(where: { $0.url.url.path.lowercased() == url.path.lowercased() }) != nil { return url.lastPathComponent } else if mastodonAPIClient.instanceURL.host == url.host { @@ -133,7 +133,9 @@ private extension NavigationService { } func accountId(url: URL) -> String? { - if let mentionId = status?.mentions.first(where: { $0.url.path.lowercased() == url.path.lowercased() })?.id { + if let mentionId = status?.mentions.first(where: { + $0.url.url.path.lowercased() == url.path.lowercased() + })?.id { return mentionId } else if mastodonAPIClient.instanceURL.host == url.host { diff --git a/View Controllers/AddIdentityViewController.swift b/View Controllers/AddIdentityViewController.swift index 35723a3..b5bc4b9 100644 --- a/View Controllers/AddIdentityViewController.swift +++ b/View Controllers/AddIdentityViewController.swift @@ -262,7 +262,7 @@ private extension AddIdentityViewController { if let instance = instance { self.instanceTitleLabel.text = instance.title self.instanceURLLabel.text = instance.uri - self.instanceImageView.sd_setImage(with: instance.thumbnail) + self.instanceImageView.sd_setImage(with: instance.thumbnail?.url) self.instanceStackView.isHidden_stackViewSafe = false if instance.registrations { diff --git a/View Controllers/EditAttachmentViewController.swift b/View Controllers/EditAttachmentViewController.swift index 80ec2b5..5f2d34d 100644 --- a/View Controllers/EditAttachmentViewController.swift +++ b/View Controllers/EditAttachmentViewController.swift @@ -52,9 +52,9 @@ final class EditAttachmentViewController: UIViewController { let player: AVPlayer if viewModel.attachment.type == .video { - player = PlayerCache.shared.player(url: viewModel.attachment.url) + player = PlayerCache.shared.player(url: viewModel.attachment.url.url) } else { - player = AVPlayer(url: viewModel.attachment.url) + player = AVPlayer(url: viewModel.attachment.url.url) } player.isMuted = false @@ -188,7 +188,7 @@ private extension EditAttachmentViewController { func detectTextFromPicture() { SDWebImageManager.shared.loadImage( - with: viewModel.attachment.url, + with: viewModel.attachment.url.url, options: [], progress: nil) { image, _, _, _, _, _ in guard let cgImage = image?.cgImage else { return } diff --git a/View Controllers/ImageViewController.swift b/View Controllers/ImageViewController.swift index 480f357..a24c7a9 100644 --- a/View Controllers/ImageViewController.swift +++ b/View Controllers/ImageViewController.swift @@ -127,7 +127,7 @@ final class ImageViewController: UIViewController { playerView.isHidden = true let placeholderImage: UIImage? - let cachedImageKey = viewModel.attachment.previewUrl?.absoluteString + let cachedImageKey = viewModel.attachment.previewUrl?.url.absoluteString let cachedImage = SDImageCache.shared.imageFromCache(forKey: cachedImageKey) if cachedImage != nil { @@ -139,7 +139,7 @@ final class ImageViewController: UIViewController { placeholderImage = nil } - imageView.sd_setImage(with: viewModel.attachment.url, + imageView.sd_setImage(with: viewModel.attachment.url.url, placeholderImage: placeholderImage) { _, error, _, _ in if error != nil { let alertItem = AlertItem(error: ImageError.unableToLoad) @@ -150,7 +150,7 @@ final class ImageViewController: UIViewController { case .gifv: playerView.tag = viewModel.tag imageView.isHidden = true - let player = PlayerCache.shared.player(url: viewModel.attachment.url) + let player = PlayerCache.shared.player(url: viewModel.attachment.url.url) player.isMuted = true diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index d96cb77..80b08bb 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -589,9 +589,9 @@ private extension TableViewController { let player: AVPlayer if attachmentViewModel.attachment.type == .video { - player = PlayerCache.shared.player(url: attachmentViewModel.attachment.url) + player = PlayerCache.shared.player(url: attachmentViewModel.attachment.url.url) } else { - player = AVPlayer(url: attachmentViewModel.attachment.url) + player = AVPlayer(url: attachmentViewModel.attachment.url.url) } playerViewController.delegate = self diff --git a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift index 8de168a..a8ef7bb 100644 --- a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift @@ -29,9 +29,9 @@ public extension AccountViewModel { var headerURL: URL { if identityContext.appPreferences.animateHeaders { - return accountService.account.header + return accountService.account.header.url } else { - return accountService.account.headerStatic + return accountService.account.headerStatic.url } } @@ -66,9 +66,9 @@ public extension AccountViewModel { func avatarURL(profile: Bool = false) -> URL { if identityContext.appPreferences.animateAvatars == .everywhere || (identityContext.appPreferences.animateAvatars == .profiles && profile) { - return accountService.account.avatar + return accountService.account.avatar.url } else { - return accountService.account.avatarStatic + return accountService.account.avatarStatic.url } } diff --git a/ViewModels/Sources/ViewModels/View Models/CardViewModel.swift b/ViewModels/Sources/ViewModels/View Models/CardViewModel.swift index 83bd4e9..56e17ed 100644 --- a/ViewModels/Sources/ViewModels/View Models/CardViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/CardViewModel.swift @@ -12,11 +12,11 @@ public struct CardViewModel { } public extension CardViewModel { - var url: URL { card.url } + var url: URL { card.url.url } var title: String { card.title } var description: String { card.description } - var imageURL: URL? { card.image } + var imageURL: URL? { card.image?.url } } diff --git a/ViewModels/Sources/ViewModels/View Models/EmojiViewModel.swift b/ViewModels/Sources/ViewModels/View Models/EmojiViewModel.swift index d61e72e..de7ecc3 100644 --- a/ViewModels/Sources/ViewModels/View Models/EmojiViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/EmojiViewModel.swift @@ -18,13 +18,13 @@ public extension EmojiViewModel { var system: Bool { emoji.system } - var url: String? { + var url: URL? { guard case let .custom(emoji, _) = emoji else { return nil } if identityContext.appPreferences.animateCustomEmojis { - return emoji.url + return emoji.url.url } else { - return emoji.staticUrl + return emoji.staticUrl.url } } } diff --git a/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift b/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift index b142340..70bf877 100644 --- a/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift @@ -83,17 +83,17 @@ public extension StatusViewModel { var avatarURL: URL { if identityContext.appPreferences.animateAvatars == .everywhere { - return statusService.status.displayStatus.account.avatar + return statusService.status.displayStatus.account.avatar.url } else { - return statusService.status.displayStatus.account.avatarStatic + return statusService.status.displayStatus.account.avatarStatic.url } } var rebloggerAvatarURL: URL { if identityContext.appPreferences.animateAvatars == .everywhere { - return statusService.status.account.avatar + return statusService.status.account.avatar.url } else { - return statusService.status.account.avatarStatic + return statusService.status.account.avatarStatic.url } } @@ -351,7 +351,7 @@ public extension StatusViewModel { func attachmentSelected(viewModel: AttachmentViewModel) { if viewModel.attachment.type == .unknown, let remoteUrl = viewModel.attachment.remoteUrl { - urlSelected(remoteUrl) + urlSelected(remoteUrl.url) } else { eventsSubject.send(Just(.attachment(viewModel, self)).setFailureType(to: Error.self).eraseToAnyPublisher()) } diff --git a/Views/UIKit/AttachmentView.swift b/Views/UIKit/AttachmentView.swift index b9ea517..65c556f 100644 --- a/Views/UIKit/AttachmentView.swift +++ b/Views/UIKit/AttachmentView.swift @@ -64,7 +64,7 @@ final class AttachmentView: UIView { extension AttachmentView { func play() { - let player = PlayerCache.shared.player(url: viewModel.attachment.url) + let player = PlayerCache.shared.player(url: viewModel.attachment.url.url) playerCancellable = NotificationCenter.default.publisher( for: .AVPlayerItemDidPlayToEndTime, @@ -180,7 +180,7 @@ private extension AttachmentView { } imageView.sd_setImage( - with: viewModel.attachment.previewUrl, + with: viewModel.attachment.previewUrl?.url, placeholderImage: placeholderImage) { [weak self] _, _, _, _ in self?.layoutSubviews() } diff --git a/Views/UIKit/CompositionView.swift b/Views/UIKit/CompositionView.swift index 25571b6..d5c30c9 100644 --- a/Views/UIKit/CompositionView.swift +++ b/Views/UIKit/CompositionView.swift @@ -199,7 +199,7 @@ private extension CompositionView { ? $0.identity.account?.avatar : $0.identity.account?.avatarStatic - self.avatarImageView.sd_setImage(with: avatarURL) + self.avatarImageView.sd_setImage(with: avatarURL?.url) self.changeIdentityButton.accessibilityLabel = $0.identity.handle self.changeIdentityButton.accessibilityHint = NSLocalizedString("compose.change-identity-button.accessibility-hint", comment: "") diff --git a/Views/UIKit/Content Configurations/EmojiView.swift b/Views/UIKit/Content Configurations/EmojiView.swift index 194294c..09d54c4 100644 --- a/Views/UIKit/Content Configurations/EmojiView.swift +++ b/Views/UIKit/Content Configurations/EmojiView.swift @@ -76,16 +76,7 @@ private extension EmojiView { emojiLabel.isHidden = true emojiLabel.text = nil imageView.isHidden = false - - let url: URL? - - if let urlString = emojiConfiguration.viewModel.url { - url = URL(stringEscapingPath: urlString) - } else { - url = nil - } - - imageView.sd_setImage(with: url) + imageView.sd_setImage(with: emojiConfiguration.viewModel.url) } accessibilityLabel = emojiConfiguration.viewModel.name diff --git a/Views/UIKit/Content Views/AutocompleteItemView.swift b/Views/UIKit/Content Views/AutocompleteItemView.swift index 4934a0a..bb3d263 100644 --- a/Views/UIKit/Content Views/AutocompleteItemView.swift +++ b/Views/UIKit/Content Views/AutocompleteItemView.swift @@ -73,7 +73,7 @@ private extension AutocompleteItemView { switch autocompleteItemConfiguration.item { case let .account(account): let appPreferences = autocompleteItemConfiguration.identityContext.appPreferences - let avatarURL = appPreferences.animateAvatars == .everywhere ? account.avatar : account.avatarStatic + let avatarURL = (appPreferences.animateAvatars == .everywhere ? account.avatar : account.avatarStatic).url imageView.sd_setImage(with: avatarURL) imageView.isHidden = false diff --git a/Views/UIKit/Content Views/InstanceView.swift b/Views/UIKit/Content Views/InstanceView.swift index b8b527e..e51896a 100644 --- a/Views/UIKit/Content Views/InstanceView.swift +++ b/Views/UIKit/Content Views/InstanceView.swift @@ -77,7 +77,7 @@ private extension InstanceView { func applyInstanceConfiguration() { let viewModel = instanceConfiguration.viewModel - imageView.sd_setImage(with: viewModel.instance.thumbnail) + imageView.sd_setImage(with: viewModel.instance.thumbnail?.url) imageView.autoPlayAnimatedImage = !UIAccessibility.isReduceMotionEnabled titleLabel.text = viewModel.instance.title diff --git a/Views/UIKit/EditThumbnailView.swift b/Views/UIKit/EditThumbnailView.swift index 080294f..6a2522b 100644 --- a/Views/UIKit/EditThumbnailView.swift +++ b/Views/UIKit/EditThumbnailView.swift @@ -138,7 +138,7 @@ private extension EditThumbnailView { previewImageView.contentMode = .scaleAspectFill previewImageView.clipsToBounds = true previewImageView.layer.cornerRadius = .defaultCornerRadius - previewImageView.sd_setImage(with: viewModel.attachment.previewUrl) + previewImageView.sd_setImage(with: viewModel.attachment.previewUrl?.url) switch viewModel.attachment.type { case .image: @@ -151,10 +151,10 @@ private extension EditThumbnailView { placeholderImage = nil } - imageView.sd_setImage(with: viewModel.attachment.previewUrl, placeholderImage: placeholderImage) + imageView.sd_setImage(with: viewModel.attachment.previewUrl?.url, placeholderImage: placeholderImage) case .gifv: imageView.isHidden = true - let player = PlayerCache.shared.player(url: viewModel.attachment.url) + let player = PlayerCache.shared.player(url: viewModel.attachment.url.url) player.isMuted = true diff --git a/Views/UIKit/SecondaryNavigationTitleView.swift b/Views/UIKit/SecondaryNavigationTitleView.swift index e813b27..e32c9d3 100644 --- a/Views/UIKit/SecondaryNavigationTitleView.swift +++ b/Views/UIKit/SecondaryNavigationTitleView.swift @@ -69,7 +69,7 @@ private extension SecondaryNavigationTitleView { ? viewModel.identityContext.identity.account?.avatar : viewModel.identityContext.identity.account?.avatarStatic - avatarImageView.sd_setImage(with: avatarURL) + avatarImageView.sd_setImage(with: avatarURL?.url) if let displayName = viewModel.identityContext.identity.account?.displayName, !displayName.isEmpty {