fix: GIF not load in some scene issue
This commit is contained in:
parent
a614e0c156
commit
86da7a8ba1
|
@ -273,7 +273,6 @@
|
||||||
DB49A63D25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB49A63C25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift */; };
|
DB49A63D25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB49A63C25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift */; };
|
||||||
DB5086A525CC0B7000C2C187 /* AvatarBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086A425CC0B7000C2C187 /* AvatarBarButtonItem.swift */; };
|
DB5086A525CC0B7000C2C187 /* AvatarBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086A425CC0B7000C2C187 /* AvatarBarButtonItem.swift */; };
|
||||||
DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */; };
|
DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */; };
|
||||||
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DB5086B725CC0D6400C2C187 /* Kingfisher */; };
|
|
||||||
DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */; };
|
DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */; };
|
||||||
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D170262832380062B7A1 /* BlurHashDecode.swift */; };
|
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D170262832380062B7A1 /* BlurHashDecode.swift */; };
|
||||||
DB51D173262832380062B7A1 /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D171262832380062B7A1 /* BlurHashEncode.swift */; };
|
DB51D173262832380062B7A1 /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D171262832380062B7A1 /* BlurHashEncode.swift */; };
|
||||||
|
@ -1146,7 +1145,6 @@
|
||||||
DBB525082611EAC0002F1F29 /* Tabman in Frameworks */,
|
DBB525082611EAC0002F1F29 /* Tabman in Frameworks */,
|
||||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
||||||
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */,
|
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */,
|
||||||
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */,
|
|
||||||
DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */,
|
DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */,
|
||||||
DBAC6483267D0B21007FE9FD /* DifferenceKit in Frameworks */,
|
DBAC6483267D0B21007FE9FD /* DifferenceKit in Frameworks */,
|
||||||
DB0E2D2E26833FF700865C3C /* NukeFLAnimatedImagePlugin in Frameworks */,
|
DB0E2D2E26833FF700865C3C /* NukeFLAnimatedImagePlugin in Frameworks */,
|
||||||
|
@ -2677,7 +2675,6 @@
|
||||||
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */,
|
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */,
|
||||||
2D42FF6025C8177C004A627A /* ActiveLabel */,
|
2D42FF6025C8177C004A627A /* ActiveLabel */,
|
||||||
DB0140BC25C40D7500F9F3CF /* CommonOSLog */,
|
DB0140BC25C40D7500F9F3CF /* CommonOSLog */,
|
||||||
DB5086B725CC0D6400C2C187 /* Kingfisher */,
|
|
||||||
2D5981B925E4D7F8000FB903 /* ThirdPartyMailer */,
|
2D5981B925E4D7F8000FB903 /* ThirdPartyMailer */,
|
||||||
2D939AC725EE14620076FA61 /* CropViewController */,
|
2D939AC725EE14620076FA61 /* CropViewController */,
|
||||||
DB9A487D2603456B008B817C /* UITextView+Placeholder */,
|
DB9A487D2603456B008B817C /* UITextView+Placeholder */,
|
||||||
|
@ -2871,7 +2868,6 @@
|
||||||
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */,
|
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */,
|
||||||
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */,
|
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */,
|
||||||
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */,
|
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */,
|
||||||
DB5086B625CC0D6400C2C187 /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
|
||||||
2D5981B825E4D7F8000FB903 /* XCRemoteSwiftPackageReference "ThirdPartyMailer" */,
|
2D5981B825E4D7F8000FB903 /* XCRemoteSwiftPackageReference "ThirdPartyMailer" */,
|
||||||
2D939AC625EE14620076FA61 /* XCRemoteSwiftPackageReference "TOCropViewController" */,
|
2D939AC625EE14620076FA61 /* XCRemoteSwiftPackageReference "TOCropViewController" */,
|
||||||
DB9A487C2603456B008B817C /* XCRemoteSwiftPackageReference "UITextView-Placeholder" */,
|
DB9A487C2603456B008B817C /* XCRemoteSwiftPackageReference "UITextView-Placeholder" */,
|
||||||
|
@ -4801,14 +4797,6 @@
|
||||||
minimumVersion = 4.1.0;
|
minimumVersion = 4.1.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
DB5086B625CC0D6400C2C187 /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/onevcat/Kingfisher.git";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 6.1.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
||||||
|
@ -4938,11 +4926,6 @@
|
||||||
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;
|
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;
|
||||||
productName = AlamofireImage;
|
productName = AlamofireImage;
|
||||||
};
|
};
|
||||||
DB5086B725CC0D6400C2C187 /* Kingfisher */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = DB5086B625CC0D6400C2C187 /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
|
||||||
productName = Kingfisher;
|
|
||||||
};
|
|
||||||
DB68050F2637D0F800430867 /* KeychainAccess */ = {
|
DB68050F2637D0F800430867 /* KeychainAccess */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */;
|
package = DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>21</integer>
|
<integer>20</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>20</integer>
|
<integer>21</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|
|
@ -114,8 +114,8 @@
|
||||||
"repositoryURL": "https://github.com/TwidereProject/MetaTextView.git",
|
"repositoryURL": "https://github.com/TwidereProject/MetaTextView.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "5b86b386464be8a6da5383aa714c458c07da6c01",
|
"revision": "28e53130d16f12e0eeb479d83b77a0a718ef2088",
|
||||||
"version": "1.2.3"
|
"version": "1.2.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@ import CoreDataStack
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Nuke
|
||||||
|
|
||||||
enum NotificationSection: Equatable, Hashable {
|
enum NotificationSection: Equatable, Hashable {
|
||||||
case main
|
case main
|
||||||
|
@ -72,10 +73,13 @@ extension NotificationSection {
|
||||||
}
|
}
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
if let url = notification.account.avatarImageURL() {
|
if let url = notification.account.avatarImageURL() {
|
||||||
cell.avatarImageView.af.setImage(
|
cell.avatarImageViewTask = Nuke.loadImage(
|
||||||
withURL: url,
|
with: url,
|
||||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
options: ImageLoadingOptions(
|
||||||
imageTransition: .crossDissolve(0.2)
|
placeholder: UIImage.placeholder(color: .systemFill),
|
||||||
|
transition: .fadeIn(duration: 0.2)
|
||||||
|
),
|
||||||
|
into: cell.avatarImageView
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
||||||
|
@ -113,10 +117,13 @@ extension NotificationSection {
|
||||||
cell.actionLabel.text = actionText + " · " + timeText
|
cell.actionLabel.text = actionText + " · " + timeText
|
||||||
cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict)
|
cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict)
|
||||||
if let url = notification.account.avatarImageURL() {
|
if let url = notification.account.avatarImageURL() {
|
||||||
cell.avatarImageView.af.setImage(
|
cell.avatarImageViewTask = Nuke.loadImage(
|
||||||
withURL: url,
|
with: url,
|
||||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
options: ImageLoadingOptions(
|
||||||
imageTransition: .crossDissolve(0.2)
|
placeholder: UIImage.placeholder(color: .systemFill),
|
||||||
|
transition: .fadeIn(duration: 0.2)
|
||||||
|
),
|
||||||
|
into: cell.avatarImageView
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
||||||
|
|
|
@ -547,7 +547,13 @@ extension StatusSection {
|
||||||
// name
|
// name
|
||||||
let author = (status.reblog ?? status).author
|
let author = (status.reblog ?? status).author
|
||||||
let nameContent = author.displayNameWithFallback
|
let nameContent = author.displayNameWithFallback
|
||||||
cell.statusView.nameLabel.configure(content: nameContent, emojiDict: author.emojiDict)
|
MastodonStatusContent.parseResult(content: nameContent, emojiDict: author.emojiDict)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak cell] parseResult in
|
||||||
|
guard let cell = cell else { return }
|
||||||
|
cell.statusView.nameLabel.configure(contentParseResult: parseResult)
|
||||||
|
}
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
// username
|
// username
|
||||||
cell.statusView.usernameLabel.text = "@" + author.acct
|
cell.statusView.usernameLabel.text = "@" + author.acct
|
||||||
// avatar
|
// avatar
|
||||||
|
@ -571,9 +577,10 @@ extension StatusSection {
|
||||||
) {
|
) {
|
||||||
// set content
|
// set content
|
||||||
do {
|
do {
|
||||||
|
let status = status.reblog ?? status
|
||||||
let content = MastodonContent(
|
let content = MastodonContent(
|
||||||
content: (status.reblog ?? status).content,
|
content: status.content,
|
||||||
emojis: (status.reblog ?? status).emojiMeta
|
emojis: status.emojiMeta
|
||||||
)
|
)
|
||||||
let metaContent = try MastodonMetaContent.convert(document: content)
|
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||||
cell.statusView.contentMetaText.configure(content: metaContent)
|
cell.statusView.contentMetaText.configure(content: metaContent)
|
||||||
|
@ -648,46 +655,19 @@ extension StatusSection {
|
||||||
|
|
||||||
let isSingleMosaicLayout = mosaics.count == 1
|
let isSingleMosaicLayout = mosaics.count == 1
|
||||||
|
|
||||||
// set link preview
|
|
||||||
// cell.statusView.linkPreview.isHidden = true
|
|
||||||
//
|
|
||||||
// var _firstURL: URL? = {
|
|
||||||
// for entity in cell.statusView.activeTextLabel.activeEntities {
|
|
||||||
// guard case let .url(_, _, url, _) = entity.type else { continue }
|
|
||||||
// return URL(string: url)
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }()
|
|
||||||
//
|
|
||||||
// if let url = _firstURL {
|
|
||||||
// Future<LPLinkMetadata?, Error> { promise in
|
|
||||||
// LPMetadataProvider().startFetchingMetadata(for: url) { meta, error in
|
|
||||||
// if let error = error {
|
|
||||||
// promise(.failure(error))
|
|
||||||
// } else {
|
|
||||||
// promise(.success(meta))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .receive(on: RunLoop.main)
|
|
||||||
// .sink { _ in
|
|
||||||
// // do nothing
|
|
||||||
// } receiveValue: { [weak cell] meta in
|
|
||||||
// guard let meta = meta else { return }
|
|
||||||
// guard let cell = cell else { return }
|
|
||||||
// cell.statusView.linkPreview.metadata = meta
|
|
||||||
// cell.statusView.linkPreview.isHidden = false
|
|
||||||
// }
|
|
||||||
// .store(in: &cell.disposeBag)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// set image
|
// set image
|
||||||
let imageSize = CGSize(
|
let imageSize = CGSize(
|
||||||
width: mosaic.imageViewSize.width * imageView.traitCollection.displayScale,
|
width: mosaic.imageViewSize.width * imageView.traitCollection.displayScale,
|
||||||
height: mosaic.imageViewSize.height * imageView.traitCollection.displayScale
|
height: mosaic.imageViewSize.height * imageView.traitCollection.displayScale
|
||||||
)
|
)
|
||||||
|
let url: URL? = {
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
|
return meta.previewURL ?? meta.url
|
||||||
|
}
|
||||||
|
return meta.url
|
||||||
|
}()
|
||||||
let request = ImageRequest(
|
let request = ImageRequest(
|
||||||
url: meta.url,
|
url: url,
|
||||||
processors: [
|
processors: [
|
||||||
ImageProcessors.Resize(
|
ImageProcessors.Resize(
|
||||||
size: imageSize,
|
size: imageSize,
|
||||||
|
|
|
@ -57,6 +57,15 @@ extension ActiveLabel {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ActiveLabel {
|
||||||
|
func configure(text: String) {
|
||||||
|
attributedText = nil
|
||||||
|
activeEntities.removeAll()
|
||||||
|
self.text = text
|
||||||
|
accessibilityLabel = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension ActiveLabel {
|
extension ActiveLabel {
|
||||||
|
|
||||||
/// status content
|
/// status content
|
||||||
|
|
|
@ -15,7 +15,7 @@ enum MastodonStatusContent {
|
||||||
typealias EmojiShortcode = String
|
typealias EmojiShortcode = String
|
||||||
typealias EmojiDict = [EmojiShortcode: URL]
|
typealias EmojiDict = [EmojiShortcode: URL]
|
||||||
|
|
||||||
static let workingQueue = DispatchQueue(label: "org.joinmastodon.app.ActiveLabel.working-queue", qos: .userInteractive)
|
static let workingQueue = DispatchQueue(label: "org.joinmastodon.app.ActiveLabel.working-queue", qos: .userInteractive, attributes: .concurrent)
|
||||||
|
|
||||||
static func parseResult(content: String, emojiDict: MastodonStatusContent.EmojiDict) -> AnyPublisher<MastodonStatusContent.ParseResult?, Never> {
|
static func parseResult(content: String, emojiDict: MastodonStatusContent.EmojiDict) -> AnyPublisher<MastodonStatusContent.ParseResult?, Never> {
|
||||||
return Future { promise in
|
return Future { promise in
|
||||||
|
|
|
@ -14,27 +14,27 @@ extension StatusTableViewCellDelegate where Self: StatusProvider {
|
||||||
// prefetch reply status
|
// prefetch reply status
|
||||||
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
let domain = activeMastodonAuthenticationBox.domain
|
let domain = activeMastodonAuthenticationBox.domain
|
||||||
|
let items = self.items(indexPaths: indexPaths)
|
||||||
|
|
||||||
var statusObjectIDs: [NSManagedObjectID] = []
|
let managedObjectContext = context.managedObjectContext
|
||||||
for item in items(indexPaths: indexPaths) {
|
managedObjectContext.perform { [weak self] in
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
statusObjectIDs.append(homeTimelineIndex.status.objectID)
|
|
||||||
case .status(let objectID, _):
|
|
||||||
statusObjectIDs.append(objectID)
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let backgroundManagedObjectContext = context.backgroundManagedObjectContext
|
|
||||||
backgroundManagedObjectContext.perform { [weak self] in
|
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
for objectID in statusObjectIDs {
|
|
||||||
let status = backgroundManagedObjectContext.object(with: objectID) as! Status
|
|
||||||
|
|
||||||
// fetch in-reply info if needs
|
var statuses: [Status] = []
|
||||||
|
for item in items {
|
||||||
|
switch item {
|
||||||
|
case .homeTimelineIndex(let objectID, _):
|
||||||
|
guard let homeTimelineIndex = try? managedObjectContext.existingObject(with: objectID) as? HomeTimelineIndex else { continue }
|
||||||
|
statuses.append(homeTimelineIndex.status)
|
||||||
|
case .status(let objectID, _):
|
||||||
|
guard let status = try? managedObjectContext.existingObject(with: objectID) as? Status else { continue }
|
||||||
|
statuses.append(status)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for status in statuses {
|
||||||
if let replyToID = status.inReplyToID, status.replyTo == nil {
|
if let replyToID = status.inReplyToID, status.replyTo == nil {
|
||||||
self.context.statusPrefetchingService.prefetchReplyTo(
|
self.context.statusPrefetchingService.prefetchReplyTo(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
@ -44,12 +44,7 @@ extension StatusTableViewCellDelegate where Self: StatusProvider {
|
||||||
authorizationBox: activeMastodonAuthenticationBox
|
authorizationBox: activeMastodonAuthenticationBox
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} // end for in
|
||||||
// self.context.statusContentCacheService.prefetch(
|
} // end context.perform
|
||||||
// content: (status.reblog ?? status).content,
|
} // end func
|
||||||
// emojiDict: (status.reblog ?? status).emojiDict
|
|
||||||
// )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,6 @@ import MastodonSDK
|
||||||
import AlamofireImage
|
import AlamofireImage
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
import GDPerformanceView_Swift
|
|
||||||
#endif
|
|
||||||
|
|
||||||
final class AsyncHomeTimelineViewController: ASDKViewController<ASTableNode>, NeedsDependency, MediaPreviewableViewController {
|
final class AsyncHomeTimelineViewController: ASDKViewController<ASTableNode>, NeedsDependency, MediaPreviewableViewController {
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
@ -107,7 +103,6 @@ extension AsyncHomeTimelineViewController {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// long press to trigger debug menu
|
// long press to trigger debug menu
|
||||||
settingBarButtonItem.menu = debugMenu
|
settingBarButtonItem.menu = debugMenu
|
||||||
PerformanceMonitor.shared().delegate = self
|
|
||||||
#else
|
#else
|
||||||
settingBarButtonItem.target = self
|
settingBarButtonItem.target = self
|
||||||
settingBarButtonItem.action = #selector(AsyncHomeTimelineViewController.settingBarButtonItemPressed(_:))
|
settingBarButtonItem.action = #selector(AsyncHomeTimelineViewController.settingBarButtonItemPressed(_:))
|
||||||
|
@ -548,13 +543,6 @@ extension AsyncHomeTimelineViewController: StatusTableViewControllerNavigateable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
extension AsyncHomeTimelineViewController: PerformanceMonitorDelegate {
|
|
||||||
func performanceMonitor(didReport performanceReport: PerformanceReport) {
|
|
||||||
// print(performanceReport)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - ASTableDelegate
|
// MARK: - ASTableDelegate
|
||||||
extension AsyncHomeTimelineViewController: ASTableDelegate {
|
extension AsyncHomeTimelineViewController: ASTableDelegate {
|
||||||
|
|
|
@ -77,7 +77,12 @@ final class HomeTimelineViewModel: NSObject {
|
||||||
let fetchRequest = HomeTimelineIndex.sortedFetchRequest
|
let fetchRequest = HomeTimelineIndex.sortedFetchRequest
|
||||||
fetchRequest.fetchBatchSize = 20
|
fetchRequest.fetchBatchSize = 20
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(HomeTimelineIndex.status)]
|
fetchRequest.relationshipKeyPathsForPrefetching = [
|
||||||
|
#keyPath(HomeTimelineIndex.status),
|
||||||
|
#keyPath(HomeTimelineIndex.status.author),
|
||||||
|
#keyPath(HomeTimelineIndex.status.reblog),
|
||||||
|
#keyPath(HomeTimelineIndex.status.reblog.author),
|
||||||
|
]
|
||||||
let controller = NSFetchedResultsController(
|
let controller = NSFetchedResultsController(
|
||||||
fetchRequest: fetchRequest,
|
fetchRequest: fetchRequest,
|
||||||
managedObjectContext: context.managedObjectContext,
|
managedObjectContext: context.managedObjectContext,
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
import os.log
|
import os.log
|
||||||
import func AVFoundation.AVMakeRect
|
import func AVFoundation.AVMakeRect
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import FLAnimatedImage
|
||||||
|
|
||||||
final class MediaPreviewImageView: UIScrollView {
|
final class MediaPreviewImageView: UIScrollView {
|
||||||
|
|
||||||
let imageView: UIImageView = {
|
let imageView: FLAnimatedImageView = {
|
||||||
let imageView = UIImageView()
|
let imageView = FLAnimatedImageView()
|
||||||
imageView.contentMode = .scaleAspectFit
|
imageView.contentMode = .scaleAspectFit
|
||||||
imageView.clipsToBounds = true
|
imageView.clipsToBounds = true
|
||||||
imageView.isUserInteractionEnabled = true
|
imageView.isUserInteractionEnabled = true
|
||||||
|
@ -120,7 +121,9 @@ extension MediaPreviewImageView {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
imageView.frame = CGRect(origin: .zero, size: imageViewSize)
|
imageView.frame = CGRect(origin: .zero, size: imageViewSize)
|
||||||
imageView.image = image
|
if imageView.image == nil {
|
||||||
|
imageView.image = image
|
||||||
|
}
|
||||||
contentSize = imageViewSize
|
contentSize = imageViewSize
|
||||||
contentInset = imageContentInset
|
contentInset = imageContentInset
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
import Nuke
|
||||||
|
|
||||||
protocol MediaPreviewImageViewControllerDelegate: AnyObject {
|
protocol MediaPreviewImageViewControllerDelegate: AnyObject {
|
||||||
func mediaPreviewImageViewController(_ viewController: MediaPreviewImageViewController, tapGestureRecognizerDidTrigger tapGestureRecognizer: UITapGestureRecognizer)
|
func mediaPreviewImageViewController(_ viewController: MediaPreviewImageViewController, tapGestureRecognizerDidTrigger tapGestureRecognizer: UITapGestureRecognizer)
|
||||||
|
@ -69,16 +70,35 @@ extension MediaPreviewImageViewController {
|
||||||
let previewImageViewContextMenuInteraction = UIContextMenuInteraction(delegate: self)
|
let previewImageViewContextMenuInteraction = UIContextMenuInteraction(delegate: self)
|
||||||
previewImageView.addInteraction(previewImageViewContextMenuInteraction)
|
previewImageView.addInteraction(previewImageViewContextMenuInteraction)
|
||||||
|
|
||||||
viewModel.image
|
switch viewModel.item {
|
||||||
.receive(on: RunLoop.main) // use RunLoop prevent set image during zooming (TODO: handle transitioning state)
|
case .local(let meta):
|
||||||
.sink { [weak self] image in
|
self.previewImageView.imageView.image = meta.image
|
||||||
guard let self = self else { return }
|
self.previewImageView.setup(image: meta.image, container: self.previewImageView, forceUpdate: true)
|
||||||
guard let image = image else { return }
|
self.previewImageView.imageView.accessibilityLabel = self.viewModel.altText
|
||||||
self.previewImageView.imageView.image = image
|
case .status(let meta):
|
||||||
self.previewImageView.setup(image: image, container: self.previewImageView, forceUpdate: true)
|
Nuke.loadImage(
|
||||||
self.previewImageView.imageView.accessibilityLabel = self.viewModel.altText
|
with: meta.url,
|
||||||
|
into: self.previewImageView.imageView
|
||||||
|
) { result in
|
||||||
|
switch result {
|
||||||
|
case .failure(let error):
|
||||||
|
break
|
||||||
|
case .success(let response):
|
||||||
|
self.previewImageView.setup(image: response.image, container: self.previewImageView, forceUpdate: true)
|
||||||
|
self.previewImageView.imageView.accessibilityLabel = self.viewModel.altText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
}
|
||||||
|
// viewModel.image
|
||||||
|
// .receive(on: RunLoop.main) // use RunLoop prevent set image during zooming (TODO: handle transitioning state)
|
||||||
|
// .sink { [weak self] image in
|
||||||
|
// guard let self = self else { return }
|
||||||
|
// guard let image = image else { return }
|
||||||
|
// self.previewImageView.imageView.image = image
|
||||||
|
// self.previewImageView.setup(image: image, container: self.previewImageView, forceUpdate: true)
|
||||||
|
// self.previewImageView.imageView.accessibilityLabel = self.viewModel.altText
|
||||||
|
// }
|
||||||
|
// .store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,12 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import AlamofireImage
|
import Nuke
|
||||||
|
|
||||||
class MediaPreviewImageViewModel {
|
class MediaPreviewImageViewModel {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
// input
|
// input
|
||||||
let item: ImagePreviewItem
|
let item: ImagePreviewItem
|
||||||
|
|
||||||
|
@ -25,16 +27,20 @@ class MediaPreviewImageViewModel {
|
||||||
self.altText = meta.altText
|
self.altText = meta.altText
|
||||||
|
|
||||||
let url = meta.url
|
let url = meta.url
|
||||||
ImageDownloader.default.download(URLRequest(url: url), completion: { [weak self] response in
|
|
||||||
guard let self = self else { return }
|
ImagePipeline.shared.imagePublisher(with: url)
|
||||||
switch response.result {
|
.sink { completion in
|
||||||
case .failure(let error):
|
switch completion {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: download image %s fail: %s", ((#file as NSString).lastPathComponent), #line, #function, url.debugDescription, error.localizedDescription)
|
case .failure(let error):
|
||||||
case .success(let image):
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: download image %s fail: %s", ((#file as NSString).lastPathComponent), #line, #function, url.debugDescription, error.localizedDescription)
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: download image %s success", ((#file as NSString).lastPathComponent), #line, #function, url.debugDescription)
|
case .finished:
|
||||||
self.image.value = image
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: download image %s success", ((#file as NSString).lastPathComponent), #line, #function, url.debugDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.image.value = response.image
|
||||||
}
|
}
|
||||||
})
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(meta: LocalImagePreviewMeta) {
|
init(meta: LocalImagePreviewMeta) {
|
||||||
|
|
|
@ -11,6 +11,8 @@ import UIKit
|
||||||
import ActiveLabel
|
import ActiveLabel
|
||||||
import MetaTextView
|
import MetaTextView
|
||||||
import Meta
|
import Meta
|
||||||
|
import FLAnimatedImage
|
||||||
|
import Nuke
|
||||||
|
|
||||||
final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||||
static let actionImageBorderWidth: CGFloat = 2
|
static let actionImageBorderWidth: CGFloat = 2
|
||||||
|
@ -19,8 +21,9 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||||
var pollCountdownSubscription: AnyCancellable?
|
var pollCountdownSubscription: AnyCancellable?
|
||||||
var delegate: NotificationTableViewCellDelegate?
|
var delegate: NotificationTableViewCellDelegate?
|
||||||
|
|
||||||
|
var avatarImageViewTask: ImageTask?
|
||||||
let avatarImageView: UIImageView = {
|
let avatarImageView: UIImageView = {
|
||||||
let imageView = UIImageView()
|
let imageView = FLAnimatedImageView()
|
||||||
imageView.layer.cornerRadius = 4
|
imageView.layer.cornerRadius = 4
|
||||||
imageView.layer.cornerCurve = .continuous
|
imageView.layer.cornerCurve = .continuous
|
||||||
imageView.clipsToBounds = true
|
imageView.clipsToBounds = true
|
||||||
|
@ -88,12 +91,12 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
avatarImageView.af.cancelImageRequest()
|
avatarImageViewTask?.cancel()
|
||||||
|
avatarImageViewTask = nil
|
||||||
statusView.updateContentWarningDisplay(isHidden: true, animated: false)
|
statusView.updateContentWarningDisplay(isHidden: true, animated: false)
|
||||||
statusView.pollTableView.dataSource = nil
|
statusView.pollTableView.dataSource = nil
|
||||||
statusView.playerContainerView.reset()
|
statusView.playerContainerView.reset()
|
||||||
statusView.playerContainerView.isHidden = true
|
statusView.playerContainerView.isHidden = true
|
||||||
|
|
||||||
disposeBag.removeAll()
|
disposeBag.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ import UIKit
|
||||||
import Meta
|
import Meta
|
||||||
import MetaTextView
|
import MetaTextView
|
||||||
import ActiveLabel
|
import ActiveLabel
|
||||||
|
import FLAnimatedImage
|
||||||
|
import Nuke
|
||||||
|
|
||||||
protocol NotificationTableViewCellDelegate: AnyObject {
|
protocol NotificationTableViewCellDelegate: AnyObject {
|
||||||
var context: AppContext! { get }
|
var context: AppContext! { get }
|
||||||
|
@ -38,8 +40,9 @@ final class NotificationTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
var delegate: NotificationTableViewCellDelegate?
|
var delegate: NotificationTableViewCellDelegate?
|
||||||
|
|
||||||
|
var avatarImageViewTask: ImageTask?
|
||||||
let avatarImageView: UIImageView = {
|
let avatarImageView: UIImageView = {
|
||||||
let imageView = UIImageView()
|
let imageView = FLAnimatedImageView()
|
||||||
imageView.layer.cornerRadius = 4
|
imageView.layer.cornerRadius = 4
|
||||||
imageView.layer.cornerCurve = .continuous
|
imageView.layer.cornerCurve = .continuous
|
||||||
imageView.clipsToBounds = true
|
imageView.clipsToBounds = true
|
||||||
|
@ -112,7 +115,8 @@ final class NotificationTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
avatarImageView.af.cancelImageRequest()
|
avatarImageViewTask?.cancel()
|
||||||
|
avatarImageViewTask = nil
|
||||||
disposeBag.removeAll()
|
disposeBag.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
import func AVFoundation.AVMakeRect
|
import func AVFoundation.AVMakeRect
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
import Nuke
|
||||||
|
import FLAnimatedImage
|
||||||
|
|
||||||
final class ContextMenuImagePreviewViewController: UIViewController {
|
final class ContextMenuImagePreviewViewController: UIViewController {
|
||||||
|
|
||||||
|
@ -15,13 +17,19 @@ final class ContextMenuImagePreviewViewController: UIViewController {
|
||||||
|
|
||||||
var viewModel: ContextMenuImagePreviewViewModel!
|
var viewModel: ContextMenuImagePreviewViewModel!
|
||||||
|
|
||||||
|
var imageTask: ImageTask?
|
||||||
let imageView: UIImageView = {
|
let imageView: UIImageView = {
|
||||||
let imageView = UIImageView()
|
let imageView = FLAnimatedImageView()
|
||||||
imageView.contentMode = .scaleAspectFill
|
imageView.contentMode = .scaleAspectFill
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
imageTask?.cancel()
|
||||||
|
imageTask = nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ContextMenuImagePreviewViewController {
|
extension ContextMenuImagePreviewViewController {
|
||||||
|
@ -47,12 +55,13 @@ extension ContextMenuImagePreviewViewController {
|
||||||
.sink { [weak self] url in
|
.sink { [weak self] url in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard let url = url else { return }
|
guard let url = url else { return }
|
||||||
self.imageView.af.setImage(
|
self.imageTask = Nuke.loadImage(
|
||||||
withURL: url,
|
with: url,
|
||||||
placeholderImage: self.viewModel.thumbnail,
|
options: ImageLoadingOptions(
|
||||||
imageTransition: .crossDissolve(0.2),
|
placeholder: self.viewModel.thumbnail,
|
||||||
runImageTransitionIfCached: true,
|
transition: .fadeIn(duration: 0.2)
|
||||||
completion: nil
|
),
|
||||||
|
into: self.imageView
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
|
@ -95,7 +95,12 @@ final class StatusView: UIView {
|
||||||
view.accessibilityLabel = L10n.Common.Controls.Status.showUserProfile
|
view.accessibilityLabel = L10n.Common.Controls.Status.showUserProfile
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
let avatarImageView: UIImageView = FLAnimatedImageView()
|
let avatarImageView: UIImageView = {
|
||||||
|
let imageView = FLAnimatedImageView()
|
||||||
|
imageView.layer.shouldRasterize = true
|
||||||
|
imageView.layer.rasterizationScale = UIScreen.main.scale
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
let avatarStackedContainerButton: AvatarStackContainerButton = AvatarStackContainerButton()
|
let avatarStackedContainerButton: AvatarStackContainerButton = AvatarStackContainerButton()
|
||||||
|
|
||||||
let nameLabel: ActiveLabel = {
|
let nameLabel: ActiveLabel = {
|
||||||
|
@ -217,6 +222,7 @@ final class StatusView: UIView {
|
||||||
metaText.textView.textContainer.lineFragmentPadding = 0
|
metaText.textView.textContainer.lineFragmentPadding = 0
|
||||||
metaText.textView.textContainerInset = .zero
|
metaText.textView.textContainerInset = .zero
|
||||||
metaText.textView.layer.masksToBounds = false
|
metaText.textView.layer.masksToBounds = false
|
||||||
|
|
||||||
let paragraphStyle: NSMutableParagraphStyle = {
|
let paragraphStyle: NSMutableParagraphStyle = {
|
||||||
let style = NSMutableParagraphStyle()
|
let style = NSMutableParagraphStyle()
|
||||||
style.lineSpacing = 5
|
style.lineSpacing = 5
|
||||||
|
@ -400,11 +406,6 @@ extension StatusView {
|
||||||
statusContainerStackView.addArrangedSubview(contentMetaText.textView)
|
statusContainerStackView.addArrangedSubview(contentMetaText.textView)
|
||||||
contentMetaText.textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
|
contentMetaText.textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// link preview
|
|
||||||
// statusContainerStackView.addArrangedSubview(linkPreview)
|
|
||||||
// linkPreview.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
||||||
|
|
||||||
// image
|
// image
|
||||||
statusContainerStackView.addArrangedSubview(statusMosaicImageViewContainer)
|
statusContainerStackView.addArrangedSubview(statusMosaicImageViewContainer)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ final class AvatarStackContainerButton: UIControl {
|
||||||
static let maskOffset: CGFloat = 2
|
static let maskOffset: CGFloat = 2
|
||||||
|
|
||||||
// UIControl.Event - Application: 0x0F000000
|
// UIControl.Event - Application: 0x0F000000
|
||||||
static let primaryAction = UIControl.Event(rawValue: 1 << 25) // 0x01000000
|
static let primaryAction = UIControl.Event(rawValue: 1 << 25) // 0x01000000
|
||||||
var primaryActionState: UIControl.State = .normal
|
var primaryActionState: UIControl.State = .normal
|
||||||
|
|
||||||
let topLeadingAvatarStackedImageView = AvatarStackedImageView()
|
let topLeadingAvatarStackedImageView = AvatarStackedImageView()
|
||||||
|
@ -46,6 +46,12 @@ final class AvatarStackContainerButton: UIControl {
|
||||||
extension AvatarStackContainerButton {
|
extension AvatarStackContainerButton {
|
||||||
|
|
||||||
private func _init() {
|
private func _init() {
|
||||||
|
topLeadingAvatarStackedImageView.layer.shouldRasterize = true
|
||||||
|
topLeadingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale
|
||||||
|
|
||||||
|
bottomTrailingAvatarStackedImageView.layer.shouldRasterize = true
|
||||||
|
bottomTrailingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale
|
||||||
|
|
||||||
topLeadingAvatarStackedImageView.translatesAutoresizingMaskIntoConstraints = false
|
topLeadingAvatarStackedImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
addSubview(topLeadingAvatarStackedImageView)
|
addSubview(topLeadingAvatarStackedImageView)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
|
|
|
@ -23,7 +23,7 @@ struct MosaicImageViewModel {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let mosaicMeta = MosaicMeta(
|
let mosaicMeta = MosaicMeta(
|
||||||
priviewURL: element.previewURL.flatMap { URL(string: $0) },
|
previewURL: element.previewURL.flatMap { URL(string: $0) },
|
||||||
url: url,
|
url: url,
|
||||||
size: CGSize(width: width, height: height),
|
size: CGSize(width: width, height: height),
|
||||||
blurhash: element.blurhash,
|
blurhash: element.blurhash,
|
||||||
|
@ -39,7 +39,7 @@ struct MosaicImageViewModel {
|
||||||
struct MosaicMeta {
|
struct MosaicMeta {
|
||||||
static let edgeMaxLength: CGFloat = 20
|
static let edgeMaxLength: CGFloat = 20
|
||||||
|
|
||||||
let priviewURL: URL?
|
let previewURL: URL?
|
||||||
let url: URL
|
let url: URL
|
||||||
let size: CGSize
|
let size: CGSize
|
||||||
let blurhash: String?
|
let blurhash: String?
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import os.log
|
import os.log
|
||||||
import Foundation
|
import Foundation
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import Kingfisher
|
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
extension MastodonAttachmentService {
|
extension MastodonAttachmentService {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import PhotosUI
|
import PhotosUI
|
||||||
import Kingfisher
|
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import MobileCoreServices
|
import MobileCoreServices
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Combine
|
||||||
import CoreData
|
import CoreData
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
import AlamofireImage
|
import AlamofireImage
|
||||||
import Kingfisher
|
|
||||||
|
|
||||||
class AppContext: ObservableObject {
|
class AppContext: ObservableObject {
|
||||||
|
|
||||||
|
@ -124,7 +123,6 @@ extension AppContext {
|
||||||
func purgeCache() -> AnyPublisher<ByteCount, Never> {
|
func purgeCache() -> AnyPublisher<ByteCount, Never> {
|
||||||
Publishers.MergeMany([
|
Publishers.MergeMany([
|
||||||
AppContext.purgeAlamofireImageCache(),
|
AppContext.purgeAlamofireImageCache(),
|
||||||
AppContext.purgeKingfisherCache(),
|
|
||||||
AppContext.purgeTemporaryDirectory(),
|
AppContext.purgeTemporaryDirectory(),
|
||||||
])
|
])
|
||||||
.reduce(0, +)
|
.reduce(0, +)
|
||||||
|
@ -146,29 +144,6 @@ extension AppContext {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func purgeKingfisherCache() -> AnyPublisher<ByteCount, Never> {
|
|
||||||
Future<ByteCount, Never> { promise in
|
|
||||||
KingfisherManager.shared.cache.calculateDiskStorageSize { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let diskBytes):
|
|
||||||
KingfisherManager.shared.cache.clearCache()
|
|
||||||
KingfisherManager.shared.cache.calculateDiskStorageSize { currentResult in
|
|
||||||
switch currentResult {
|
|
||||||
case .success(let currentDiskBytes):
|
|
||||||
let purgedDiskBytes = max(0, Int(diskBytes) - Int(currentDiskBytes))
|
|
||||||
promise(.success(purgedDiskBytes))
|
|
||||||
case .failure:
|
|
||||||
promise(.success(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .failure:
|
|
||||||
promise(.success(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func purgeTemporaryDirectory() -> AnyPublisher<ByteCount, Never> {
|
private static func purgeTemporaryDirectory() -> AnyPublisher<ByteCount, Never> {
|
||||||
Future<ByteCount, Never> { promise in
|
Future<ByteCount, Never> { promise in
|
||||||
AppContext.purgeCacheWorkingQueue.async {
|
AppContext.purgeCacheWorkingQueue.async {
|
||||||
|
|
Loading…
Reference in New Issue