Move all UI*FeedbackGenerators to FeedbackGenerator and disable them 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
This commit is contained in:
parent
eace1ea815
commit
4ea600403b
@ -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
|
||||
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -8,6 +8,7 @@
|
||||
import UIKit
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import MastodonCore
|
||||
|
||||
enum CategoryPickerSection: Equatable, Hashable {
|
||||
case main
|
||||
@ -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
|
||||
}
|
||||
@ -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,
|
||||
|
@ -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)
|
||||
@ -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)
|
||||
|
@ -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,
|
||||
@ -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,
|
||||
@ -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`
|
||||
|
@ -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
|
||||
|
40
MastodonSDK/Sources/MastodonCore/FeedbackGenerator.swift
Normal file
40
MastodonSDK/Sources/MastodonCore/FeedbackGenerator.swift
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
@ -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()
|
||||
@ -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):
|
||||
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user