Skip to content

Commit

Permalink
Move all UI*FeedbackGenerators to FeedbackGenerator and disable them …
Browse files Browse the repository at this point in the history
…for now (IOS-247) (#1267)

* Move all UI*FeedbackGenerators to FeedbackGenerator and disable them for now (IOS-247)

* Fix copyright header

* Remove empty private constructor
  • Loading branch information
kimar authored Apr 9, 2024
1 parent eace1ea commit 4ea6004
Show file tree
Hide file tree
Showing 16 changed files with 81 additions and 51 deletions.
6 changes: 2 additions & 4 deletions Mastodon/Protocol/Provider/DataSourceFacade+Block.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ extension DataSourceFacade {
dependency: NeedsDependency & AuthContextProvider,
account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Relationship {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

let apiService = dependency.context.apiService
let authBox = dependency.authContext.mastodonAuthenticationBox
Expand All @@ -39,8 +38,7 @@ extension DataSourceFacade {
dependency: NeedsDependency & AuthContextProvider,
account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Empty {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

let apiService = dependency.context.apiService
let authBox = dependency.authContext.mastodonAuthenticationBox
Expand Down
3 changes: 1 addition & 2 deletions Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ extension DataSourceFacade {
provider: NeedsDependency & AuthContextProvider & DataSourceProvider,
status: MastodonStatus
) async throws {
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
selectionFeedbackGenerator.selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

let updatedStatus = try await provider.context.apiService.bookmark(
record: status,
Expand Down
5 changes: 2 additions & 3 deletions Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ extension DataSourceFacade {
provider: DataSourceProvider & AuthContextProvider,
status: MastodonStatus
) async throws {
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
selectionFeedbackGenerator.selectionChanged()

FeedbackGenerator.shared.generate(.selectionChanged)

let updatedStatus = try await provider.context.apiService.favorite(
status: status,
authenticationBox: provider.authContext.mastodonAuthenticationBox
Expand Down
6 changes: 2 additions & 4 deletions Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ extension DataSourceFacade {
return try await withCheckedThrowingContinuation { continuation in
Task { @MainActor in
let performAction = {
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
selectionFeedbackGenerator.selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

let response = try await dependency.context.apiService.toggleFollow(
account: account,
Expand Down Expand Up @@ -84,8 +83,7 @@ extension DataSourceFacade {
notificationView: NotificationView,
query: Mastodon.API.Account.FollowRequestQuery
) async throws {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

let userID = notification.account.id
let state: MastodonFollowRequestState = notification.followRequestState
Expand Down
5 changes: 2 additions & 3 deletions Mastodon/Protocol/Provider/DataSourceFacade+Mute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ extension DataSourceFacade {
dependency: NeedsDependency & AuthContextProvider,
account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Relationship {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()

FeedbackGenerator.shared.generate(.selectionChanged)

let response = try await dependency.context.apiService.toggleMute(
authenticationBox: dependency.authContext.mastodonAuthenticationBox,
account: account
Expand Down
5 changes: 2 additions & 3 deletions Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ private extension DataSourceFacade {
provider: DataSourceProvider & AuthContextProvider,
status: MastodonStatus
) async throws {
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
selectionFeedbackGenerator.selectionChanged()

FeedbackGenerator.shared.generate(.selectionChanged)

let updatedStatus = try await provider.context.apiService.reblog(
status: status,
authenticationBox: provider.authContext.mastodonAuthenticationBox
Expand Down
5 changes: 2 additions & 3 deletions Mastodon/Protocol/Provider/DataSourceFacade+Status.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,8 @@ extension DataSourceFacade {

switch action {
case .reply:
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
selectionFeedbackGenerator.selectionChanged()

FeedbackGenerator.shared.generate(.selectionChanged)

let composeViewModel = ComposeViewModel(
context: provider.context,
authContext: provider.authContext,
Expand Down
3 changes: 1 addition & 2 deletions Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ extension DataSourceFacade {
provider: Provider,
status: MastodonStatus
) async throws -> Mastodon.Entity.Translation? {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

do {
let value = try await provider.context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,8 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid
private func replyStatus() async {
guard let status = await statusRecord() else { return }

let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
selectionFeedbackGenerator.selectionChanged()

FeedbackGenerator.shared.generate(.selectionChanged)

let composeViewModel = ComposeViewModel(
context: self.context,
authContext: authContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,7 @@ extension HomeTimelineViewModel.LoadLatestState {
viewModel.timelineIsEmpty.value = latestStatusIDs.isEmpty && statuses.isEmpty

if !isUserInitiated {
await UIImpactFeedbackGenerator(style: .light)
.impactOccurred()
FeedbackGenerator.shared.generate(.impact(.light))
}

} catch {
Expand Down
11 changes: 6 additions & 5 deletions Mastodon/Scene/Onboarding/PickServer/CategoryPickerSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import UIKit
import MastodonAsset
import MastodonLocalization
import MastodonCore

enum CategoryPickerSection: Equatable, Hashable {
case main
Expand Down Expand Up @@ -36,13 +37,13 @@ extension CategoryPickerSection {

let allLanguagesAction = UIAction(title: L10n.Scene.ServerPicker.Language.all) { _ in
viewModel.selectedLanguage.value = nil
UISelectionFeedbackGenerator().selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)
cell.titleLabel.text = L10n.Scene.ServerPicker.Button.language
}

let languageActions = viewModel.allLanguages.value.compactMap { language in
UIAction(title: language.language ?? language.locale) { action in
UISelectionFeedbackGenerator().selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)
viewModel.selectedLanguage.value = language.locale
cell.titleLabel.text = language.language
}
Expand All @@ -64,19 +65,19 @@ extension CategoryPickerSection {
let doesntMatterAction = UIAction(title: L10n.Scene.ServerPicker.SignupSpeed.all) { _ in
viewModel.manualApprovalRequired.value = nil
cell.titleLabel.text = L10n.Scene.ServerPicker.Button.signupSpeed
UISelectionFeedbackGenerator().selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)
}

let manualApprovalAction = UIAction(title: L10n.Scene.ServerPicker.SignupSpeed.manuallyReviewed) { action in
viewModel.manualApprovalRequired.value = true
cell.titleLabel.text = action.title
UISelectionFeedbackGenerator().selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)
}

let instantSignupAction = UIAction(title: L10n.Scene.ServerPicker.SignupSpeed.instant) { action in
viewModel.manualApprovalRequired.value = false
cell.titleLabel.text = action.title
UISelectionFeedbackGenerator().selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)
}

let signupSpeedMenu = UIMenu(title: L10n.Scene.ServerPicker.Button.signupSpeed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Tabman
import MastodonAsset
import MastodonUI
import MastodonLocalization
import MastodonCore

protocol PickServerServerSectionTableHeaderViewDelegate: AnyObject {
func pickServerServerSectionTableHeaderView(_ headerView: PickServerServerSectionTableHeaderView, collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
Expand Down Expand Up @@ -97,7 +98,7 @@ extension PickServerServerSectionTableHeaderView {
extension PickServerServerSectionTableHeaderView: UICollectionViewDelegate {

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
UISelectionFeedbackGenerator().selectionChanged()
FeedbackGenerator.shared.generate(.selectionChanged)

collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
delegate?.pickServerServerSectionTableHeaderView(self, collectionView: collectionView, didSelectItemAt: indexPath)
Expand Down
6 changes: 3 additions & 3 deletions Mastodon/Scene/Root/MainTab/MainTabBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MainTabBarController: UITabBarController {
@Published var avatarURL: URL?

// haptic feedback
private let selectionFeedbackGenerator = UIImpactFeedbackGenerator(style: .medium)
private let feedbackGenerator = FeedbackGenerator.shared

init(
context: AppContext,
Expand Down Expand Up @@ -249,7 +249,7 @@ extension MainTabBarController {

@objc private func composeButtonDidPressed(_ sender: Any) {

selectionFeedbackGenerator.impactOccurred()
feedbackGenerator.generate(.impact(.medium))
guard let authContext = self.authContext else { return }
let composeViewModel = ComposeViewModel(
context: context,
Expand Down Expand Up @@ -382,7 +382,7 @@ extension MainTabBarController: UITabBarControllerDelegate {

// Different tab has been selected, send haptic feedback
if viewController.tabBarItem.tag != tabBarController.selectedIndex {
selectionFeedbackGenerator.impactOccurred()
feedbackGenerator.generate(.impact(.medium))
}

// Assert index is as same as the tab rawValue. This check needs to be done `shouldSelect`
Expand Down
4 changes: 4 additions & 0 deletions Mastodon/Supporting Files/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var coordinator: SceneCoordinator?

var savedShortCutItem: UIApplicationShortcutItem?

let feedbackGenerator = FeedbackGenerator.shared

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }

feedbackGenerator.isEnabled = false // Disable Haptic Feedback for now

#if DEBUG
let window = TouchesVisibleWindow(windowScene: windowScene)
self.window = window
Expand Down
40 changes: 40 additions & 0 deletions MastodonSDK/Sources/MastodonCore/FeedbackGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright © 2024 Mastodon gGmbH. All rights reserved.

import UIKit

public class FeedbackGenerator {

private let lightImpactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
private let mediumImpactFeedbackGenerator = UIImpactFeedbackGenerator(style: .medium)
private let notificationFeedbackGenerator = UINotificationFeedbackGenerator()
private let selectionFeedbackGenerator = UISelectionFeedbackGenerator()

public enum Impact {
case light, medium
}

public enum Feedback {
case impact(Impact)
case notification(UINotificationFeedbackGenerator.FeedbackType)
case selectionChanged
}

public static let shared = FeedbackGenerator()
public var isEnabled = true

public func generate(_ feedback: Feedback) {
guard isEnabled else { return }
DispatchQueue.main.async { [self] in
switch feedback {
case .impact(.light):
lightImpactFeedbackGenerator.impactOccurred()
case .impact(.medium):
mediumImpactFeedbackGenerator.impactOccurred()
case let .notification(type):
notificationFeedbackGenerator.notificationOccurred(type)
case .selectionChanged:
selectionFeedbackGenerator.selectionChanged()
}
}
}
}
22 changes: 9 additions & 13 deletions MastodonSDK/Sources/MastodonCore/Service/PhotoLibraryService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ extension PhotoLibraryService {
extension PhotoLibraryService {

public func save(imageSource source: ImageSource) -> AnyPublisher<Void, Error> {
let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
let notificationFeedbackGenerator = UINotificationFeedbackGenerator()

let feedbackGenerator = FeedbackGenerator.shared

let imageDataPublisher: AnyPublisher<Data, Error> = {
switch source {
Expand All @@ -50,13 +48,13 @@ extension PhotoLibraryService {
PhotoLibraryService.save(imageData: data)
}
.handleEvents(receiveSubscription: { _ in
impactFeedbackGenerator.impactOccurred()
feedbackGenerator.generate(.impact(.light))
}, receiveCompletion: { completion in
switch completion {
case .failure:
notificationFeedbackGenerator.notificationOccurred(.error)
feedbackGenerator.generate(.notification(.error))
case .finished:
notificationFeedbackGenerator.notificationOccurred(.success)
feedbackGenerator.generate(.notification(.success))
}
})
.eraseToAnyPublisher()
Expand All @@ -67,10 +65,8 @@ extension PhotoLibraryService {
extension PhotoLibraryService {

public func copy(imageSource source: ImageSource) -> AnyPublisher<Void, Error> {

let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
let notificationFeedbackGenerator = UINotificationFeedbackGenerator()

let feedbackGenerator = FeedbackGenerator.shared

let imageDataPublisher: AnyPublisher<Data, Error> = {
switch source {
case .url(let url):
Expand All @@ -85,13 +81,13 @@ extension PhotoLibraryService {
PhotoLibraryService.copy(imageData: data)
}
.handleEvents(receiveSubscription: { _ in
impactFeedbackGenerator.impactOccurred()
feedbackGenerator.generate(.impact(.light))
}, receiveCompletion: { completion in
switch completion {
case .failure:
notificationFeedbackGenerator.notificationOccurred(.error)
feedbackGenerator.generate(.notification(.error))
case .finished:
notificationFeedbackGenerator.notificationOccurred(.success)
feedbackGenerator.generate(.notification(.success))
}
})
.eraseToAnyPublisher()
Expand Down

0 comments on commit 4ea6004

Please sign in to comment.