mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 09:41:00 +00:00
Mute UI
This commit is contained in:
parent
1580fb0032
commit
5de072aa8f
13 changed files with 232 additions and 14 deletions
|
@ -24,6 +24,12 @@
|
||||||
"account.hide-reblogs" = "Hide boosts";
|
"account.hide-reblogs" = "Hide boosts";
|
||||||
"account.locked.accessibility-label" = "Locked account";
|
"account.locked.accessibility-label" = "Locked account";
|
||||||
"account.mute" = "Mute";
|
"account.mute" = "Mute";
|
||||||
|
"account.mute.indefinite" = "Indefnite";
|
||||||
|
"account.mute.confirm-%@" = "Are you sure you want to mute %@?";
|
||||||
|
"account.mute.confirm.explanation" = "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.";
|
||||||
|
"account.mute.confirm.hide-notifications" = "Hide notifications from this user?";
|
||||||
|
"account.mute.confirm.duration" = "Duration";
|
||||||
|
"account.mute.target-%@" = "Muting %@";
|
||||||
"account.reject-follow-request-button.accessibility-label" = "Reject follow request";
|
"account.reject-follow-request-button.accessibility-label" = "Reject follow request";
|
||||||
"account.request" = "Request";
|
"account.request" = "Request";
|
||||||
"account.request.cancel" = "Cancel follow request";
|
"account.request.cancel" = "Cancel follow request";
|
||||||
|
@ -38,6 +44,7 @@
|
||||||
"account.unblock.confirm-%@" = "Unblock %@?";
|
"account.unblock.confirm-%@" = "Unblock %@?";
|
||||||
"account.unfollow" = "Unfollow";
|
"account.unfollow" = "Unfollow";
|
||||||
"account.unmute" = "Unmute";
|
"account.unmute" = "Unmute";
|
||||||
|
"account.unmute.confirm-%@" = "Unmute %@?";
|
||||||
"activity.open-in-default-browser" = "Open in default browser";
|
"activity.open-in-default-browser" = "Open in default browser";
|
||||||
"add" = "Add";
|
"add" = "Add";
|
||||||
"apns-default-message" = "New notification";
|
"apns-default-message" = "New notification";
|
||||||
|
|
|
@ -9,7 +9,7 @@ public enum RelationshipEndpoint {
|
||||||
case accountsUnfollow(id: Account.Id)
|
case accountsUnfollow(id: Account.Id)
|
||||||
case accountsBlock(id: Account.Id)
|
case accountsBlock(id: Account.Id)
|
||||||
case accountsUnblock(id: Account.Id)
|
case accountsUnblock(id: Account.Id)
|
||||||
case accountsMute(id: Account.Id)
|
case accountsMute(id: Account.Id, notifications: Bool = true, duration: Int = 0)
|
||||||
case accountsUnmute(id: Account.Id)
|
case accountsUnmute(id: Account.Id)
|
||||||
case accountsPin(id: Account.Id)
|
case accountsPin(id: Account.Id)
|
||||||
case accountsUnpin(id: Account.Id)
|
case accountsUnpin(id: Account.Id)
|
||||||
|
@ -40,7 +40,7 @@ extension RelationshipEndpoint: Endpoint {
|
||||||
return [id, "block"]
|
return [id, "block"]
|
||||||
case let .accountsUnblock(id):
|
case let .accountsUnblock(id):
|
||||||
return [id, "unblock"]
|
return [id, "unblock"]
|
||||||
case let .accountsMute(id):
|
case let .accountsMute(id, _, _):
|
||||||
return [id, "mute"]
|
return [id, "mute"]
|
||||||
case let .accountsUnmute(id):
|
case let .accountsUnmute(id):
|
||||||
return [id, "unmute"]
|
return [id, "unmute"]
|
||||||
|
@ -72,6 +72,8 @@ extension RelationshipEndpoint: Endpoint {
|
||||||
|
|
||||||
public var jsonBody: [String: Any]? {
|
public var jsonBody: [String: Any]? {
|
||||||
switch self {
|
switch self {
|
||||||
|
case let .accountsMute(_, notifications, duration):
|
||||||
|
return ["notifications": notifications, "duration": duration]
|
||||||
case let .note(note, _):
|
case let .note(note, _):
|
||||||
return ["comment": note]
|
return ["comment": note]
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -92,6 +92,7 @@
|
||||||
D07EC7FE25B16994006DF726 /* EmojiCategoryHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07EC7FC25B16994006DF726 /* EmojiCategoryHeaderView.swift */; };
|
D07EC7FE25B16994006DF726 /* EmojiCategoryHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07EC7FC25B16994006DF726 /* EmojiCategoryHeaderView.swift */; };
|
||||||
D07EC81125B232C2006DF726 /* SystemEmoji+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07EC81025B232C2006DF726 /* SystemEmoji+Extensions.swift */; };
|
D07EC81125B232C2006DF726 /* SystemEmoji+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07EC81025B232C2006DF726 /* SystemEmoji+Extensions.swift */; };
|
||||||
D07EC81225B232C2006DF726 /* SystemEmoji+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07EC81025B232C2006DF726 /* SystemEmoji+Extensions.swift */; };
|
D07EC81225B232C2006DF726 /* SystemEmoji+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07EC81025B232C2006DF726 /* SystemEmoji+Extensions.swift */; };
|
||||||
|
D07F4D9825D493E300F61133 /* MuteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07F4D9725D493E300F61133 /* MuteView.swift */; };
|
||||||
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0849C7E25903C4900A5EBCC /* Status+Extensions.swift */; };
|
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0849C7E25903C4900A5EBCC /* Status+Extensions.swift */; };
|
||||||
D087671625BAA8C0001FDD43 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D087671525BAA8C0001FDD43 /* ExploreViewController.swift */; };
|
D087671625BAA8C0001FDD43 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D087671525BAA8C0001FDD43 /* ExploreViewController.swift */; };
|
||||||
D088406D25AFBBE200BB749B /* EmojiPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D088406C25AFBBE200BB749B /* EmojiPickerViewController.swift */; };
|
D088406D25AFBBE200BB749B /* EmojiPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D088406C25AFBBE200BB749B /* EmojiPickerViewController.swift */; };
|
||||||
|
@ -292,6 +293,7 @@
|
||||||
D07EC7F125B13E57006DF726 /* EmojiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiView.swift; sourceTree = "<group>"; };
|
D07EC7F125B13E57006DF726 /* EmojiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiView.swift; sourceTree = "<group>"; };
|
||||||
D07EC7FC25B16994006DF726 /* EmojiCategoryHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategoryHeaderView.swift; sourceTree = "<group>"; };
|
D07EC7FC25B16994006DF726 /* EmojiCategoryHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategoryHeaderView.swift; sourceTree = "<group>"; };
|
||||||
D07EC81025B232C2006DF726 /* SystemEmoji+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SystemEmoji+Extensions.swift"; sourceTree = "<group>"; };
|
D07EC81025B232C2006DF726 /* SystemEmoji+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SystemEmoji+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
D07F4D9725D493E300F61133 /* MuteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteView.swift; sourceTree = "<group>"; };
|
||||||
D0849C7E25903C4900A5EBCC /* Status+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+Extensions.swift"; sourceTree = "<group>"; };
|
D0849C7E25903C4900A5EBCC /* Status+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
||||||
D087671525BAA8C0001FDD43 /* ExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreViewController.swift; sourceTree = "<group>"; };
|
D087671525BAA8C0001FDD43 /* ExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -487,6 +489,7 @@
|
||||||
D0BEB21024FA2A90001B0F04 /* EditFilterView.swift */,
|
D0BEB21024FA2A90001B0F04 /* EditFilterView.swift */,
|
||||||
D0BEB20424FA1107001B0F04 /* FiltersView.swift */,
|
D0BEB20424FA1107001B0F04 /* FiltersView.swift */,
|
||||||
D0BEB1FE24F9E5BB001B0F04 /* ListsView.swift */,
|
D0BEB1FE24F9E5BB001B0F04 /* ListsView.swift */,
|
||||||
|
D07F4D9725D493E300F61133 /* MuteView.swift */,
|
||||||
D08B9F0F25CB8E060062D040 /* NotificationPreferencesView.swift */,
|
D08B9F0F25CB8E060062D040 /* NotificationPreferencesView.swift */,
|
||||||
D0C7D42D24F76169001EBDBB /* NotificationTypesPreferencesView.swift */,
|
D0C7D42D24F76169001EBDBB /* NotificationTypesPreferencesView.swift */,
|
||||||
D0C7D42624F76169001EBDBB /* PreferencesView.swift */,
|
D0C7D42624F76169001EBDBB /* PreferencesView.swift */,
|
||||||
|
@ -1015,6 +1018,7 @@
|
||||||
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */,
|
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */,
|
||||||
D0477F2C25C6EBAD005C5368 /* OpenInDefaultBrowserActivity.swift in Sources */,
|
D0477F2C25C6EBAD005C5368 /* OpenInDefaultBrowserActivity.swift in Sources */,
|
||||||
D0DD50CB256B1F24004A04F7 /* ReportView.swift in Sources */,
|
D0DD50CB256B1F24004A04F7 /* ReportView.swift in Sources */,
|
||||||
|
D07F4D9825D493E300F61133 /* MuteView.swift in Sources */,
|
||||||
D0477F1525C68BAC005C5368 /* PrefetchRequestModifier.swift in Sources */,
|
D0477F1525C68BAC005C5368 /* PrefetchRequestModifier.swift in Sources */,
|
||||||
D097F41B25BE3E1A00859F2C /* SearchScope+Extensions.swift in Sources */,
|
D097F41B25BE3E1A00859F2C /* SearchScope+Extensions.swift in Sources */,
|
||||||
D035F8B325B9616000DC75ED /* Timeline+Extensions.swift in Sources */,
|
D035F8B325B9616000DC75ED /* Timeline+Extensions.swift in Sources */,
|
||||||
|
|
|
@ -62,8 +62,8 @@ public extension AccountService {
|
||||||
relationshipAction(.accountsUnblock(id: account.id))
|
relationshipAction(.accountsUnblock(id: account.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func mute() -> AnyPublisher<Never, Error> {
|
func mute(notifications: Bool, duration: Int) -> AnyPublisher<Never, Error> {
|
||||||
relationshipAction(.accountsMute(id: account.id))
|
relationshipAction(.accountsMute(id: account.id, notifications: notifications, duration: duration))
|
||||||
.collect()
|
.collect()
|
||||||
.flatMap { _ in contentDatabase.mute(id: account.id) }
|
.flatMap { _ in contentDatabase.mute(id: account.id) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
|
@ -95,13 +95,13 @@ private extension ProfileViewController {
|
||||||
actions.append(UIAction(
|
actions.append(UIAction(
|
||||||
title: NSLocalizedString("account.unmute", comment: ""),
|
title: NSLocalizedString("account.unmute", comment: ""),
|
||||||
image: UIImage(systemName: "speaker")) { _ in
|
image: UIImage(systemName: "speaker")) { _ in
|
||||||
accountViewModel.unmute()
|
accountViewModel.confirmUnmute()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
actions.append(UIAction(
|
actions.append(UIAction(
|
||||||
title: NSLocalizedString("account.mute", comment: ""),
|
title: NSLocalizedString("account.mute", comment: ""),
|
||||||
image: UIImage(systemName: "speaker.slash")) { _ in
|
image: UIImage(systemName: "speaker.slash")) { _ in
|
||||||
accountViewModel.mute()
|
accountViewModel.confirmMute()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -443,6 +443,10 @@ private extension TableViewController {
|
||||||
compose(inReplyToViewModel: inReplyToViewModel, redraft: redraft)
|
compose(inReplyToViewModel: inReplyToViewModel, redraft: redraft)
|
||||||
case let .confirmDelete(statusViewModel, redraft):
|
case let .confirmDelete(statusViewModel, redraft):
|
||||||
confirmDelete(statusViewModel: statusViewModel, redraft: redraft)
|
confirmDelete(statusViewModel: statusViewModel, redraft: redraft)
|
||||||
|
case let .confirmMute(accountViewModel):
|
||||||
|
confirmMute(muteViewModel: accountViewModel.muteViewModel())
|
||||||
|
case let .confirmUnmute(accountViewModel):
|
||||||
|
confirmUnmute(accountViewModel: accountViewModel)
|
||||||
case let .confirmBlock(accountViewModel):
|
case let .confirmBlock(accountViewModel):
|
||||||
confirmBlock(accountViewModel: accountViewModel)
|
confirmBlock(accountViewModel: accountViewModel)
|
||||||
case let .confirmUnblock(accountViewModel):
|
case let .confirmUnblock(accountViewModel):
|
||||||
|
@ -571,6 +575,21 @@ private extension TableViewController {
|
||||||
present(alertController, animated: true)
|
present(alertController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func confirmMute(muteViewModel: MuteViewModel) {
|
||||||
|
let muteViewController = MuteViewController(viewModel: muteViewModel)
|
||||||
|
let navigationController = UINavigationController(rootViewController: muteViewController)
|
||||||
|
|
||||||
|
present(navigationController, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirmUnmute(accountViewModel: AccountViewModel) {
|
||||||
|
confirm(message: String.localizedStringWithFormat(
|
||||||
|
NSLocalizedString("account.unmute.confirm-%@", comment: ""),
|
||||||
|
accountViewModel.accountName)) {
|
||||||
|
accountViewModel.unmute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func confirmBlock(accountViewModel: AccountViewModel) {
|
func confirmBlock(accountViewModel: AccountViewModel) {
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
title: nil,
|
title: nil,
|
||||||
|
|
|
@ -85,6 +85,15 @@ public extension ReportViewModel {
|
||||||
identityContext: .preview)
|
identityContext: .preview)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension MuteViewModel {
|
||||||
|
static let preview = MuteViewModel(
|
||||||
|
accountService: AccountService(
|
||||||
|
account: .preview,
|
||||||
|
mastodonAPIClient: .preview,
|
||||||
|
contentDatabase: .preview),
|
||||||
|
identityContext: .preview)
|
||||||
|
}
|
||||||
|
|
||||||
public extension DomainBlocksViewModel {
|
public extension DomainBlocksViewModel {
|
||||||
static let preview = DomainBlocksViewModel(service: .init(mastodonAPIClient: .preview))
|
static let preview = DomainBlocksViewModel(service: .init(mastodonAPIClient: .preview))
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ public enum CollectionItemEvent {
|
||||||
case attachment(AttachmentViewModel, StatusViewModel)
|
case attachment(AttachmentViewModel, StatusViewModel)
|
||||||
case compose(inReplyTo: StatusViewModel?, redraft: Status?)
|
case compose(inReplyTo: StatusViewModel?, redraft: Status?)
|
||||||
case confirmDelete(StatusViewModel, redraft: Bool)
|
case confirmDelete(StatusViewModel, redraft: Bool)
|
||||||
|
case confirmMute(AccountViewModel)
|
||||||
|
case confirmUnmute(AccountViewModel)
|
||||||
case confirmBlock(AccountViewModel)
|
case confirmBlock(AccountViewModel)
|
||||||
case confirmUnblock(AccountViewModel)
|
case confirmUnblock(AccountViewModel)
|
||||||
case confirmDomainBlock(AccountViewModel)
|
case confirmDomainBlock(AccountViewModel)
|
||||||
|
|
|
@ -95,6 +95,10 @@ public extension AccountViewModel {
|
||||||
ReportViewModel(accountService: accountService, identityContext: identityContext)
|
ReportViewModel(accountService: accountService, identityContext: identityContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func muteViewModel() -> MuteViewModel {
|
||||||
|
MuteViewModel(accountService: accountService, identityContext: identityContext)
|
||||||
|
}
|
||||||
|
|
||||||
func follow() {
|
func follow() {
|
||||||
ignorableOutputEvent(accountService.follow())
|
ignorableOutputEvent(accountService.follow())
|
||||||
}
|
}
|
||||||
|
@ -127,8 +131,12 @@ public extension AccountViewModel {
|
||||||
ignorableOutputEvent(accountService.unblock())
|
ignorableOutputEvent(accountService.unblock())
|
||||||
}
|
}
|
||||||
|
|
||||||
func mute() {
|
func confirmMute() {
|
||||||
ignorableOutputEvent(accountService.mute())
|
eventsSubject.send(Just(.confirmMute(self)).setFailureType(to: Error.self).eraseToAnyPublisher())
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirmUnmute() {
|
||||||
|
eventsSubject.send(Just(.confirmUnmute(self)).setFailureType(to: Error.self).eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmute() {
|
func unmute() {
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import ServiceLayer
|
||||||
|
|
||||||
|
public final class MuteViewModel: ObservableObject {
|
||||||
|
@Published public var notifications = true
|
||||||
|
@Published public var duration = Duration.indefinite
|
||||||
|
@Published public private(set) var loading = false
|
||||||
|
@Published public var alertItem: AlertItem?
|
||||||
|
public let events: AnyPublisher<Event, Never>
|
||||||
|
public let identityContext: IdentityContext
|
||||||
|
|
||||||
|
private let accountService: AccountService
|
||||||
|
private let eventsSubject = PassthroughSubject<Event, Never>()
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
public init(accountService: AccountService, identityContext: IdentityContext) {
|
||||||
|
self.accountService = accountService
|
||||||
|
self.identityContext = identityContext
|
||||||
|
events = eventsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension MuteViewModel {
|
||||||
|
enum Event {
|
||||||
|
case muted
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Duration: Int, CaseIterable {
|
||||||
|
case indefinite = 0
|
||||||
|
case fiveMinutes = 300
|
||||||
|
case thirtyMinutes = 1800
|
||||||
|
case oneHour = 3600
|
||||||
|
case sixHours = 21600
|
||||||
|
case oneDay = 86400
|
||||||
|
case threeDays = 259200
|
||||||
|
case sevenDays = 604800
|
||||||
|
}
|
||||||
|
|
||||||
|
var accountName: String { "@".appending(accountService.account.acct) }
|
||||||
|
|
||||||
|
func mute() {
|
||||||
|
accountService.mute(notifications: notifications, duration: duration.rawValue)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.handleEvents(receiveSubscription: { [weak self] _ in self?.loading = true })
|
||||||
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
.sink { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
self.loading = false
|
||||||
|
|
||||||
|
if $0 == .finished {
|
||||||
|
self.eventsSubject.send(.muted)
|
||||||
|
}
|
||||||
|
} receiveValue: { _ in }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MuteViewModel.Duration: Identifiable {
|
||||||
|
public var id: Int { rawValue }
|
||||||
|
}
|
105
Views/SwiftUI/MuteView.swift
Normal file
105
Views/SwiftUI/MuteView.swift
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
struct MuteView: View {
|
||||||
|
@StateObject var viewModel: MuteViewModel
|
||||||
|
|
||||||
|
@Environment(\.presentationMode) private var presentationMode
|
||||||
|
fileprivate var dismissHostingController: (() -> Void)?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
VStack(alignment: .leading, spacing: .defaultSpacing) {
|
||||||
|
Text("account.mute.confirm-\(viewModel.accountName)")
|
||||||
|
Text("account.mute.confirm.explanation")
|
||||||
|
}
|
||||||
|
Toggle("account.mute.confirm.hide-notifications", isOn: $viewModel.notifications)
|
||||||
|
Picker("account.mute.confirm.duration", selection: $viewModel.duration) {
|
||||||
|
ForEach(MuteViewModel.Duration.allCases) { duration in
|
||||||
|
Text(verbatim: duration.title).tag(duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Group {
|
||||||
|
if viewModel.loading {
|
||||||
|
ProgressView()
|
||||||
|
} else {
|
||||||
|
Button("account.mute") {
|
||||||
|
viewModel.mute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alertItem($viewModel.alertItem)
|
||||||
|
.onReceive(viewModel.events) {
|
||||||
|
switch $0 {
|
||||||
|
case .muted:
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("account.mute.target-\(viewModel.accountName)")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button("cancel") {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension MuteView {
|
||||||
|
func dismiss() {
|
||||||
|
if let dismissHostingController = dismissHostingController {
|
||||||
|
dismissHostingController()
|
||||||
|
} else {
|
||||||
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class MuteViewController: UIHostingController<MuteView> {
|
||||||
|
init(viewModel: MuteViewModel) {
|
||||||
|
super.init(rootView: MuteView(viewModel: viewModel))
|
||||||
|
|
||||||
|
rootView.dismissHostingController = { [weak self] in self?.dismiss(animated: true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
@objc required dynamic init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension MuteViewModel.Duration {
|
||||||
|
static let dateComponentsFormatter: DateComponentsFormatter = {
|
||||||
|
let formatter = DateComponentsFormatter()
|
||||||
|
|
||||||
|
formatter.unitsStyle = .full
|
||||||
|
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .indefinite:
|
||||||
|
return NSLocalizedString("account.mute.indefinite", comment: "")
|
||||||
|
default:
|
||||||
|
return Self.dateComponentsFormatter.string(from: TimeInterval(rawValue)) ?? String(rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
import PreviewViewModels
|
||||||
|
|
||||||
|
struct MuteView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationView {
|
||||||
|
MuteView(viewModel: .preview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -52,10 +52,8 @@ struct ReportView: View {
|
||||||
.navigationTitle("report.target-\(viewModel.accountName)")
|
.navigationTitle("report.target-\(viewModel.accountName)")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
Button {
|
Button("cancel") {
|
||||||
dismiss()
|
dismiss()
|
||||||
} label: {
|
|
||||||
Image(systemName: "xmark.circle.fill")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +73,7 @@ private extension ReportView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReportViewController: UIHostingController<ReportView> {
|
final class ReportViewController: UIHostingController<ReportView> {
|
||||||
init(viewModel: ReportViewModel) {
|
init(viewModel: ReportViewModel) {
|
||||||
super.init(rootView: ReportView(viewModel: viewModel))
|
super.init(rootView: ReportView(viewModel: viewModel))
|
||||||
|
|
||||||
|
|
|
@ -681,13 +681,13 @@ private extension StatusView {
|
||||||
secondSectionItems.append(UIAction(
|
secondSectionItems.append(UIAction(
|
||||||
title: NSLocalizedString("account.unmute", comment: ""),
|
title: NSLocalizedString("account.unmute", comment: ""),
|
||||||
image: UIImage(systemName: "speaker")) { _ in
|
image: UIImage(systemName: "speaker")) { _ in
|
||||||
viewModel.accountViewModel.unmute()
|
viewModel.accountViewModel.confirmUnmute()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
secondSectionItems.append(UIAction(
|
secondSectionItems.append(UIAction(
|
||||||
title: NSLocalizedString("account.mute", comment: ""),
|
title: NSLocalizedString("account.mute", comment: ""),
|
||||||
image: UIImage(systemName: "speaker.slash")) { _ in
|
image: UIImage(systemName: "speaker.slash")) { _ in
|
||||||
viewModel.accountViewModel.mute()
|
viewModel.accountViewModel.confirmMute()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue