From 1806f9a350b5d8b7d462a0c510a02cd988a74f9f Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Thu, 9 Nov 2023 13:01:02 +0100 Subject: [PATCH] Replace CoreData.StatusEdit by Mastodon.Entity.StatusEdit --- Mastodon/Coordinator/SceneCoordinator.swift | 1 + ...Provider+StatusTableViewCellDelegate.swift | 21 +++-- .../PollOptionView+Configuration.swift | 3 +- .../StatusEditHistoryTableViewCell.swift | 2 +- .../StatusEditHistoryViewController.swift | 6 +- .../StatusEditHistoryViewModel.swift | 3 +- .../Mastodon+Entity+StatusEdit.swift | 15 ++++ .../MastodonCore/Model/Poll/PollItem.swift | 3 +- .../Entity/Mastodon+Entity+StatusEdit.swift | 15 +++- .../Content/MediaView+Configuration.swift | 80 +++++++++++++++++++ .../Content/StatusView+Configuration.swift | 15 +++- 11 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 MastodonSDK/Sources/MastodonCore/Extension/MastodonSDK/Mastodon+Entity+StatusEdit.swift diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index a12d3509f..1ef1dcb65 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -571,6 +571,7 @@ private extension SceneCoordinator { //MARK: - Loading public extension SceneCoordinator { + @MainActor func showLoading() { guard let rootViewController else { return } diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift index dafd65fdf..68be145e2 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift @@ -670,19 +670,30 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, showEditHistory button: UIButton) { Task { - + + await coordinator.showLoading() + let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil) guard let item = await self.item(from: source), case let .status(status) = item else { assertionFailure("only works for status data provider") return } + + guard let status = status.object(in: context.managedObjectContext) else { + return await coordinator.hideLoading() + } + + do { + let edits = try await context.apiService.getHistory(forStatusID: status.id, authenticationBox: authContext.mastodonAuthenticationBox).value - guard let status = status.object(in: context.managedObjectContext), - let edits = status.editHistory?.sorted(by: { $0.createdAt > $1.createdAt }) else { return } + await coordinator.hideLoading() - let viewModel = StatusEditHistoryViewModel(status: status, edits: edits, appContext: context, authContext: authContext) - _ = await coordinator.present(scene: .editHistory(viewModel: viewModel), from: self, transition: .show) + let viewModel = StatusEditHistoryViewModel(status: status, edits: edits, appContext: context, authContext: authContext) + _ = await coordinator.present(scene: .editHistory(viewModel: viewModel), from: self, transition: .show) + } catch { + await coordinator.hideLoading() + } } } } diff --git a/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift b/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift index 0e3f07f66..631e5b337 100644 --- a/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift +++ b/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift @@ -11,6 +11,7 @@ import CoreDataStack import MetaTextKit import MastodonCore import MastodonUI +import MastodonSDK extension PollOptionView { public func configure(pollOption option: PollOption) { @@ -103,7 +104,7 @@ extension PollOptionView { } extension PollOptionView { - public func configure(historyPollOption option: StatusEdit.Poll.Option) { + public func configure(historyPollOption option: Mastodon.Entity.StatusEdit.Poll.Option) { // background viewModel.roundedBackgroundViewColor = SystemTheme.systemElevatedBackgroundColor // metaContent diff --git a/Mastodon/Scene/Thread/Edit History/StatusEditHistoryTableViewCell.swift b/Mastodon/Scene/Thread/Edit History/StatusEditHistoryTableViewCell.swift index 06520462d..3485cfeab 100644 --- a/Mastodon/Scene/Thread/Edit History/StatusEditHistoryTableViewCell.swift +++ b/Mastodon/Scene/Thread/Edit History/StatusEditHistoryTableViewCell.swift @@ -72,7 +72,7 @@ class StatusEditHistoryTableViewCell: UITableViewCell { NSLayoutConstraint.activate(constraints) } - func configure(status: Status, statusEdit: StatusEdit, dateText: String) { + func configure(status: Status, statusEdit: Mastodon.Entity.StatusEdit, dateText: String) { dateLabel.text = dateText statusHistoryView.statusView.configure(status: status, statusEdit: statusEdit) } diff --git a/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewController.swift b/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewController.swift index 52dfd3cc2..a09242b4a 100644 --- a/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewController.swift +++ b/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewController.swift @@ -10,7 +10,7 @@ class StatusEditHistoryViewController: UIViewController { private let tableView: UITableView - var tableViewDataSource: UITableViewDiffableDataSource? + var tableViewDataSource: UITableViewDiffableDataSource? var viewModel: StatusEditHistoryViewModel private let dateFormatter: DateFormatter @@ -28,7 +28,7 @@ class StatusEditHistoryViewController: UIViewController { super.init(nibName: nil, bundle: nil) - let tableViewDataSource = UITableViewDiffableDataSource(tableView: tableView) {tableView, indexPath, itemIdentifier in + let tableViewDataSource = UITableViewDiffableDataSource(tableView: tableView) {tableView, indexPath, itemIdentifier in guard let cell = tableView.dequeueReusableCell(withIdentifier: StatusEditHistoryTableViewCell.identifier, for: indexPath) as? StatusEditHistoryTableViewCell else { fatalError("Wrong cell") } @@ -71,7 +71,7 @@ class StatusEditHistoryViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - var snapshot = NSDiffableDataSourceSnapshot() + var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([0]) snapshot.appendItems(viewModel.edits) diff --git a/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewModel.swift b/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewModel.swift index a5d778bec..525f7e72a 100644 --- a/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewModel.swift +++ b/Mastodon/Scene/Thread/Edit History/StatusEditHistoryViewModel.swift @@ -5,10 +5,11 @@ import CoreDataStack import MastodonCore import MastodonUI import UIKit +import MastodonSDK struct StatusEditHistoryViewModel { let status: Status - let edits: [StatusEdit] + let edits: [Mastodon.Entity.StatusEdit] let appContext: AppContext let authContext: AuthContext diff --git a/MastodonSDK/Sources/MastodonCore/Extension/MastodonSDK/Mastodon+Entity+StatusEdit.swift b/MastodonSDK/Sources/MastodonCore/Extension/MastodonSDK/Mastodon+Entity+StatusEdit.swift new file mode 100644 index 000000000..ba4392485 --- /dev/null +++ b/MastodonSDK/Sources/MastodonCore/Extension/MastodonSDK/Mastodon+Entity+StatusEdit.swift @@ -0,0 +1,15 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation +import MastodonSDK + +extension Mastodon.Entity.StatusEdit: Hashable, Equatable { + public func hash(into hasher: inout Hasher) { + hasher.combine(createdAt) + hasher.combine(content) + } + + public static func == (lhs: Mastodon.Entity.StatusEdit, rhs: Mastodon.Entity.StatusEdit) -> Bool { + lhs.createdAt == rhs.createdAt && lhs.content == rhs.content + } +} diff --git a/MastodonSDK/Sources/MastodonCore/Model/Poll/PollItem.swift b/MastodonSDK/Sources/MastodonCore/Model/Poll/PollItem.swift index 367422752..c0283cbd7 100644 --- a/MastodonSDK/Sources/MastodonCore/Model/Poll/PollItem.swift +++ b/MastodonSDK/Sources/MastodonCore/Model/Poll/PollItem.swift @@ -8,8 +8,9 @@ import Foundation import CoreData import CoreDataStack +import MastodonSDK public enum PollItem: Hashable { case option(record: ManagedObjectRecord) - case history(option: StatusEdit.Poll.Option) + case history(option: Mastodon.Entity.StatusEdit.Poll.Option) } diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+StatusEdit.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+StatusEdit.swift index eb092a4c6..b1697b00c 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+StatusEdit.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+StatusEdit.swift @@ -14,11 +14,23 @@ extension Mastodon.Entity { /// [Document](https://docs.joinmastodon.org/entities/statusedit/) public class StatusEdit: Codable { public class Poll: Codable { - public class Option: Codable { + public class Option: Codable, Hashable { public let title: String + + public func hash(into hasher: inout Hasher) { + hasher.combine(title) + } + + public static func == (lhs: Mastodon.Entity.StatusEdit.Poll.Option, rhs: Mastodon.Entity.StatusEdit.Poll.Option) -> Bool { + lhs.title == rhs.title + } } public let options: [Option] public let title: String? + +// public static func == (lhs: Mastodon.Entity.StatusEdit.Poll, rhs: Mastodon.Entity.StatusEdit.Poll) -> Bool { +// lhs.title == rhs.title && lhs.options == rhs.options +// } } public let content: String @@ -40,5 +52,6 @@ extension Mastodon.Entity { case mediaAttachments = "media_attachments" case emojis } + } } diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/MediaView+Configuration.swift b/MastodonSDK/Sources/MastodonUI/View/Content/MediaView+Configuration.swift index 173f043f6..4ce5da911 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/MediaView+Configuration.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/MediaView+Configuration.swift @@ -13,6 +13,7 @@ import CoreDataStack import Photos import AlamofireImage import MastodonCore +import MastodonSDK extension MediaView { public class Configuration: Hashable { @@ -243,3 +244,82 @@ extension MediaView { return configurations } } + +extension MediaView { + public static func configuration(status: Mastodon.Entity.StatusEdit) -> [MediaView.Configuration] { + func aspectRatio(from attachment: Mastodon.Entity.Attachment) -> CGSize { + guard let width = attachment.meta?.width, let height = attachment.meta?.height else { + return .zero + } + return CGSize(width: width, height: height) + } + + func videoInfo(from attachment: Mastodon.Entity.Attachment) -> MediaView.Configuration.VideoInfo { + MediaView.Configuration.VideoInfo( + aspectRadio: aspectRatio(from: attachment), + assetURL: attachment.remoteURL, + previewURL: attachment.previewURL, + altDescription: attachment.description, + durationMS: { + guard let duration = attachment.meta?.duration else { + return 0 + } + return Int(duration) + }() + ) + } + + let attachments = status.mediaAttachments ?? [] + let configurations = attachments.enumerated().compactMap { (idx, attachment) -> MediaView.Configuration? in + let configuration: MediaView.Configuration? = { + switch attachment.attachmentKind { + case .image: + let info = MediaView.Configuration.ImageInfo( + aspectRadio: aspectRatio(from: attachment), + assetURL: attachment.remoteURL, + altDescription: attachment.description + ) + return .init( + info: .image(info: info), + blurhash: attachment.blurhash, + index: idx, + total: attachments.count + ) + case .video: + let info = videoInfo(from: attachment) + return .init( + info: .video(info: info), + blurhash: attachment.blurhash, + index: idx, + total: attachments.count + ) + case .gifv: + let info = videoInfo(from: attachment) + return .init( + info: .gif(info: info), + blurhash: attachment.blurhash, + index: idx, + total: attachments.count + ) + case .audio: + let info = videoInfo(from: attachment) + return .init( + info: .video(info: info), + blurhash: attachment.blurhash, + index: idx, + total: attachments.count + ) + case .none: + return nil + } // end switch + }() + + configuration?.load() + configuration?.isReveal = true + + return configuration + } + + return configurations + } +} diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift index fcb468d0d..4ed4c603e 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift @@ -40,7 +40,7 @@ extension StatusView { extension StatusView { - public func configure(status: Status, statusEdit: StatusEdit) { + public func configure(status: Status, statusEdit: Mastodon.Entity.StatusEdit) { viewModel.objects.insert(status) if let reblog = status.reblog { viewModel.objects.insert(reblog) @@ -313,7 +313,7 @@ extension StatusView { } } - private func configureContent(statusEdit: StatusEdit, status: Status) { + private func configureContent(statusEdit: Mastodon.Entity.StatusEdit, status: Status) { statusEdit.spoilerText.map { viewModel.spoilerContent = PlaintextMetaContent(string: $0) } @@ -322,7 +322,7 @@ extension StatusView { viewModel.language = (status.reblog ?? status).language // content do { - let content = MastodonContent(content: statusEdit.content, emojis: statusEdit.emojis.asDictionary) + let content = MastodonContent(content: statusEdit.content, emojis: statusEdit.emojis?.asDictionary ?? [:]) let metaContent = try MastodonMetaContent.convert(document: content) viewModel.content = metaContent viewModel.isCurrentlyTranslating = false @@ -385,7 +385,14 @@ extension StatusView { viewModel.mediaViewConfigurations = configurations } - private func configurePollHistory(statusEdit: StatusEdit) { + private func configureMedia(status: Mastodon.Entity.StatusEdit) { + viewModel.isMediaSensitive = status.sensitive + + let configurations = MediaView.configuration(status: status) + viewModel.mediaViewConfigurations = configurations + } + + private func configurePollHistory(statusEdit: Mastodon.Entity.StatusEdit) { guard let poll = statusEdit.poll else { return } let pollItems = poll.options.map { PollItem.history(option: $0) }