Improve SoundEffectManager & HapticManager (#1662)

* Remove unnecessary vars and switches

* Improve SoundEffectManager call-site API

* Improve HapticManager call-site API
This commit is contained in:
Théo Arrouye 2023-11-07 02:22:36 -08:00 committed by GitHub
parent 6e1e83cace
commit 4266ac4b42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 75 additions and 105 deletions

View file

@ -14,8 +14,8 @@ extension IceCubesApp {
}
}
HapticManager.shared.fireHaptic(of: .tabSelection)
SoundEffectManager.shared.playSound(of: .tabSelection)
HapticManager.shared.fireHaptic(.tabSelection)
SoundEffectManager.shared.playSound(.tabSelection)
selectedTab = newTab

View file

@ -75,7 +75,7 @@ struct SideBarView<Content: View>: View {
Button {
if account.id == appAccounts.currentAccount.id {
selectedTab = .profile
SoundEffectManager.shared.playSound(of: .tabSelection)
SoundEffectManager.shared.playSound(.tabSelection)
} else {
var transation = Transaction()
transation.disablesAnimations = true
@ -114,7 +114,7 @@ struct SideBarView<Content: View>: View {
}
}
selectedTab = tab
SoundEffectManager.shared.playSound(of: .tabSelection)
SoundEffectManager.shared.playSound(.tabSelection)
if tab == .notifications {
if let token = appAccounts.currentAccount.oauthToken {
userPreferences.notificationsCount[token] = 0

View file

@ -118,12 +118,12 @@ public struct AccountDetailView: View {
}
.refreshable {
Task {
SoundEffectManager.shared.playSound(of: .pull)
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.3))
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.fetchAccount()
await viewModel.fetchNewestStatuses()
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(of: .refresh)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
}
.onChange(of: watcher.latestEvent?.id) {

View file

@ -48,13 +48,13 @@ public struct AppAccountView: View {
let account = viewModel.account
{
routerPath.navigate(to: .accountSettingsWithAccount(account: account, appAccount: viewModel.appAccount))
HapticManager.shared.fireHaptic(of: .buttonPress)
HapticManager.shared.fireHaptic(.buttonPress)
} else {
var transation = Transaction()
transation.disablesAnimations = true
withTransaction(transation) {
appAccounts.currentAccount = viewModel.appAccount
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
}
}
} label: {

View file

@ -43,7 +43,7 @@ public struct AppAccountsSelectorView: View {
public var body: some View {
Button {
isPresented.toggle()
HapticManager.shared.fireHaptic(of: .buttonPress)
HapticManager.shared.fireHaptic(.buttonPress)
} label: {
labelView
}
@ -109,7 +109,7 @@ public struct AppAccountsSelectorView: View {
Section {
Button {
isPresented = false
HapticManager.shared.fireHaptic(of: .buttonPress)
HapticManager.shared.fireHaptic(.buttonPress)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
routerPath.presentedSheet = .addAccount
}
@ -142,7 +142,7 @@ public struct AppAccountsSelectorView: View {
private var settingsButton: some View {
Button {
isPresented = false
HapticManager.shared.fireHaptic(of: .buttonPress)
HapticManager.shared.fireHaptic(.buttonPress)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
routerPath.presentedSheet = .settings
}

View file

@ -81,7 +81,7 @@ struct ConversationsListRow: View {
}
.accessibilityAction(.magicTap) {
if let lastStatus = conversation.lastStatus {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
routerPath.presentedSheet = .replyToStatusEditor(status: lastStatus)
}
}
@ -182,7 +182,7 @@ struct ConversationsListRow: View {
var replyAction: some View {
if let lastStatus = conversation.lastStatus {
Button("status.action.reply") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
routerPath.presentedSheet = .replyToStatusEditor(status: lastStatus)
}
} else {
@ -195,7 +195,7 @@ struct ConversationsListRow: View {
if let lastStatus = conversation.lastStatus {
if lastStatus.account.id != currentAccount.account?.id {
Button("@\(lastStatus.account.username)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
routerPath.navigate(to: .accountDetail(id: lastStatus.account.id))
}
}
@ -205,18 +205,18 @@ struct ConversationsListRow: View {
case .url:
if UIApplication.shared.canOpenURL(link.url) {
Button("accessibility.tabs.timeline.content-link-\(link.title)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
_ = routerPath.handle(url: link.url)
}
}
case .hashtag:
Button("accessibility.tabs.timeline.content-hashtag-\(link.title)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
_ = routerPath.handle(url: link.url)
}
case .mention:
Button("\(link.title)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
_ = routerPath.handle(url: link.url)
}
}

View file

@ -105,11 +105,11 @@ public struct ConversationsListView: View {
// note: this Task wrapper should not be necessary, but it reportedly crashes without it
// when refreshing on an empty list
Task {
SoundEffectManager.shared.playSound(of: .pull)
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.3))
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.fetchConversations()
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(of: .refresh)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
}
.onAppear {

View file

@ -30,7 +30,7 @@ public struct StatusEditorToolbarItem: ToolbarContent {
openWindow(value: WindowDestination.newStatusEditor(visibility: visibility))
} else {
routerPath.presentedSheet = .newStatusEditor(visibility: visibility)
HapticManager.shared.fireHaptic(of: .buttonPress)
HapticManager.shared.fireHaptic(.buttonPress)
}
}
} label: {

View file

@ -25,7 +25,7 @@ public class HapticManager {
}
@MainActor
public func fireHaptic(of type: HapticType) {
public func fireHaptic(_ type: HapticType) {
guard supportsHaptics else { return }
switch type {

View file

@ -11,14 +11,7 @@ public class SoundEffectManager {
case pull, refresh, tootSent, tabSelection, bookmark, boost, favorite, share
}
var pullId: SystemSoundID = 0
var refreshId: SystemSoundID = 1
var tootSentId: SystemSoundID = 2
var tabSelectionId: SystemSoundID = 3
var bookmarkId: SystemSoundID = 4
var boostId: SystemSoundID = 5
var favoriteId: SystemSoundID = 6
var shareId: SystemSoundID = 7
private var systemSoundIDs: [SoundEffect: SystemSoundID] = [:]
private let userPreferences = UserPreferences.shared
@ -27,49 +20,26 @@ public class SoundEffectManager {
}
private func registerSounds() {
for effect in SoundEffect.allCases {
if let url = Bundle.main.url(forResource: effect.rawValue, withExtension: "wav") {
switch effect {
case .pull:
AudioServicesCreateSystemSoundID(url as CFURL, &pullId)
case .refresh:
AudioServicesCreateSystemSoundID(url as CFURL, &refreshId)
case .tootSent:
AudioServicesCreateSystemSoundID(url as CFURL, &tootSentId)
case .tabSelection:
AudioServicesCreateSystemSoundID(url as CFURL, &tabSelectionId)
case .bookmark:
AudioServicesCreateSystemSoundID(url as CFURL, &bookmarkId)
case .boost:
AudioServicesCreateSystemSoundID(url as CFURL, &boostId)
case .favorite:
AudioServicesCreateSystemSoundID(url as CFURL, &favoriteId)
case .share:
AudioServicesCreateSystemSoundID(url as CFURL, &shareId)
}
}
SoundEffect.allCases.forEach { effect in
guard let url = Bundle.main.url(forResource: effect.rawValue, withExtension: "wav") else { return }
register(url: url, for: effect)
}
}
public func playSound(of type: SoundEffect) {
guard userPreferences.soundEffectEnabled else { return }
switch type {
case .pull:
AudioServicesPlaySystemSound(pullId)
case .refresh:
AudioServicesPlaySystemSound(refreshId)
case .tootSent:
AudioServicesPlaySystemSound(tootSentId)
case .tabSelection:
AudioServicesPlaySystemSound(tabSelectionId)
case .bookmark:
AudioServicesPlaySystemSound(bookmarkId)
case .boost:
AudioServicesPlaySystemSound(boostId)
case .favorite:
AudioServicesPlaySystemSound(favoriteId)
case .share:
AudioServicesPlaySystemSound(shareId)
private func register(url: URL, for effect: SoundEffect) {
var soundId: SystemSoundID = .init()
AudioServicesCreateSystemSoundID(url as CFURL, &soundId)
systemSoundIDs[effect] = soundId
}
public func playSound(_ effect: SoundEffect) {
guard
userPreferences.soundEffectEnabled,
let soundId = systemSoundIDs[effect]
else {
return
}
AudioServicesPlaySystemSound(soundId)
}
}

View file

@ -82,11 +82,11 @@ public struct ExploreView: View {
}
.refreshable {
Task {
SoundEffectManager.shared.playSound(of: .pull)
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.3))
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.fetchTrending()
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(of: .refresh)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
}
.listStyle(.grouped)

View file

@ -203,7 +203,7 @@ struct NotificationRowView: View {
private var accessibilityUserActions: some View {
ForEach(notification.accounts) { account in
Button("@\(account.username)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
routerPath.navigate(to: .accountDetail(id: account.id))
}
}

View file

@ -93,11 +93,11 @@ public struct NotificationsListView: View {
await viewModel.fetchNotifications()
}
.refreshable {
SoundEffectManager.shared.playSound(of: .pull)
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.3))
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.fetchNotifications()
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(of: .refresh)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
.onChange(of: watcher.latestEvent?.id) {
if let latestEvent = watcher.latestEvent {

View file

@ -213,7 +213,7 @@ public struct StatusEditorView: View {
let status = await viewModel.postStatus()
if status != nil {
close()
SoundEffectManager.shared.playSound(of: .tootSent)
SoundEffectManager.shared.playSound(.tootSent)
NotificationCenter.default.post(name: .shareSheetClose,
object: nil)
if !viewModel.mode.isInShareExtension, !preferences.requestedReview, !ProcessInfo.processInfo.isMacCatalystApp {

View file

@ -192,7 +192,7 @@ import SwiftUI
case let .edit(status):
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id, json: data))
}
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
if hasExplicitlySelectedLanguage, let selectedLanguage {
preferences?.markLanguageAsSelected(isoCode: selectedLanguage)
}
@ -204,7 +204,7 @@ import SwiftUI
showPostingErrorAlert = true
}
isPosting = false
HapticManager.shared.fireHaptic(of: .notification(.error))
HapticManager.shared.fireHaptic(.notification(.error))
return nil
}
}

View file

@ -195,19 +195,19 @@ public struct StatusRowView: View {
private var accessibilityActions: some View {
// Add reply and quote, which are lost when the swipe actions are removed
Button("status.action.reply") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.status)
}
Button("settings.swipeactions.status.action.quote") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
viewModel.routerPath.presentedSheet = .quoteStatusEditor(status: viewModel.status)
}
.disabled(viewModel.status.visibility == .direct || viewModel.status.visibility == .priv)
if viewModel.finalStatus.mediaAttachments.isEmpty == false {
Button("accessibility.status.media-viewer-action.label") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
let attachments = viewModel.finalStatus.mediaAttachments
if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.mediaViewer(attachments: attachments,
@ -225,14 +225,14 @@ public struct StatusRowView: View {
}
Button("@\(viewModel.status.account.username)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
viewModel.routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id))
}
// Add a reference to the post creator
if viewModel.status.account != viewModel.finalStatus.account {
Button("@\(viewModel.finalStatus.account.username)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
viewModel.routerPath.navigate(to: .accountDetail(id: viewModel.finalStatus.account.id))
}
}
@ -243,18 +243,18 @@ public struct StatusRowView: View {
case .url:
if UIApplication.shared.canOpenURL(link.url) {
Button("accessibility.tabs.timeline.content-link-\(link.title)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
_ = viewModel.routerPath.handle(url: link.url)
}
}
case .hashtag:
Button("accessibility.tabs.timeline.content-hashtag-\(link.title)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
_ = viewModel.routerPath.handle(url: link.url)
}
case .mention:
Button("\(link.title)") {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
_ = viewModel.routerPath.handle(url: link.url)
}
}

View file

@ -201,23 +201,23 @@ struct StatusRowActionsView: View {
return
}
}
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
switch action {
case .respond:
SoundEffectManager.shared.playSound(of: .share)
SoundEffectManager.shared.playSound(.share)
if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status))
} else {
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status)
}
case .favorite:
SoundEffectManager.shared.playSound(of: .favorite)
SoundEffectManager.shared.playSound(.favorite)
await statusDataController.toggleFavorite(remoteStatus: viewModel.localStatusId)
case .bookmark:
SoundEffectManager.shared.playSound(of: .bookmark)
SoundEffectManager.shared.playSound(.bookmark)
await statusDataController.toggleBookmark(remoteStatus: viewModel.localStatusId)
case .boost:
SoundEffectManager.shared.playSound(of: .boost)
SoundEffectManager.shared.playSound(.boost)
await statusDataController.toggleReblog(remoteStatus: viewModel.localStatusId)
default:
break

View file

@ -83,7 +83,7 @@ struct StatusRowSwipeView: View {
@ViewBuilder
private func makeSwipeButtonForRouterPath(action: StatusAction, destination: SheetDestination) -> some View {
Button {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
viewModel.routerPath.presentedSheet = destination
} label: {
makeSwipeLabel(action: action, style: preferences.swipeActionsIconStyle)
@ -94,7 +94,7 @@ struct StatusRowSwipeView: View {
private func makeSwipeButtonForTask(action: StatusAction, privateBoost: Bool = false, task: @escaping () async -> Void) -> some View {
Button {
Task {
HapticManager.shared.fireHaptic(of: .notification(.success))
HapticManager.shared.fireHaptic(.notification(.success))
await task()
}
} label: {

View file

@ -20,7 +20,7 @@ import SwiftUI
func removeStatus(status: Status) {
if !disableUpdate, let index = pendingStatuses.firstIndex(of: status.id) {
pendingStatuses.removeSubrange(index ... (pendingStatuses.count - 1))
HapticManager.shared.fireHaptic(of: .timeline)
HapticManager.shared.fireHaptic(.timeline)
}
}

View file

@ -109,11 +109,11 @@ public struct TimelineView: View {
viewModel.isTimelineVisible = false
}
.refreshable {
SoundEffectManager.shared.playSound(of: .pull)
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.3))
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.pullToRefresh()
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(of: .refresh)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
.onChange(of: watcher.latestEvent?.id) {
if let latestEvent = watcher.latestEvent {