StatusRowViewModel: Cleanup

This commit is contained in:
Thomas Ricouard 2023-03-02 06:56:25 +01:00
parent d2d297f019
commit 15b7954705
8 changed files with 51 additions and 49 deletions

View file

@ -128,11 +128,13 @@ public final class StatusDataController: StatusDataControlling {
isBookmarked.toggle()
let id = remoteStatus ?? status.id
let endpoint = isBookmarked ? Statuses.bookmark(id: id) : Statuses.unbookmark(id: id)
objectWillChange.send()
do {
let status: Status = try await client.post(endpoint: endpoint)
updateFrom(status: status, publishUpdate: true)
} catch {
isBookmarked.toggle()
objectWillChange.send()
}
}
}

View file

@ -27,7 +27,6 @@ public struct StatusRowView: View {
public var body: some View {
VStack(alignment: .leading) {
let status: AnyStatus = viewModel.status.reblog ?? viewModel.status
if viewModel.isFiltered, let filter = viewModel.filter {
switch filter.filter.filterAction {
case .warn:
@ -45,9 +44,9 @@ public struct StatusRowView: View {
theme.avatarPosition == .leading
{
Button {
viewModel.routerPath.navigate(to: .accountDetailWithAccount(account: status.account))
viewModel.routerPath.navigate(to: .accountDetailWithAccount(account: viewModel.finalStatus.account))
} label: {
AvatarView(url: status.account.avatar, size: .status)
AvatarView(url: viewModel.finalStatus.account.avatar, size: .status)
}
}
VStack(alignment: .leading) {
@ -56,11 +55,10 @@ public struct StatusRowView: View {
StatusRowReplyView(viewModel: viewModel)
}
VStack(alignment: .leading, spacing: 8) {
let status: AnyStatus = viewModel.status.reblog ?? viewModel.status
if !isCompact {
StatusRowHeaderView(status: status, viewModel: viewModel)
StatusRowHeaderView(viewModel: viewModel)
}
StatusRowContentView(status: status, viewModel: viewModel)
StatusRowContentView(viewModel: viewModel)
.contentShape(Rectangle())
.onTapGesture {
viewModel.navigateToDetail()

View file

@ -12,6 +12,7 @@ public class StatusRowViewModel: ObservableObject {
let isFocused: Bool
let isRemote: Bool
let showActions: Bool
let finalStatus: AnyStatus
@Published var isPinned: Bool
@Published var embeddedStatus: Status?
@ -49,7 +50,7 @@ public class StatusRowViewModel: ObservableObject {
private func recalcCollapse() {
let hasContentWarning = !status.spoilerText.asRawText.isEmpty
let showCollapseButton = collapseLongPosts && isCollapsed && !hasContentWarning
&& (status.reblog?.content ?? status.content).asRawText.unicodeScalars.count > collapseThresholdLength
&& finalStatus.content.asRawText.unicodeScalars.count > collapseThresholdLength
let newlineLimit = showCollapseButton && isCollapsed ? collapsedLines : nil
if newlineLimit != lineLimit {
lineLimit = newlineLimit
@ -62,7 +63,7 @@ public class StatusRowViewModel: ObservableObject {
private var seen = false
var filter: Filtered? {
status.reblog?.filtered?.first ?? status.filtered?.first
finalStatus.filtered?.first
}
var isThread: Bool {
@ -91,6 +92,7 @@ public class StatusRowViewModel: ObservableObject {
showActions: Bool = true)
{
self.status = status
self.finalStatus = status.reblog ?? status
self.client = client
self.routerPath = routerPath
self.isFocused = isFocused
@ -104,7 +106,7 @@ public class StatusRowViewModel: ObservableObject {
if UserPreferences.shared.autoExpandSpoilers {
displaySpoiler = false
} else {
displaySpoiler = !(status.reblog?.spoilerText.asRawText ?? status.spoilerText.asRawText).isEmpty
displaySpoiler = !finalStatus.spoilerText.asRawText.isEmpty
}
@ -142,7 +144,7 @@ public class StatusRowViewModel: ObservableObject {
func navigateToDetail() {
guard !isFocused else { return }
if isRemote, let url = URL(string: status.reblog?.url ?? status.url ?? "") {
if isRemote, let url = URL(string: finalStatus.url ?? "") {
routerPath.navigate(to: .remoteStatusDetail(url: url))
} else {
routerPath.navigate(to: .statusDetailWithStatus(status: status.reblogAsAsStatus ?? status))
@ -178,7 +180,7 @@ public class StatusRowViewModel: ObservableObject {
}
private func embededStatusURL() -> URL? {
let content = status.reblog?.content ?? status.content
let content = finalStatus.content
if !content.statusesURLs.isEmpty,
let url = content.statusesURLs.first,
!StatusEmbedCache.shared.badStatusesURLs.contains(url),
@ -237,7 +239,7 @@ public class StatusRowViewModel: ObservableObject {
guard client.isAuth else { return }
isPinned = true
do {
let status: Status = try await client.post(endpoint: Statuses.pin(id: status.reblog?.id ?? status.id))
let status: Status = try await client.post(endpoint: Statuses.pin(id: finalStatus.id))
updateFromStatus(status: status)
} catch {
isPinned = false
@ -248,7 +250,7 @@ public class StatusRowViewModel: ObservableObject {
guard client.isAuth else { return }
isPinned = false
do {
let status: Status = try await client.post(endpoint: Statuses.unpin(id: status.reblog?.id ?? status.id))
let status: Status = try await client.post(endpoint: Statuses.unpin(id: finalStatus.id))
updateFromStatus(status: status)
} catch {
isPinned = true
@ -279,7 +281,7 @@ public class StatusRowViewModel: ObservableObject {
}
func getStatusLang() -> String? {
status.reblog?.language ?? status.language
finalStatus.language
}
func translate(userLang: String) async {
@ -288,7 +290,7 @@ public class StatusRowViewModel: ObservableObject {
isLoadingTranslation = true
}
// We first use instance translation API if available.
let translation: StatusTranslation = try await client.post(endpoint: Statuses.translate(id: status.reblog?.id ?? status.id,
let translation: StatusTranslation = try await client.post(endpoint: Statuses.translate(id: finalStatus.id,
lang: userLang))
withAnimation {
self.translation = translation
@ -298,8 +300,8 @@ public class StatusRowViewModel: ObservableObject {
// If not or fail we use Ice Cubes own DeepL client.
let deepLClient = DeepLClient()
let translation = try? await deepLClient.request(target: userLang,
source: status.language,
text: status.reblog?.content.asRawText ?? status.content.asRawText)
source: finalStatus.language,
text: finalStatus.content.asRawText)
withAnimation {
self.translation = translation
isLoadingTranslation = false
@ -308,7 +310,7 @@ public class StatusRowViewModel: ObservableObject {
}
func fetchRemoteStatus() async -> Bool {
guard isRemote, let remoteStatusURL = URL(string: status.reblog?.url ?? status.url ?? "") else { return false }
guard isRemote, let remoteStatusURL = URL(string: finalStatus.url ?? "") else { return false }
isLoadingRemoteContent = true
let results: SearchResults? = try? await client.get(endpoint: Search.search(query: remoteStatusURL.absoluteString,
type: "statuses",

View file

@ -94,12 +94,12 @@ struct StatusRowActionsView: View {
HStack {
ForEach(Action.allCases, id: \.self) { action in
if action == .share {
if let urlString = viewModel.status.reblog?.url ?? viewModel.status.url,
if let urlString = viewModel.finalStatus.url,
let url = URL(string: urlString)
{
ShareLink(item: url,
subject: Text(viewModel.status.reblog?.account.safeDisplayName ?? viewModel.status.account.safeDisplayName),
message: Text(viewModel.status.reblog?.content.asRawText ?? viewModel.status.content.asRawText)) {
subject: Text(viewModel.finalStatus.account.safeDisplayName),
message: Text(viewModel.finalStatus.content.asRawText)) {
action.image(dataController: statusDataController)
}
.buttonStyle(.statusAction())

View file

@ -9,19 +9,18 @@ struct StatusRowContentView: View {
@EnvironmentObject private var theme: Theme
let status: AnyStatus
@ObservedObject var viewModel: StatusRowViewModel
var body: some View {
if !status.spoilerText.asRawText.isEmpty {
StatusRowSpoilerView(status: status, displaySpoiler: $viewModel.displaySpoiler)
if !viewModel.finalStatus.spoilerText.asRawText.isEmpty {
StatusRowSpoilerView(status: viewModel.finalStatus, displaySpoiler: $viewModel.displaySpoiler)
}
if !viewModel.displaySpoiler {
StatusRowTextView(status: status, viewModel: viewModel)
StatusRowTranslateView(status: status, viewModel: viewModel)
if let poll = status.poll {
StatusPollView(poll: poll, status: status)
StatusRowTextView(viewModel: viewModel)
StatusRowTranslateView(viewModel: viewModel)
if let poll = viewModel.finalStatus.poll {
StatusPollView(poll: poll, status: viewModel.finalStatus)
}
if !reasons.contains(.placeholder),
@ -37,10 +36,10 @@ struct StatusRowContentView: View {
.transition(.opacity)
}
if !status.mediaAttachments.isEmpty {
if !viewModel.finalStatus.mediaAttachments.isEmpty {
HStack {
StatusRowMediaPreviewView(attachments: status.mediaAttachments,
sensitive: status.sensitive,
StatusRowMediaPreviewView(attachments: viewModel.finalStatus.mediaAttachments,
sensitive: viewModel.finalStatus.sensitive,
isNotifications: isCompact)
if theme.statusDisplayStyle == .compact {
Spacer()
@ -49,12 +48,12 @@ struct StatusRowContentView: View {
.padding(.vertical, 4)
}
if let card = status.card,
if let card = viewModel.finalStatus.card,
!viewModel.isEmbedLoading,
!isCompact,
theme.statusDisplayStyle != .compact,
status.content.statusesURLs.isEmpty,
status.mediaAttachments.isEmpty
viewModel.finalStatus.content.statusesURLs.isEmpty,
viewModel.finalStatus.mediaAttachments.isEmpty
{
StatusRowCardView(card: card)
}

View file

@ -7,15 +7,14 @@ struct StatusRowHeaderView: View {
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
@EnvironmentObject private var theme: Theme
let status: AnyStatus
let viewModel: StatusRowViewModel
var body: some View {
HStack(alignment: .center) {
Button {
viewModel.navigateToAccountDetail(account: status.account)
viewModel.navigateToAccountDetail(account: viewModel.finalStatus.account)
} label: {
accountView(status: status)
accountView
}
.buttonStyle(.plain)
Spacer()
@ -25,19 +24,20 @@ struct StatusRowHeaderView: View {
}
}
.accessibilityElement()
.accessibilityLabel(Text("\(status.account.displayName)"))
.accessibilityLabel(Text("\(viewModel.finalStatus.account.displayName)"))
}
@ViewBuilder
private func accountView(status: AnyStatus) -> some View {
private var accountView: some View {
HStack(alignment: .center) {
if theme.avatarPosition == .top {
AvatarView(url: status.account.avatar, size: .status)
AvatarView(url: viewModel.finalStatus.account.avatar, size: .status)
}
VStack(alignment: .leading, spacing: 2) {
HStack(alignment: .firstTextBaseline, spacing: 2) {
Group {
EmojiTextApp(.init(stringValue: status.account.safeDisplayName), emojis: status.account.emojis)
EmojiTextApp(.init(stringValue: viewModel.finalStatus.account.safeDisplayName),
emojis: viewModel.finalStatus.account.emojis)
.font(.scaledSubheadline)
.emojiSize(Font.scaledSubheadlinePointSize)
.fontWeight(.semibold)
@ -52,7 +52,7 @@ struct StatusRowHeaderView: View {
.foregroundColor(.gray)
.lineLimit(1)
} else {
Text("@\(theme.displayFullUsername ? status.account.acct : status.account.username)")
Text("@\(theme.displayFullUsername ? viewModel.finalStatus.account.acct : viewModel.finalStatus.account.username)")
.font(.scaledFootnote)
.foregroundColor(.gray)
.lineLimit(1)
@ -64,7 +64,7 @@ struct StatusRowHeaderView: View {
.foregroundColor(.gray)
.lineLimit(1)
} else if theme.displayFullUsername, theme.avatarPosition == .leading {
Text("@\(status.account.acct)")
Text("@\(viewModel.finalStatus.account.acct)")
.font(.scaledFootnote)
.foregroundColor(.gray)
.lineLimit(1)
@ -84,7 +84,7 @@ struct StatusRowHeaderView: View {
}
private var dateView: Text {
Text(status.createdAt.relativeFormatted) +
Text(viewModel.finalStatus.createdAt.relativeFormatted) +
Text("") +
Text(Image(systemName: viewModel.status.visibility.iconName))
}

View file

@ -7,17 +7,19 @@ struct StatusRowTextView: View {
@EnvironmentObject private var theme: Theme
@EnvironmentObject private var preferences: UserPreferences
let status: AnyStatus
@ObservedObject var viewModel: StatusRowViewModel
var body: some View {
VStack {
HStack {
EmojiTextApp(status.content, emojis: status.emojis, language: status.language, lineLimit: viewModel.lineLimit)
EmojiTextApp(viewModel.finalStatus.content,
emojis: viewModel.finalStatus.emojis,
language: viewModel.finalStatus.language,
lineLimit: viewModel.lineLimit)
.font(.scaledBody)
.emojiSize(Font.scaledBodyPointSize)
.environment(\.openURL, OpenURLAction { url in
viewModel.routerPath.handleStatus(status: status, url: url)
viewModel.routerPath.handleStatus(status: viewModel.finalStatus, url: url)
})
Spacer()
}

View file

@ -8,7 +8,6 @@ struct StatusRowTranslateView: View {
@EnvironmentObject private var preferences: UserPreferences
let status: AnyStatus
@ObservedObject var viewModel: StatusRowViewModel
private var shouldShowTranslateButton: Bool {
@ -16,7 +15,7 @@ struct StatusRowTranslateView: View {
if let userLang = preferences.serverPreferences?.postLanguage,
preferences.showTranslateButton,
!status.content.asRawText.isEmpty,
!viewModel.finalStatus.content.asRawText.isEmpty,
viewModel.translation == nil
{
return userLang != statusLang