IceCubesApp/Packages/Status/Sources/Status/Row/Subviews/StatusRowActionsView.swift

174 lines
5.3 KiB
Swift
Raw Normal View History

2023-01-17 10:36:01 +00:00
import DesignSystem
2022-12-22 09:53:36 +00:00
import Env
2023-01-17 10:36:01 +00:00
import Models
2022-12-20 19:33:45 +00:00
import Network
2023-01-17 10:36:01 +00:00
import SwiftUI
2022-12-16 12:16:48 +00:00
struct StatusRowActionsView: View {
2022-12-29 09:39:34 +00:00
@EnvironmentObject private var theme: Theme
@EnvironmentObject private var currentAccount: CurrentAccount
2022-12-20 19:33:45 +00:00
@ObservedObject var viewModel: StatusRowViewModel
2023-02-21 06:23:42 +00:00
func privateBoost() -> Bool {
2023-02-21 06:23:42 +00:00
return viewModel.status.visibility == .priv && viewModel.status.account.id == currentAccount.account?.id
}
2023-02-21 06:23:42 +00:00
2022-12-20 19:33:45 +00:00
@MainActor
2023-02-21 17:52:30 +00:00
enum Action: CaseIterable {
case respond, boost, favorite, bookmark, share
2023-01-17 10:36:01 +00:00
// Have to implement this manually here due to compiler not implicitly
// inserting `nonisolated`, which leads to a warning:
//
// Main actor-isolated static property 'allCases' cannot be used to
// satisfy nonisolated protocol requirement
//
nonisolated public static var allCases: [StatusRowActionsView.Action] {
[.respond, .boost, .favorite, .bookmark, .share]
}
func iconName(viewModel: StatusRowViewModel, privateBoost: Bool = false) -> String {
2022-12-19 15:17:25 +00:00
switch self {
case .respond:
2022-12-24 06:32:20 +00:00
return "arrowshape.turn.up.left"
2022-12-19 15:17:25 +00:00
case .boost:
2023-02-21 06:23:42 +00:00
if privateBoost {
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "lock.rotation"
}
2023-02-21 06:23:42 +00:00
2022-12-21 11:39:29 +00:00
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "arrow.left.arrow.right.circle"
case .favorite:
return viewModel.isFavorited ? "star.fill" : "star"
2023-01-09 18:26:56 +00:00
case .bookmark:
return viewModel.isBookmarked ? "bookmark.fill" : "bookmark"
2022-12-19 15:17:25 +00:00
case .share:
return "square.and.arrow.up"
2022-12-16 12:16:48 +00:00
}
2022-12-19 15:17:25 +00:00
}
2023-01-17 10:36:01 +00:00
2023-01-06 16:14:34 +00:00
func count(viewModel: StatusRowViewModel, theme: Theme) -> Int? {
2023-02-22 18:09:39 +00:00
if theme.statusActionsDisplay == .discret && !viewModel.isFocused {
2023-01-06 16:14:34 +00:00
return nil
}
2022-12-19 15:17:25 +00:00
switch self {
case .respond:
2022-12-21 11:39:29 +00:00
return viewModel.repliesCount
case .favorite:
return viewModel.favoritesCount
2022-12-19 15:17:25 +00:00
case .boost:
2022-12-21 11:39:29 +00:00
return viewModel.reblogsCount
2023-01-09 18:26:56 +00:00
case .share, .bookmark:
2022-12-19 15:17:25 +00:00
return nil
2022-12-16 12:16:48 +00:00
}
2022-12-19 15:17:25 +00:00
}
2023-01-17 10:36:01 +00:00
func tintColor(theme: Theme) -> Color? {
2022-12-24 14:09:17 +00:00
switch self {
case .respond, .share:
return nil
case .favorite:
return .yellow
2023-01-09 18:26:56 +00:00
case .bookmark:
return .pink
2022-12-24 14:09:17 +00:00
case .boost:
return theme.tintColor
}
}
func isOn(viewModel: StatusRowViewModel) -> Bool {
switch self {
case .respond, .share: return false
case .favorite: return viewModel.isFavorited
case .bookmark: return viewModel.isBookmarked
case .boost: return viewModel.isReblogged
2022-12-24 14:09:17 +00:00
}
}
2022-12-19 15:17:25 +00:00
}
2023-01-17 10:36:01 +00:00
2022-12-19 15:17:25 +00:00
var body: some View {
2022-12-24 12:41:25 +00:00
VStack(spacing: 12) {
HStack {
2023-02-21 17:52:30 +00:00
ForEach(Action.allCases, id: \.self) { action in
2022-12-24 12:41:25 +00:00
if action == .share {
if let urlString = viewModel.status.reblog?.url ?? viewModel.status.url,
2023-02-18 06:26:48 +00:00
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)) {
2022-12-24 12:41:25 +00:00
Image(systemName: action.iconName(viewModel: viewModel))
}
.buttonStyle(.statusAction())
2022-12-23 14:53:02 +00:00
}
2022-12-24 12:41:25 +00:00
} else {
actionButton(action: action)
2022-12-24 12:41:25 +00:00
Spacer()
2022-12-19 15:17:25 +00:00
}
2022-12-24 12:41:25 +00:00
}
}
if viewModel.isFocused {
StatusRowDetailView(viewModel: viewModel)
}
}
}
2023-02-12 15:29:41 +00:00
private func actionButton(action: Action) -> some View {
HStack(spacing: 2) {
Button {
handleAction(action: action)
} label: {
Image(systemName: action.iconName(viewModel: viewModel, privateBoost: privateBoost()))
}
.buttonStyle(
.statusAction(
isOn: action.isOn(viewModel: viewModel),
tintColor: action.tintColor(theme: theme)
)
)
.disabled(action == .boost &&
2023-02-22 18:09:39 +00:00
(viewModel.status.visibility == .direct || viewModel.status.visibility == .priv && viewModel.status.account.id != currentAccount.account?.id))
if let count = action.count(viewModel: viewModel, theme: theme), !viewModel.isRemote {
Text("\(count)")
.foregroundColor(Color(UIColor.secondaryLabel))
.font(.scaledFootnote)
.monospacedDigit()
}
}
}
2023-02-21 17:52:30 +00:00
private func handleAction(action: Action) {
2022-12-20 19:33:45 +00:00
Task {
if viewModel.isRemote, viewModel.localStatusId == nil || viewModel.localStatus == nil {
guard await viewModel.fetchRemoteStatus() else {
return
}
}
HapticManager.shared.fireHaptic(of: .notification(.success))
2022-12-20 19:33:45 +00:00
switch action {
case .respond:
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status)
case .favorite:
if viewModel.isFavorited {
await viewModel.unFavorite()
2022-12-20 19:33:45 +00:00
} else {
await viewModel.favorite()
2022-12-20 19:33:45 +00:00
}
2023-01-09 18:26:56 +00:00
case .bookmark:
if viewModel.isBookmarked {
await viewModel.unbookmark()
} else {
await viewModel.bookmark()
}
2022-12-21 11:39:29 +00:00
case .boost:
if viewModel.isReblogged {
await viewModel.unReblog()
} else {
await viewModel.reblog()
}
2022-12-20 19:33:45 +00:00
default:
break
}
}
2022-12-16 12:16:48 +00:00
}
}