diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents index 14c7dc2e..51df98e3 100644 --- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents +++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -195,6 +195,7 @@ + @@ -290,7 +291,7 @@ - + diff --git a/CoreDataStack/Entity/Setting.swift b/CoreDataStack/Entity/Setting.swift index 0be80f97..27971157 100644 --- a/CoreDataStack/Entity/Setting.swift +++ b/CoreDataStack/Entity/Setting.swift @@ -16,6 +16,7 @@ public final class Setting: NSManagedObject { @NSManaged public var appearanceRaw: String @NSManaged public var preferredTrueBlackDarkMode: Bool @NSManaged public var preferredStaticAvatar: Bool + @NSManaged public var preferredStaticEmoji: Bool @NSManaged public var preferredUsingDefaultBrowser: Bool @NSManaged public private(set) var createdAt: Date @@ -64,6 +65,12 @@ extension Setting { didUpdate(at: Date()) } + public func update(preferredStaticEmoji: Bool) { + guard preferredStaticEmoji != self.preferredStaticEmoji else { return } + self.preferredStaticEmoji = preferredStaticEmoji + didUpdate(at: Date()) + } + public func update(preferredUsingDefaultBrowser: Bool) { guard preferredUsingDefaultBrowser != self.preferredUsingDefaultBrowser else { return } self.preferredUsingDefaultBrowser = preferredUsingDefaultBrowser diff --git a/Localization/app.json b/Localization/app.json index 049094d4..21e91ca2 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -500,10 +500,6 @@ "light": "Always Light", "dark": "Always Dark" }, - "appearance_settings": { - "true_black_dark_mode": "True black dark mode", - "disable_avatar_animation": "Disable animated avatars" - }, "notifications": { "title": "Notifications", "favorites": "Favorites my post", @@ -520,6 +516,9 @@ }, "preference": { "title": "Preferences", + "true_black_dark_mode": "True black dark mode", + "disable_avatar_animation": "Disable animated avatars", + "disable_emoji_animation": "Disable animated emojis", "using_default_browser": "Use default browser to open links" }, "boring_zone": { diff --git a/Mastodon/Diffiable/Item/SettingsItem.swift b/Mastodon/Diffiable/Item/SettingsItem.swift index 777df750..e82d0e1f 100644 --- a/Mastodon/Diffiable/Item/SettingsItem.swift +++ b/Mastodon/Diffiable/Item/SettingsItem.swift @@ -43,12 +43,14 @@ extension SettingsItem { enum PreferenceType: CaseIterable { case darkMode case disableAvatarAnimation + case disableEmojiAnimation case useDefaultBrowser var title: String { switch self { - case .darkMode: return L10n.Scene.Settings.Section.AppearanceSettings.trueBlackDarkMode - case .disableAvatarAnimation: return L10n.Scene.Settings.Section.AppearanceSettings.disableAvatarAnimation + case .darkMode: return L10n.Scene.Settings.Section.Preference.trueBlackDarkMode + case .disableAvatarAnimation: return L10n.Scene.Settings.Section.Preference.disableAvatarAnimation + case .disableEmojiAnimation: return L10n.Scene.Settings.Section.Preference.disableEmojiAnimation case .useDefaultBrowser: return L10n.Scene.Settings.Section.Preference.usingDefaultBrowser } } diff --git a/Mastodon/Diffiable/Section/Compose/CustomEmojiPickerSection.swift b/Mastodon/Diffiable/Section/Compose/CustomEmojiPickerSection.swift index 251b52f3..e55e8849 100644 --- a/Mastodon/Diffiable/Section/Compose/CustomEmojiPickerSection.swift +++ b/Mastodon/Diffiable/Section/Compose/CustomEmojiPickerSection.swift @@ -24,7 +24,8 @@ extension CustomEmojiPickerSection { let placeholder = UIImage.placeholder(size: CustomEmojiPickerItemCollectionViewCell.itemSize, color: .systemFill) .af.imageRounded(withCornerRadius: 4) - let url = URL(string: attribute.emoji.url) + let isAnimated = !UserDefaults.shared.preferredStaticEmoji + let url = URL(string: isAnimated ? attribute.emoji.url : attribute.emoji.staticURL) cell.emojiImageView.sd_setImage( with: url, placeholderImage: placeholder, diff --git a/Mastodon/Diffiable/Section/SettingsSection.swift b/Mastodon/Diffiable/Section/SettingsSection.swift index e329bd12..939fd431 100644 --- a/Mastodon/Diffiable/Section/SettingsSection.swift +++ b/Mastodon/Diffiable/Section/SettingsSection.swift @@ -125,6 +125,8 @@ extension SettingsSection { cell.switchButton.isOn = setting.preferredTrueBlackDarkMode case .disableAvatarAnimation: cell.switchButton.isOn = setting.preferredStaticAvatar + case .disableEmojiAnimation: + cell.switchButton.isOn = setting.preferredStaticEmoji case .useDefaultBrowser: cell.switchButton.isOn = setting.preferredUsingDefaultBrowser } diff --git a/Mastodon/Extension/CoreDataStack/Emojis.swift b/Mastodon/Extension/CoreDataStack/Emojis.swift index 2f45880b..c318e8ed 100644 --- a/Mastodon/Extension/CoreDataStack/Emojis.swift +++ b/Mastodon/Extension/CoreDataStack/Emojis.swift @@ -13,6 +13,8 @@ protocol EmojiContainer { var emojisData: Data? { get } } +// FIXME: `Mastodon.Entity.Account` extension + extension EmojiContainer { static func encode(emojis: [Mastodon.Entity.Emoji]) -> Data? { @@ -25,9 +27,11 @@ extension EmojiContainer { } var emojiMeta: MastodonContent.Emojis { + let isAnimated = !UserDefaults.shared.preferredStaticEmoji + var dict = MastodonContent.Emojis() for emoji in emojis ?? [] { - dict[emoji.shortcode] = emoji.url + dict[emoji.shortcode] = isAnimated ? emoji.url : emoji.staticURL } return dict } diff --git a/Mastodon/Extension/MastodonSDK/Mastodon+Entity+Account.swift b/Mastodon/Extension/MastodonSDK/Mastodon+Entity+Account.swift index de703fd1..6aad350c 100644 --- a/Mastodon/Extension/MastodonSDK/Mastodon+Entity+Account.swift +++ b/Mastodon/Extension/MastodonSDK/Mastodon+Entity+Account.swift @@ -32,9 +32,11 @@ extension Mastodon.Entity.Account { extension Mastodon.Entity.Account { var emojiMeta: MastodonContent.Emojis { + let isAnimated = !UserDefaults.shared.preferredStaticEmoji + var dict = MastodonContent.Emojis() for emoji in emojis ?? [] { - dict[emoji.shortcode] = emoji.url + dict[emoji.shortcode] = isAnimated ? emoji.url : emoji.staticURL } return dict } diff --git a/Mastodon/Generated/Strings.swift b/Mastodon/Generated/Strings.swift index 8d5443ab..0e31e5da 100644 --- a/Mastodon/Generated/Strings.swift +++ b/Mastodon/Generated/Strings.swift @@ -943,12 +943,6 @@ internal enum L10n { /// Appearance internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.Appearance.Title") } - internal enum AppearanceSettings { - /// Disable animated avatars - internal static let disableAvatarAnimation = L10n.tr("Localizable", "Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation") - /// True black dark mode - internal static let trueBlackDarkMode = L10n.tr("Localizable", "Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode") - } internal enum BoringZone { /// Account Settings internal static let accountSettings = L10n.tr("Localizable", "Scene.Settings.Section.BoringZone.AccountSettings") @@ -984,8 +978,14 @@ internal enum L10n { } } internal enum Preference { + /// Disable animated avatars + internal static let disableAvatarAnimation = L10n.tr("Localizable", "Scene.Settings.Section.Preference.DisableAvatarAnimation") + /// Disable animated emojis + internal static let disableEmojiAnimation = L10n.tr("Localizable", "Scene.Settings.Section.Preference.DisableEmojiAnimation") /// Preferences internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.Preference.Title") + /// True black dark mode + internal static let trueBlackDarkMode = L10n.tr("Localizable", "Scene.Settings.Section.Preference.TrueBlackDarkMode") /// Use default browser to open links internal static let usingDefaultBrowser = L10n.tr("Localizable", "Scene.Settings.Section.Preference.UsingDefaultBrowser") } diff --git a/Mastodon/Preference/AppearancePreference.swift b/Mastodon/Preference/AppearancePreference.swift index 4630c123..034bf965 100644 --- a/Mastodon/Preference/AppearancePreference.swift +++ b/Mastodon/Preference/AppearancePreference.swift @@ -26,4 +26,13 @@ extension UserDefaults { set { self[#function] = newValue } } + @objc dynamic var preferredStaticEmoji: Bool { + get { + // default false + // without set register to profile timeline performance + return bool(forKey: #function) + } + set { self[#function] = newValue } + } + } diff --git a/Mastodon/Resources/ar.lproj/Localizable.strings b/Mastodon/Resources/ar.lproj/Localizable.strings index 99babb0b..0d09e459 100644 --- a/Mastodon/Resources/ar.lproj/Localizable.strings +++ b/Mastodon/Resources/ar.lproj/Localizable.strings @@ -321,8 +321,6 @@ any server."; "Scene.Settings.Section.Appearance.Dark" = "Always Dark"; "Scene.Settings.Section.Appearance.Light" = "Always Light"; "Scene.Settings.Section.Appearance.Title" = "Appearance"; -"Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable animated avatars"; -"Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black dark mode"; "Scene.Settings.Section.BoringZone.AccountSettings" = "Account Settings"; "Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy"; "Scene.Settings.Section.BoringZone.Terms" = "Terms of Service"; @@ -337,7 +335,10 @@ any server."; "Scene.Settings.Section.Notifications.Trigger.Follower" = "a follower"; "Scene.Settings.Section.Notifications.Trigger.Noone" = "no one"; "Scene.Settings.Section.Notifications.Trigger.Title" = "Notify me when"; +"Scene.Settings.Section.Preference.DisableAvatarAnimation" = "Disable animated avatars"; +"Scene.Settings.Section.Preference.DisableEmojiAnimation" = "Disable animated emojis"; "Scene.Settings.Section.Preference.Title" = "Preferences"; +"Scene.Settings.Section.Preference.TrueBlackDarkMode" = "True black dark mode"; "Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Use default browser to open links"; "Scene.Settings.Section.SpicyZone.Clear" = "Clear Media Cache"; "Scene.Settings.Section.SpicyZone.Signout" = "Sign Out"; diff --git a/Mastodon/Resources/en.lproj/Localizable.strings b/Mastodon/Resources/en.lproj/Localizable.strings index 99babb0b..0d09e459 100644 --- a/Mastodon/Resources/en.lproj/Localizable.strings +++ b/Mastodon/Resources/en.lproj/Localizable.strings @@ -321,8 +321,6 @@ any server."; "Scene.Settings.Section.Appearance.Dark" = "Always Dark"; "Scene.Settings.Section.Appearance.Light" = "Always Light"; "Scene.Settings.Section.Appearance.Title" = "Appearance"; -"Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable animated avatars"; -"Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black dark mode"; "Scene.Settings.Section.BoringZone.AccountSettings" = "Account Settings"; "Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy"; "Scene.Settings.Section.BoringZone.Terms" = "Terms of Service"; @@ -337,7 +335,10 @@ any server."; "Scene.Settings.Section.Notifications.Trigger.Follower" = "a follower"; "Scene.Settings.Section.Notifications.Trigger.Noone" = "no one"; "Scene.Settings.Section.Notifications.Trigger.Title" = "Notify me when"; +"Scene.Settings.Section.Preference.DisableAvatarAnimation" = "Disable animated avatars"; +"Scene.Settings.Section.Preference.DisableEmojiAnimation" = "Disable animated emojis"; "Scene.Settings.Section.Preference.Title" = "Preferences"; +"Scene.Settings.Section.Preference.TrueBlackDarkMode" = "True black dark mode"; "Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Use default browser to open links"; "Scene.Settings.Section.SpicyZone.Clear" = "Clear Media Cache"; "Scene.Settings.Section.SpicyZone.Signout" = "Sign Out"; diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift index e8ff5784..122a853e 100644 --- a/Mastodon/Scene/Settings/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/SettingsViewController.swift @@ -486,6 +486,8 @@ extension SettingsViewController: SettingsToggleCellDelegate { setting.update(preferredTrueBlackDarkMode: isOn) case .disableAvatarAnimation: setting.update(preferredStaticAvatar: isOn) + case .disableEmojiAnimation: + setting.update(preferredStaticEmoji: isOn) case .useDefaultBrowser: setting.update(preferredUsingDefaultBrowser: isOn) } @@ -498,6 +500,8 @@ extension SettingsViewController: SettingsToggleCellDelegate { ThemeService.shared.set(themeName: isOn ? .system : .mastodon) case .disableAvatarAnimation: UserDefaults.shared.preferredStaticAvatar = isOn + case .disableEmojiAnimation: + UserDefaults.shared.preferredStaticEmoji = isOn case .useDefaultBrowser: UserDefaults.shared.preferredUsingDefaultBrowser = isOn } diff --git a/Mastodon/Scene/Settings/SettingsViewModel.swift b/Mastodon/Scene/Settings/SettingsViewModel.swift index b586195e..9bc0adce 100644 --- a/Mastodon/Scene/Settings/SettingsViewModel.swift +++ b/Mastodon/Scene/Settings/SettingsViewModel.swift @@ -121,11 +121,9 @@ extension SettingsViewModel { // preference snapshot.appendSections([.preference]) - let preferenceItems: [SettingsItem] = [ - .preference(settingObjectID: setting.objectID, preferenceType: .darkMode), - .preference(settingObjectID: setting.objectID, preferenceType: .disableAvatarAnimation), - .preference(settingObjectID: setting.objectID, preferenceType: .useDefaultBrowser), - ] + let preferenceItems: [SettingsItem] = SettingsItem.PreferenceType.allCases.map { preferenceType in + SettingsItem.preference(settingObjectID: setting.objectID, preferenceType: preferenceType) + } snapshot.appendItems(preferenceItems,toSection: .preference) // boring zone diff --git a/Mastodon/Service/SettingService.swift b/Mastodon/Service/SettingService.swift index 37246765..5985f6fc 100644 --- a/Mastodon/Service/SettingService.swift +++ b/Mastodon/Service/SettingService.swift @@ -211,9 +211,15 @@ extension SettingService { UserDefaults.shared.preferredStaticAvatar = setting.preferredStaticAvatar } + // set emoji mode + if UserDefaults.shared.preferredStaticEmoji != setting.preferredStaticEmoji { + UserDefaults.shared.preferredStaticEmoji = setting.preferredStaticEmoji + } + // set browser if UserDefaults.shared.preferredUsingDefaultBrowser != setting.preferredUsingDefaultBrowser { UserDefaults.shared.preferredUsingDefaultBrowser = setting.preferredUsingDefaultBrowser } + } }