diff --git a/Extensions/CollectionItem+Extensions.swift b/Extensions/CollectionItem+Extensions.swift index ec2d5b0..ddba36c 100644 --- a/Extensions/CollectionItem+Extensions.swift +++ b/Extensions/CollectionItem+Extensions.swift @@ -94,8 +94,7 @@ private extension Account { } .compactMap(URL.init(string:))) - if !identityContext.appPreferences.shouldReduceMotion - && identityContext.appPreferences.animateAvatars == .everywhere { + if identityContext.appPreferences.animateAvatars == .everywhere { urls.insert(avatar) } else { urls.insert(avatarStatic) diff --git a/Extensions/NSMutableAttributedString+Extensions.swift b/Extensions/NSMutableAttributedString+Extensions.swift index 0130bac..8dd661e 100644 --- a/Extensions/NSMutableAttributedString+Extensions.swift +++ b/Extensions/NSMutableAttributedString+Extensions.swift @@ -14,8 +14,7 @@ extension NSMutableAttributedString { let attachment = AnimatedTextAttachment() let imageURL: URL? - if !identityContext.appPreferences.shouldReduceMotion, - identityContext.appPreferences.animateCustomEmojis, + if identityContext.appPreferences.animateCustomEmojis, let urlString = emoji.url { imageURL = URL(stringEscapingPath: urlString) } else if let staticURLString = emoji.staticUrl { diff --git a/Localizations/Localizable.strings b/Localizations/Localizable.strings index 998bb66..055c3b1 100644 --- a/Localizations/Localizable.strings +++ b/Localizations/Localizable.strings @@ -192,7 +192,6 @@ "preferences.blocked-domains" = "Blocked Domains"; "preferences.blocked-users" = "Blocked Users"; "preferences.media" = "Media"; -"preferences.media.use-system-reduce-motion" = "Use system reduce motion setting"; "preferences.media.avatars" = "Avatars"; "preferences.media.avatars.animate" = "Animate avatars"; "preferences.media.avatars.animate.everywhere" = "Everywhere"; diff --git a/Notification Service Extension/NotificationService.swift b/Notification Service Extension/NotificationService.swift index ce6ac40..f7ecc8b 100644 --- a/Notification Service Extension/NotificationService.swift +++ b/Notification Service Extension/NotificationService.swift @@ -75,7 +75,8 @@ final class NotificationService: UNNotificationServiceExtension { private extension NotificationService { private static let environment = AppEnvironment.live( userNotificationCenter: .current(), - reduceMotion: { false }) + reduceMotion: { false }, + autoplayVideos: { true }) enum ImageError: Error { case dataMissing diff --git a/ServiceLayer/Sources/ServiceLayer/Entities/AppEnvironment.swift b/ServiceLayer/Sources/ServiceLayer/Entities/AppEnvironment.swift index c2bf3ad..045997f 100644 --- a/ServiceLayer/Sources/ServiceLayer/Entities/AppEnvironment.swift +++ b/ServiceLayer/Sources/ServiceLayer/Entities/AppEnvironment.swift @@ -14,6 +14,7 @@ public struct AppEnvironment { let userDefaults: UserDefaults let userNotificationClient: UserNotificationClient let reduceMotion: () -> Bool + let autoplayVideos: () -> Bool let uuid: () -> UUID let inMemoryContent: Bool let fixtureDatabase: IdentityDatabase? @@ -24,6 +25,7 @@ public struct AppEnvironment { userDefaults: UserDefaults, userNotificationClient: UserNotificationClient, reduceMotion: @escaping () -> Bool, + autoplayVideos: @escaping () -> Bool, uuid: @escaping () -> UUID, inMemoryContent: Bool, fixtureDatabase: IdentityDatabase?) { @@ -33,6 +35,7 @@ public struct AppEnvironment { self.userDefaults = userDefaults self.userNotificationClient = userNotificationClient self.reduceMotion = reduceMotion + self.autoplayVideos = autoplayVideos self.uuid = uuid self.inMemoryContent = inMemoryContent self.fixtureDatabase = fixtureDatabase @@ -42,7 +45,9 @@ public struct AppEnvironment { public extension AppEnvironment { static let appGroup = "group.metabolist.metatext" - static func live(userNotificationCenter: UNUserNotificationCenter, reduceMotion: @escaping () -> Bool) -> Self { + static func live(userNotificationCenter: UNUserNotificationCenter, + reduceMotion: @escaping () -> Bool, + autoplayVideos: @escaping () -> Bool) -> Self { Self( session: URLSession.shared, webAuthSessionType: LiveWebAuthSession.self, @@ -50,6 +55,7 @@ public extension AppEnvironment { userDefaults: UserDefaults(suiteName: appGroup)!, userNotificationClient: .live(userNotificationCenter), reduceMotion: reduceMotion, + autoplayVideos: autoplayVideos, uuid: UUID.init, inMemoryContent: false, fixtureDatabase: nil) diff --git a/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift b/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift index b387687..50e0d42 100644 --- a/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift +++ b/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift @@ -7,10 +7,12 @@ import Mastodon public struct AppPreferences { private let userDefaults: UserDefaults private let systemReduceMotion: () -> Bool + private let systemAutoplayVideos: () -> Bool public init(environment: AppEnvironment) { self.userDefaults = environment.userDefaults self.systemReduceMotion = environment.reduceMotion + self.systemAutoplayVideos = environment.autoplayVideos } } @@ -45,11 +47,6 @@ public extension AppPreferences { public var id: String { rawValue } } - var useSystemReduceMotionForMedia: Bool { - get { self[.useSystemReduceMotionForMedia] ?? true } - set { self[.useSystemReduceMotionForMedia] = newValue } - } - var statusWord: StatusWord { get { if let rawValue = self[.statusWord] as String?, @@ -69,18 +66,18 @@ public extension AppPreferences { return value } - return .everywhere + return systemReduceMotion() ? .never : .everywhere } set { self[.animateAvatars] = newValue.rawValue } } var animateHeaders: Bool { - get { self[.animateHeaders] ?? true } + get { self[.animateHeaders] ?? !systemReduceMotion() } set { self[.animateHeaders] = newValue } } var animateCustomEmojis: Bool { - get { self[.animateCustomEmojis] ?? true } + get { self[.animateCustomEmojis] ?? !systemReduceMotion() } set { self[.animateCustomEmojis] = newValue } } @@ -91,7 +88,7 @@ public extension AppPreferences { return value } - return .always + return (!systemAutoplayVideos() || systemReduceMotion()) ? .never : .always } set { self[.autoplayGIFs] = newValue.rawValue } } @@ -103,7 +100,7 @@ public extension AppPreferences { return value } - return .wifi + return (!systemAutoplayVideos() || systemReduceMotion()) ? .never : .wifi } set { self[.autoplayVideos] = newValue.rawValue } } @@ -141,10 +138,6 @@ public extension AppPreferences { set { self[.notificationSounds] = newValue.map { $0.rawValue } } } - var shouldReduceMotion: Bool { - systemReduceMotion() && useSystemReduceMotionForMedia - } - func positionBehavior(timeline: Timeline) -> PositionBehavior { switch timeline { case .home: @@ -195,7 +188,6 @@ private extension AppPreferences { case statusWord case requireDoubleTapToReblog case requireDoubleTapToFavorite - case useSystemReduceMotionForMedia case animateAvatars case animateHeaders case animateCustomEmojis diff --git a/ServiceLayer/Sources/ServiceLayerMocks/MockAppEnvironment.swift b/ServiceLayer/Sources/ServiceLayerMocks/MockAppEnvironment.swift index 5b0f746..ebe03b4 100644 --- a/ServiceLayer/Sources/ServiceLayerMocks/MockAppEnvironment.swift +++ b/ServiceLayer/Sources/ServiceLayerMocks/MockAppEnvironment.swift @@ -24,6 +24,7 @@ public extension AppEnvironment { userDefaults: userDefaults, userNotificationClient: userNotificationClient, reduceMotion: { false }, + autoplayVideos: { true }, uuid: uuid, inMemoryContent: inMemoryContent, fixtureDatabase: fixtureDatabase) diff --git a/Share Extension/ShareExtensionNavigationViewController.swift b/Share Extension/ShareExtensionNavigationViewController.swift index 67528a4..71545b7 100644 --- a/Share Extension/ShareExtensionNavigationViewController.swift +++ b/Share Extension/ShareExtensionNavigationViewController.swift @@ -10,7 +10,8 @@ import ViewModels class ShareExtensionNavigationViewController: UINavigationController { private let environment = AppEnvironment.live( userNotificationCenter: .current(), - reduceMotion: { UIAccessibility.isReduceMotionEnabled }) + reduceMotion: { UIAccessibility.isReduceMotionEnabled }, + autoplayVideos: { UIAccessibility.isVideoAutoplayEnabled }) override func viewDidLoad() { super.viewDidLoad() diff --git a/System/MetatextApp.swift b/System/MetatextApp.swift index 28def51..8383a04 100644 --- a/System/MetatextApp.swift +++ b/System/MetatextApp.swift @@ -27,5 +27,6 @@ struct MetatextApp: App { private extension MetatextApp { static let environment = AppEnvironment.live( userNotificationCenter: .current(), - reduceMotion: { UIAccessibility.isReduceMotionEnabled }) + reduceMotion: { UIAccessibility.isReduceMotionEnabled }, + autoplayVideos: { UIAccessibility.isVideoAutoplayEnabled }) } diff --git a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift index 75bee39..8de168a 100644 --- a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift @@ -28,7 +28,7 @@ public extension AccountViewModel { var id: Account.Id { accountService.account.id } var headerURL: URL { - if !identityContext.appPreferences.shouldReduceMotion, identityContext.appPreferences.animateHeaders { + if identityContext.appPreferences.animateHeaders { return accountService.account.header } else { return accountService.account.headerStatic @@ -64,9 +64,8 @@ public extension AccountViewModel { var isSelf: Bool { accountService.account.id == identityContext.identity.account?.id } func avatarURL(profile: Bool = false) -> URL { - if !identityContext.appPreferences.shouldReduceMotion, - (identityContext.appPreferences.animateAvatars == .everywhere - || (identityContext.appPreferences.animateAvatars == .profiles && profile)) { + if identityContext.appPreferences.animateAvatars == .everywhere + || (identityContext.appPreferences.animateAvatars == .profiles && profile) { return accountService.account.avatar } else { return accountService.account.avatarStatic diff --git a/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift b/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift index bc5b25e..6a233e0 100644 --- a/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/StatusViewModel.swift @@ -82,8 +82,7 @@ public extension StatusViewModel { var accountName: String { "@".appending(statusService.status.displayStatus.account.acct) } var avatarURL: URL { - if !identityContext.appPreferences.shouldReduceMotion, - identityContext.appPreferences.animateAvatars == .everywhere { + if identityContext.appPreferences.animateAvatars == .everywhere { return statusService.status.displayStatus.account.avatar } else { return statusService.status.displayStatus.account.avatarStatic @@ -91,8 +90,7 @@ public extension StatusViewModel { } var rebloggerAvatarURL: URL { - if !identityContext.appPreferences.shouldReduceMotion, - identityContext.appPreferences.animateAvatars == .everywhere { + if identityContext.appPreferences.animateAvatars == .everywhere { return statusService.status.account.avatar } else { return statusService.status.account.avatarStatic diff --git a/Views/SwiftUI/PreferencesView.swift b/Views/SwiftUI/PreferencesView.swift index a667f28..f9e68e5 100644 --- a/Views/SwiftUI/PreferencesView.swift +++ b/Views/SwiftUI/PreferencesView.swift @@ -1,5 +1,6 @@ // Copyright © 2020 Metabolist. All rights reserved. +import Combine import Mastodon import SwiftUI import ViewModels @@ -107,40 +108,31 @@ struct PreferencesView: View { Toggle("preferences.links.use-universal-links", isOn: $identityContext.appPreferences.useUniversalLinks) } - if accessibilityReduceMotion { - Toggle("preferences.media.use-system-reduce-motion", - isOn: $identityContext.appPreferences.useSystemReduceMotionForMedia) - } } Group { Picker("preferences.media.autoplay.gifs", - selection: reduceMotion ? .constant(.never) : $identityContext.appPreferences.autoplayGIFs) { + selection: $identityContext.appPreferences.autoplayGIFs) { ForEach(AppPreferences.Autoplay.allCases) { option in Text(option.localizedStringKey).tag(option) } } Picker("preferences.media.autoplay.videos", - selection: reduceMotion - ? .constant(.never) : $identityContext.appPreferences.autoplayVideos) { + selection: $identityContext.appPreferences.autoplayVideos) { ForEach(AppPreferences.Autoplay.allCases) { option in Text(option.localizedStringKey).tag(option) } } Picker("preferences.media.avatars.animate", - selection: reduceMotion - ? .constant(.never) : $identityContext.appPreferences.animateAvatars) { + selection: $identityContext.appPreferences.animateAvatars) { ForEach(AppPreferences.AnimateAvatars.allCases) { option in Text(option.localizedStringKey).tag(option) } } Toggle("preferences.media.custom-emojis.animate", - isOn: reduceMotion ? .constant(false) : $identityContext.appPreferences.animateCustomEmojis) - .disabled(reduceMotion) + isOn: $identityContext.appPreferences.animateCustomEmojis) Toggle("preferences.media.headers.animate", - isOn: reduceMotion ? .constant(false) : $identityContext.appPreferences.animateHeaders) - .disabled(reduceMotion) + isOn: $identityContext.appPreferences.animateHeaders) } - .disabled(reduceMotion) if viewModel.identityContext.identity.authenticated && !viewModel.identityContext.identity.pending { Picker("preferences.home-timeline-position-on-startup", @@ -154,12 +146,10 @@ struct PreferencesView: View { } .navigationTitle("preferences") .alertItem($viewModel.alertItem) - } -} - -private extension PreferencesView { - var reduceMotion: Bool { - identityContext.appPreferences.shouldReduceMotion + .onReceive(NotificationCenter.default.publisher( + for: UIAccessibility.videoAutoplayStatusDidChangeNotification)) { _ in + viewModel.objectWillChange.send() + } } } diff --git a/Views/UIKit/CompositionView.swift b/Views/UIKit/CompositionView.swift index abe9e03..9ea40b7 100644 --- a/Views/UIKit/CompositionView.swift +++ b/Views/UIKit/CompositionView.swift @@ -195,7 +195,6 @@ private extension CompositionView { guard let self = self else { return } let avatarURL = $0.appPreferences.animateAvatars == .everywhere - && !$0.appPreferences.shouldReduceMotion ? $0.identity.account?.avatar : $0.identity.account?.avatarStatic diff --git a/Views/UIKit/Content Views/AutocompleteItemView.swift b/Views/UIKit/Content Views/AutocompleteItemView.swift index 8f39ec9..4934a0a 100644 --- a/Views/UIKit/Content Views/AutocompleteItemView.swift +++ b/Views/UIKit/Content Views/AutocompleteItemView.swift @@ -73,10 +73,7 @@ private extension AutocompleteItemView { switch autocompleteItemConfiguration.item { case let .account(account): let appPreferences = autocompleteItemConfiguration.identityContext.appPreferences - let avatarURL = appPreferences.animateAvatars == .everywhere - && !appPreferences.shouldReduceMotion - ? account.avatar - : account.avatarStatic + let avatarURL = appPreferences.animateAvatars == .everywhere ? account.avatar : account.avatarStatic imageView.sd_setImage(with: avatarURL) imageView.isHidden = false diff --git a/Views/UIKit/SecondaryNavigationTitleView.swift b/Views/UIKit/SecondaryNavigationTitleView.swift index 449af4c..e813b27 100644 --- a/Views/UIKit/SecondaryNavigationTitleView.swift +++ b/Views/UIKit/SecondaryNavigationTitleView.swift @@ -66,7 +66,6 @@ private extension SecondaryNavigationTitleView { func applyViewModel() { let avatarURL = viewModel.identityContext.appPreferences.animateAvatars == .everywhere - && !viewModel.identityContext.appPreferences.shouldReduceMotion ? viewModel.identityContext.identity.account?.avatar : viewModel.identityContext.identity.account?.avatarStatic