From 9b9979647595de2ce23e9ac0a574ece0d5144f62 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 12:51:22 +0800 Subject: [PATCH 1/7] chore: update version to 0.9.2 (42) --- Mastodon.xcodeproj/project.pbxproj | 56 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index bbe751571..ec03336bd 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4308,7 +4308,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4316,7 +4316,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4335,7 +4335,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4343,7 +4343,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4600,7 +4600,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4608,13 +4608,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -4624,7 +4624,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4632,13 +4632,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = "ASDK - Debug"; }; @@ -4648,7 +4648,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4656,13 +4656,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = "ASDK - Release"; }; @@ -4672,7 +4672,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4680,13 +4680,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -4761,7 +4761,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4769,7 +4769,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4876,7 +4876,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4884,7 +4884,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -4995,7 +4995,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5003,7 +5003,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -5110,7 +5110,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5118,7 +5118,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -5164,7 +5164,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5172,7 +5172,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -5187,7 +5187,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5195,7 +5195,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.9.1; + MARKETING_VERSION = 0.9.2; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; From ccd26c144fec01697b64457abefd1c0744330ab5 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 12:56:20 +0800 Subject: [PATCH 2/7] chore: add index meta and missing reverse relationship for SearchHistory --- .../CoreData.xcdatamodel/contents | 9 ++++--- CoreDataStack/Entity/MastodonUser.swift | 1 + CoreDataStack/Entity/SearchHistory.swift | 27 +++++++++++++++++-- .../xcschemes/xcschememanagement.plist | 6 ++--- .../SearchHistoryViewModel.swift | 7 +++-- .../SearchResult/SearchResultViewModel.swift | 5 ++-- 6 files changed, 43 insertions(+), 12 deletions(-) diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents index 5bc61b648..14c7dc2ec 100644 --- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents +++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents @@ -85,7 +85,7 @@ - + @@ -132,6 +132,7 @@ + @@ -181,8 +182,10 @@ + + @@ -281,12 +284,12 @@ - + - + diff --git a/CoreDataStack/Entity/MastodonUser.swift b/CoreDataStack/Entity/MastodonUser.swift index 6b27b4cd8..b7a101152 100644 --- a/CoreDataStack/Entity/MastodonUser.swift +++ b/CoreDataStack/Entity/MastodonUser.swift @@ -47,6 +47,7 @@ final public class MastodonUser: NSManagedObject { // one-to-many relationship @NSManaged public private(set) var statuses: Set? + @NSManaged public private(set) var notifications: Set? // many-to-many relationship @NSManaged public private(set) var favourite: Set? diff --git a/CoreDataStack/Entity/SearchHistory.swift b/CoreDataStack/Entity/SearchHistory.swift index da6d98bc2..37191bbe5 100644 --- a/CoreDataStack/Entity/SearchHistory.swift +++ b/CoreDataStack/Entity/SearchHistory.swift @@ -11,6 +11,8 @@ import CoreData public final class SearchHistory: NSManagedObject { public typealias ID = UUID @NSManaged public private(set) var identifier: ID + @NSManaged public private(set) var domain: String + @NSManaged public private(set) var userID: MastodonUser.ID @NSManaged public private(set) var createAt: Date @NSManaged public private(set) var updatedAt: Date @@ -37,9 +39,12 @@ extension SearchHistory { @discardableResult public static func insert( into context: NSManagedObjectContext, + property: Property, account: MastodonUser ) -> SearchHistory { let searchHistory: SearchHistory = context.insertObject() + searchHistory.domain = property.domain + searchHistory.userID = property.userID searchHistory.account = account return searchHistory } @@ -47,9 +52,12 @@ extension SearchHistory { @discardableResult public static func insert( into context: NSManagedObjectContext, + property: Property, hashtag: Tag ) -> SearchHistory { let searchHistory: SearchHistory = context.insertObject() + searchHistory.domain = property.domain + searchHistory.userID = property.userID searchHistory.hashtag = hashtag return searchHistory } @@ -57,20 +65,35 @@ extension SearchHistory { @discardableResult public static func insert( into context: NSManagedObjectContext, + property: Property, status: Status ) -> SearchHistory { let searchHistory: SearchHistory = context.insertObject() + searchHistory.domain = property.domain + searchHistory.userID = property.userID searchHistory.status = status return searchHistory } } -public extension SearchHistory { - func update(updatedAt: Date) { +extension SearchHistory { + public func update(updatedAt: Date) { setValue(updatedAt, forKey: #keyPath(SearchHistory.updatedAt)) } } +extension SearchHistory { + public struct Property { + public let domain: String + public let userID: MastodonUser.ID + + public init(domain: String, userID: MastodonUser.ID) { + self.domain = domain + self.userID = userID + } + } +} + extension SearchHistory: Managed { public static var defaultSortDescriptors: [NSSortDescriptor] { return [NSSortDescriptor(keyPath: \SearchHistory.updatedAt, ascending: false)] diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 238c933d4..fd09596bc 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ CoreDataStack.xcscheme_^#shared#^_ orderHint - 21 + 22 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -37,12 +37,12 @@ NotificationService.xcscheme_^#shared#^_ orderHint - 22 + 23 ShareActionExtension.xcscheme_^#shared#^_ orderHint - 23 + 21 SuppressBuildableAutocreation diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift index acfd995ff..d3d41e90a 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift @@ -81,6 +81,9 @@ extension SearchHistoryViewModel { extension SearchHistoryViewModel { func persistSearchHistory(for item: SearchHistoryItem) { + guard let box = context.authenticationService.activeMastodonAuthenticationBox.value else { return } + let property = SearchHistory.Property(domain: box.domain, userID: box.userID) + switch item { case .account(let objectID): let managedObjectContext = context.backgroundManagedObjectContext @@ -89,7 +92,7 @@ extension SearchHistoryViewModel { if let searchHistory = user.searchHistory { searchHistory.update(updatedAt: Date()) } else { - SearchHistory.insert(into: managedObjectContext, account: user) + SearchHistory.insert(into: managedObjectContext, property: property, account: user) } } .sink { result in @@ -104,7 +107,7 @@ extension SearchHistoryViewModel { if let searchHistory = hashtag.searchHistory { searchHistory.update(updatedAt: Date()) } else { - SearchHistory.insert(into: managedObjectContext, hashtag: hashtag) + SearchHistory.insert(into: managedObjectContext, property: property, hashtag: hashtag) } } .sink { result in diff --git a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel.swift b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel.swift index 0ace96226..181302a24 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel.swift @@ -142,6 +142,7 @@ extension SearchResultViewModel { extension SearchResultViewModel { func persistSearchHistory(for item: SearchResultItem) { guard let box = context.authenticationService.activeMastodonAuthenticationBox.value else { return } + let property = SearchHistory.Property(domain: box.domain, userID: box.userID) let domain = box.domain switch item { @@ -160,7 +161,7 @@ extension SearchResultViewModel { if let searchHistory = user.searchHistory { searchHistory.update(updatedAt: Date()) } else { - SearchHistory.insert(into: managedObjectContext, account: user) + SearchHistory.insert(into: managedObjectContext, property: property, account: user) } } .sink { result in @@ -178,7 +179,7 @@ extension SearchResultViewModel { if let searchHistory = hashtag.searchHistory { searchHistory.update(updatedAt: Date()) } else { - SearchHistory.insert(into: managedObjectContext, hashtag: hashtag) + SearchHistory.insert(into: managedObjectContext, property: property, hashtag: hashtag) } } .sink { result in From 681f78856a3791cefca6126ee99201edb2c33467 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 13:06:22 +0800 Subject: [PATCH 3/7] fix: ActiveLabel not respect paragraph tag issue --- Mastodon/Helper/MastodonStatusContent.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mastodon/Helper/MastodonStatusContent.swift b/Mastodon/Helper/MastodonStatusContent.swift index 90e697daf..917c456d2 100755 --- a/Mastodon/Helper/MastodonStatusContent.swift +++ b/Mastodon/Helper/MastodonStatusContent.swift @@ -29,7 +29,7 @@ public enum MastodonStatusContent { public static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult { let document: String = { - var content = content + var content = content.replacingOccurrences(of: "

", with: "

\r\n") for (shortcode, url) in emojiDict { let emojiNode = "\(shortcode)" let pattern = ":\(shortcode):" From a8c29789f51f82504efdf19483c8bf8d9516db6b Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 13:47:56 +0800 Subject: [PATCH 4/7] fix: setting item reuse issue --- Mastodon/Diffiable/Item/SettingsItem.swift | 48 ++++++- .../Diffiable/Section/SettingsSection.swift | 126 ++++++++++++++++- .../Settings/SettingsViewController.swift | 55 +++----- .../Scene/Settings/SettingsViewModel.swift | 129 ++---------------- .../Cell/SettingsToggleTableViewCell.swift | 6 + 5 files changed, 199 insertions(+), 165 deletions(-) diff --git a/Mastodon/Diffiable/Item/SettingsItem.swift b/Mastodon/Diffiable/Item/SettingsItem.swift index e23b3f36f..777df750d 100644 --- a/Mastodon/Diffiable/Item/SettingsItem.swift +++ b/Mastodon/Diffiable/Item/SettingsItem.swift @@ -8,12 +8,10 @@ import UIKit import CoreData -enum SettingsItem: Hashable { +enum SettingsItem { case appearance(settingObjectID: NSManagedObjectID) case notification(settingObjectID: NSManagedObjectID, switchMode: NotificationSwitchMode) - case preferenceDarkMode(settingObjectID: NSManagedObjectID) - case preferenceDisableAvatarAnimation(settingObjectID: NSManagedObjectID) - case preferenceUsingDefaultBrowser(settingObjectID: NSManagedObjectID) + case preference(settingObjectID: NSManagedObjectID, preferenceType: PreferenceType) case boringZone(item: Link) case spicyZone(item: Link) } @@ -26,7 +24,7 @@ extension SettingsItem { case dark } - enum NotificationSwitchMode: CaseIterable { + enum NotificationSwitchMode: CaseIterable, Hashable { case favorite case follow case reblog @@ -41,8 +39,22 @@ extension SettingsItem { } } } + + enum PreferenceType: CaseIterable { + case darkMode + case disableAvatarAnimation + 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 .useDefaultBrowser: return L10n.Scene.Settings.Section.Preference.usingDefaultBrowser + } + } + } - enum Link: CaseIterable { + enum Link: CaseIterable, Hashable { case accountSettings case termsOfService case privacyPolicy @@ -71,3 +83,27 @@ extension SettingsItem { } } + +extension SettingsItem: Hashable { + func hash(into hasher: inout Hasher) { + switch self { + case .appearance(let settingObjectID): + hasher.combine(String(describing: SettingsItem.AppearanceMode.self)) + hasher.combine(settingObjectID) + case .notification(let settingObjectID, let switchMode): + hasher.combine(String(describing: SettingsItem.notification.self)) + hasher.combine(settingObjectID) + hasher.combine(switchMode) + case .preference(let settingObjectID, let preferenceType): + hasher.combine(String(describing: SettingsItem.preference.self)) + hasher.combine(settingObjectID) + hasher.combine(preferenceType) + case .boringZone(let link): + hasher.combine(String(describing: SettingsItem.boringZone.self)) + hasher.combine(link) + case .spicyZone(let link): + hasher.combine(String(describing: SettingsItem.spicyZone.self)) + hasher.combine(link) + } + } +} diff --git a/Mastodon/Diffiable/Section/SettingsSection.swift b/Mastodon/Diffiable/Section/SettingsSection.swift index 4d3c13d27..e329bd120 100644 --- a/Mastodon/Diffiable/Section/SettingsSection.swift +++ b/Mastodon/Diffiable/Section/SettingsSection.swift @@ -5,7 +5,9 @@ // Created by MainasuK Cirno on 2021-4-25. // -import Foundation +import UIKit +import CoreData +import CoreDataStack enum SettingsSection: Hashable { case appearance @@ -24,3 +26,125 @@ enum SettingsSection: Hashable { } } } + +extension SettingsSection { + static func tableViewDiffableDataSource( + for tableView: UITableView, + managedObjectContext: NSManagedObjectContext, + settingsAppearanceTableViewCellDelegate: SettingsAppearanceTableViewCellDelegate, + settingsToggleCellDelegate: SettingsToggleCellDelegate + ) -> UITableViewDiffableDataSource { + UITableViewDiffableDataSource(tableView: tableView) { [ + weak settingsAppearanceTableViewCellDelegate, + weak settingsToggleCellDelegate + ] tableView, indexPath, item -> UITableViewCell? in + switch item { + case .appearance(let objectID): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell + managedObjectContext.performAndWait { + let setting = managedObjectContext.object(with: objectID) as! Setting + cell.update(with: setting.appearance) + ManagedObjectObserver.observe(object: setting) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { _ in + // do nothing + }, receiveValue: { [weak cell] change in + guard let cell = cell else { return } + guard case .update(let object) = change.changeType, + let setting = object as? Setting else { return } + cell.update(with: setting.appearance) + }) + .store(in: &cell.disposeBag) + } + cell.delegate = settingsAppearanceTableViewCellDelegate + return cell + case .notification(let objectID, let switchMode): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell + managedObjectContext.performAndWait { + let setting = managedObjectContext.object(with: objectID) as! Setting + if let subscription = setting.activeSubscription { + SettingsSection.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription) + } + ManagedObjectObserver.observe(object: setting) + .sink(receiveCompletion: { _ in + // do nothing + }, receiveValue: { [weak cell] change in + guard let cell = cell else { return } + guard case .update(let object) = change.changeType, + let setting = object as? Setting else { return } + guard let subscription = setting.activeSubscription else { return } + SettingsSection.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription) + }) + .store(in: &cell.disposeBag) + } + cell.delegate = settingsToggleCellDelegate + return cell + case .preference(let objectID, _): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell + cell.delegate = settingsToggleCellDelegate + managedObjectContext.performAndWait { + let setting = managedObjectContext.object(with: objectID) as! Setting + SettingsSection.configureSettingToggle(cell: cell, item: item, setting: setting) + + ManagedObjectObserver.observe(object: setting) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { _ in + // do nothing + }, receiveValue: { [weak cell] change in + guard let cell = cell else { return } + guard case .update(let object) = change.changeType, + let setting = object as? Setting else { return } + SettingsSection.configureSettingToggle(cell: cell, item: item, setting: setting) + }) + .store(in: &cell.disposeBag) + } + return cell + case .boringZone(let item), + .spicyZone(let item): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsLinkTableViewCell.self), for: indexPath) as! SettingsLinkTableViewCell + cell.update(with: item) + return cell + } + } + } +} + +extension SettingsSection { + + static func configureSettingToggle( + cell: SettingsToggleTableViewCell, + item: SettingsItem, + setting: Setting + ) { + guard case let .preference(_, preferenceType) = item else { return } + + cell.textLabel?.text = preferenceType.title + + switch preferenceType { + case .darkMode: + cell.switchButton.isOn = setting.preferredTrueBlackDarkMode + case .disableAvatarAnimation: + cell.switchButton.isOn = setting.preferredStaticAvatar + case .useDefaultBrowser: + cell.switchButton.isOn = setting.preferredUsingDefaultBrowser + } + } + + static func configureSettingToggle( + cell: SettingsToggleTableViewCell, + switchMode: SettingsItem.NotificationSwitchMode, + subscription: NotificationSubscription + ) { + cell.textLabel?.text = switchMode.title + + let enabled: Bool? + switch switchMode { + case .favorite: enabled = subscription.alert.favourite + case .follow: enabled = subscription.alert.follow + case .reblog: enabled = subscription.alert.reblog + case .mention: enabled = subscription.alert.mention + } + cell.update(enabled: enabled) + } + +} diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift index a295272ee..744900bed 100644 --- a/Mastodon/Scene/Settings/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/SettingsViewController.swift @@ -358,13 +358,10 @@ extension SettingsViewController: UITableViewDelegate { case .appearance: // do nothing break - case .preferenceDarkMode, .preferenceDisableAvatarAnimation: - // do nothing - break case .notification: // do nothing break - case .preferenceUsingDefaultBrowser: + case .preference: // do nothing break case .boringZone(let link), .spicyZone(let link): @@ -476,48 +473,30 @@ extension SettingsViewController: SettingsToggleCellDelegate { // do nothing } .store(in: &disposeBag) - case .preferenceDarkMode(let settingObjectID): + case .preference(let settingObjectID, let preferenceType): let managedObjectContext = context.backgroundManagedObjectContext managedObjectContext.performChanges { let setting = managedObjectContext.object(with: settingObjectID) as! Setting - setting.update(preferredTrueBlackDarkMode: isOn) - } - .sink { result in - switch result { - case .success: - ThemeService.shared.set(themeName: isOn ? .system : .mastodon) - case .failure(let error): - assertionFailure(error.localizedDescription) - break + switch preferenceType { + case .darkMode: + setting.update(preferredTrueBlackDarkMode: isOn) + case .disableAvatarAnimation: + setting.update(preferredStaticAvatar: isOn) + case .useDefaultBrowser: + setting.update(preferredUsingDefaultBrowser: isOn) } } - .store(in: &disposeBag) - case .preferenceDisableAvatarAnimation(let settingObjectID): - let managedObjectContext = context.backgroundManagedObjectContext - managedObjectContext.performChanges { - let setting = managedObjectContext.object(with: settingObjectID) as! Setting - setting.update(preferredStaticAvatar: isOn) - } .sink { result in switch result { case .success: - UserDefaults.shared.preferredStaticAvatar = isOn - case .failure(let error): - assertionFailure(error.localizedDescription) - break - } - } - .store(in: &disposeBag) - case .preferenceUsingDefaultBrowser(let settingObjectID): - let managedObjectContext = context.backgroundManagedObjectContext - managedObjectContext.performChanges { - let setting = managedObjectContext.object(with: settingObjectID) as! Setting - setting.update(preferredUsingDefaultBrowser: isOn) - } - .sink { result in - switch result { - case .success: - UserDefaults.shared.preferredUsingDefaultBrowser = isOn + switch preferenceType { + case .darkMode: + ThemeService.shared.set(themeName: isOn ? .system : .mastodon) + case .disableAvatarAnimation: + UserDefaults.shared.preferredStaticAvatar = isOn + case .useDefaultBrowser: + UserDefaults.shared.preferredUsingDefaultBrowser = isOn + } case .failure(let error): assertionFailure(error.localizedDescription) break diff --git a/Mastodon/Scene/Settings/SettingsViewModel.swift b/Mastodon/Scene/Settings/SettingsViewModel.swift index 2d7fafde3..b586195e6 100644 --- a/Mastodon/Scene/Settings/SettingsViewModel.swift +++ b/Mastodon/Scene/Settings/SettingsViewModel.swift @@ -122,9 +122,9 @@ extension SettingsViewModel { // preference snapshot.appendSections([.preference]) let preferenceItems: [SettingsItem] = [ - .preferenceDarkMode(settingObjectID: setting.objectID), - .preferenceDisableAvatarAnimation(settingObjectID: setting.objectID), - .preferenceUsingDefaultBrowser(settingObjectID: setting.objectID), + .preference(settingObjectID: setting.objectID, preferenceType: .darkMode), + .preference(settingObjectID: setting.objectID, preferenceType: .disableAvatarAnimation), + .preference(settingObjectID: setting.objectID, preferenceType: .useDefaultBrowser), ] snapshot.appendItems(preferenceItems,toSection: .preference) @@ -163,123 +163,12 @@ extension SettingsViewModel { settingsAppearanceTableViewCellDelegate: SettingsAppearanceTableViewCellDelegate, settingsToggleCellDelegate: SettingsToggleCellDelegate ) { - dataSource = UITableViewDiffableDataSource(tableView: tableView) { [ - weak self, - weak settingsAppearanceTableViewCellDelegate, - weak settingsToggleCellDelegate - ] tableView, indexPath, item -> UITableViewCell? in - guard let self = self else { return nil } - switch item { - case .appearance(let objectID): - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell - self.context.managedObjectContext.performAndWait { - let setting = self.context.managedObjectContext.object(with: objectID) as! Setting - cell.update(with: setting.appearance) - ManagedObjectObserver.observe(object: setting) - .receive(on: DispatchQueue.main) - .sink(receiveCompletion: { _ in - // do nothing - }, receiveValue: { [weak cell] change in - guard let cell = cell else { return } - guard case .update(let object) = change.changeType, - let setting = object as? Setting else { return } - cell.update(with: setting.appearance) - }) - .store(in: &cell.disposeBag) - } - cell.delegate = settingsAppearanceTableViewCellDelegate - return cell - case .preferenceDarkMode(let objectID), - .preferenceDisableAvatarAnimation(let objectID), - .preferenceUsingDefaultBrowser(let objectID): - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell - cell.delegate = settingsToggleCellDelegate - self.context.managedObjectContext.performAndWait { - let setting = self.context.managedObjectContext.object(with: objectID) as! Setting - SettingsViewModel.configureSettingToggle(cell: cell, item: item, setting: setting) - - ManagedObjectObserver.observe(object: setting) - .receive(on: DispatchQueue.main) - .sink(receiveCompletion: { _ in - // do nothing - }, receiveValue: { [weak cell] change in - guard let cell = cell else { return } - guard case .update(let object) = change.changeType, - let setting = object as? Setting else { return } - SettingsViewModel.configureSettingToggle(cell: cell, item: item, setting: setting) - }) - .store(in: &cell.disposeBag) - } - return cell - case .notification(let objectID, let switchMode): - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell - self.context.managedObjectContext.performAndWait { - let setting = self.context.managedObjectContext.object(with: objectID) as! Setting - if let subscription = setting.activeSubscription { - SettingsViewModel.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription) - } - ManagedObjectObserver.observe(object: setting) - .sink(receiveCompletion: { _ in - // do nothing - }, receiveValue: { [weak cell] change in - guard let cell = cell else { return } - guard case .update(let object) = change.changeType, - let setting = object as? Setting else { return } - guard let subscription = setting.activeSubscription else { return } - SettingsViewModel.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription) - }) - .store(in: &cell.disposeBag) - } - cell.delegate = settingsToggleCellDelegate - return cell - case .boringZone(let item), .spicyZone(let item): - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsLinkTableViewCell.self), for: indexPath) as! SettingsLinkTableViewCell - cell.update(with: item) - return cell - } - } - + dataSource = SettingsSection.tableViewDiffableDataSource( + for: tableView, + managedObjectContext: context.managedObjectContext, + settingsAppearanceTableViewCellDelegate: settingsAppearanceTableViewCellDelegate, + settingsToggleCellDelegate: settingsToggleCellDelegate + ) processDataSource(self.setting.value) } } - -extension SettingsViewModel { - - static func configureSettingToggle( - cell: SettingsToggleTableViewCell, - item: SettingsItem, - setting: Setting - ) { - switch item { - case .preferenceDarkMode: - cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.trueBlackDarkMode - cell.switchButton.isOn = setting.preferredTrueBlackDarkMode - case .preferenceDisableAvatarAnimation: - cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.disableAvatarAnimation - cell.switchButton.isOn = setting.preferredStaticAvatar - case .preferenceUsingDefaultBrowser: - cell.textLabel?.text = L10n.Scene.Settings.Section.Preference.usingDefaultBrowser - cell.switchButton.isOn = setting.preferredUsingDefaultBrowser - default: - assertionFailure() - } - } - - static func configureSettingToggle( - cell: SettingsToggleTableViewCell, - switchMode: SettingsItem.NotificationSwitchMode, - subscription: NotificationSubscription - ) { - cell.textLabel?.text = switchMode.title - - let enabled: Bool? - switch switchMode { - case .favorite: enabled = subscription.alert.favourite - case .follow: enabled = subscription.alert.follow - case .reblog: enabled = subscription.alert.reblog - case .mention: enabled = subscription.alert.mention - } - cell.update(enabled: enabled) - } - -} diff --git a/Mastodon/Scene/Settings/View/Cell/SettingsToggleTableViewCell.swift b/Mastodon/Scene/Settings/View/Cell/SettingsToggleTableViewCell.swift index 86698d840..2bc70e65e 100644 --- a/Mastodon/Scene/Settings/View/Cell/SettingsToggleTableViewCell.swift +++ b/Mastodon/Scene/Settings/View/Cell/SettingsToggleTableViewCell.swift @@ -22,6 +22,12 @@ class SettingsToggleTableViewCell: UITableViewCell { }() weak var delegate: SettingsToggleCellDelegate? + + override func prepareForReuse() { + super.prepareForReuse() + + disposeBag.removeAll() + } // MARK: - Methods override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { From f4056f104929c0c03768d151f6942746acef5579 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 14:05:20 +0800 Subject: [PATCH 5/7] fix: CGColor not follow user interfaces style issue --- Mastodon/Generated/Assets.swift | 6 ++---- .../Assets.xcassets/Colors/TabBar/Contents.json | 9 --------- .../Contents.json | 12 ++++++------ .../Contents.json | 12 ++++++------ .../NotificationStatusTableViewCell.swift | 6 ++---- Mastodon/Service/ThemeService/MastodonTheme.swift | 1 + Mastodon/Service/ThemeService/SystemTheme.swift | 1 + Mastodon/Service/ThemeService/Theme.swift | 1 + 8 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json rename Mastodon/Resources/Assets.xcassets/{Colors/Border/notification.status.colorset => Theme/Mastodon/notification.status.border.color.colorset}/Contents.json (76%) rename Mastodon/Resources/Assets.xcassets/{Colors/TabBar/item.inactive.colorset => Theme/system/notification.status.border.color.colorset}/Contents.json (76%) diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index 0f405ef05..0fe48777f 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -34,7 +34,6 @@ internal enum Asset { internal enum Colors { internal enum Border { internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll") - internal static let notificationStatus = ColorAsset(name: "Colors/Border/notification.status") internal static let searchCard = ColorAsset(name: "Colors/Border/searchCard") internal static let status = ColorAsset(name: "Colors/Border/status") } @@ -65,9 +64,6 @@ internal enum Asset { internal enum Slider { internal static let track = ColorAsset(name: "Colors/Slider/track") } - internal enum TabBar { - internal static let itemInactive = ColorAsset(name: "Colors/TabBar/item.inactive") - } internal enum TextField { internal static let background = ColorAsset(name: "Colors/TextField/background") internal static let invalid = ColorAsset(name: "Colors/TextField/invalid") @@ -135,6 +131,7 @@ internal enum Asset { internal static let tableViewCellSelectionBackground = ColorAsset(name: "Theme/Mastodon/table.view.cell.selection.background") internal static let tertiarySystemBackground = ColorAsset(name: "Theme/Mastodon/tertiary.system.background") internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Theme/Mastodon/tertiary.system.grouped.background") + internal static let notificationStatusBorderColor = ColorAsset(name: "Theme/Mastodon/notification.status.border.color") internal static let separator = ColorAsset(name: "Theme/Mastodon/separator") internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/Mastodon/tab.bar.item.inactive.icon.color") } @@ -153,6 +150,7 @@ internal enum Asset { internal static let tableViewCellSelectionBackground = ColorAsset(name: "Theme/system/table.view.cell.selection.background") internal static let tertiarySystemBackground = ColorAsset(name: "Theme/system/tertiary.system.background") internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Theme/system/tertiary.system.grouped.background") + internal static let notificationStatusBorderColor = ColorAsset(name: "Theme/system/notification.status.border.color") internal static let separator = ColorAsset(name: "Theme/system/separator") internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/system/tab.bar.item.inactive.icon.color") } diff --git a/Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json deleted file mode 100644 index 6e965652d..000000000 --- a/Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Border/notification.status.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Theme/Mastodon/notification.status.border.color.colorset/Contents.json similarity index 76% rename from Mastodon/Resources/Assets.xcassets/Colors/Border/notification.status.colorset/Contents.json rename to Mastodon/Resources/Assets.xcassets/Theme/Mastodon/notification.status.border.color.colorset/Contents.json index 1067c15d9..c04af0902 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Border/notification.status.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Theme/Mastodon/notification.status.border.color.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "232", - "green" : "225", - "red" : "217" + "blue" : "0.910", + "green" : "0.882", + "red" : "0.851" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "110", - "green" : "87", - "red" : "79" + "blue" : "0.431", + "green" : "0.341", + "red" : "0.310" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/TabBar/item.inactive.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Theme/system/notification.status.border.color.colorset/Contents.json similarity index 76% rename from Mastodon/Resources/Assets.xcassets/Colors/TabBar/item.inactive.colorset/Contents.json rename to Mastodon/Resources/Assets.xcassets/Theme/system/notification.status.border.color.colorset/Contents.json index 48fc40b01..c04af0902 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/TabBar/item.inactive.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Theme/system/notification.status.border.color.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "140", - "green" : "130", - "red" : "110" + "blue" : "0.910", + "green" : "0.882", + "red" : "0.851" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "200", - "green" : "174", - "red" : "155" + "blue" : "0.431", + "green" : "0.341", + "red" : "0.310" } }, "idiom" : "universal" diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift index ab7b6a518..684d9187c 100644 --- a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift +++ b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift @@ -90,7 +90,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { view.layer.cornerRadius = 6 view.layer.cornerCurve = .continuous view.layer.borderWidth = 2 - view.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor + view.layer.borderColor = ThemeService.shared.currentTheme.value.notificationStatusBorderColor.cgColor return view }() let statusView = StatusView() @@ -272,9 +272,7 @@ extension NotificationStatusTableViewCell { extension NotificationStatusTableViewCell { private func setupBackgroundColor(theme: Theme) { -// actionImageView.layer.borderColor = theme.systemBackgroundColor.cgColor -// avatarImageView.layer.borderColor = Asset.Theme.Mastodon.systemBackground.color.cgColor - statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor + statusContainerView.layer.borderColor = theme.notificationStatusBorderColor.resolvedColor(with: traitCollection).cgColor statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor }) diff --git a/Mastodon/Service/ThemeService/MastodonTheme.swift b/Mastodon/Service/ThemeService/MastodonTheme.swift index 2418877ff..60093f04d 100644 --- a/Mastodon/Service/ThemeService/MastodonTheme.swift +++ b/Mastodon/Service/ThemeService/MastodonTheme.swift @@ -34,4 +34,5 @@ struct MastodonTheme: Theme { let contentWarningOverlayBackgroundColor = Asset.Theme.Mastodon.contentWarningOverlayBackground.color let profileFieldCollectionViewBackgroundColor = Asset.Theme.Mastodon.profileFieldCollectionViewBackground.color let composeToolbarBackgroundColor = Asset.Theme.Mastodon.composeToolbarBackground.color + let notificationStatusBorderColor = Asset.Theme.System.notificationStatusBorderColor.color } diff --git a/Mastodon/Service/ThemeService/SystemTheme.swift b/Mastodon/Service/ThemeService/SystemTheme.swift index b91ba1caf..0be42c6ed 100644 --- a/Mastodon/Service/ThemeService/SystemTheme.swift +++ b/Mastodon/Service/ThemeService/SystemTheme.swift @@ -34,4 +34,5 @@ struct SystemTheme: Theme { let contentWarningOverlayBackgroundColor = Asset.Theme.System.contentWarningOverlayBackground.color let profileFieldCollectionViewBackgroundColor = Asset.Theme.System.profileFieldCollectionViewBackground.color let composeToolbarBackgroundColor = Asset.Theme.System.composeToolbarBackground.color + let notificationStatusBorderColor = Asset.Theme.System.notificationStatusBorderColor.color } diff --git a/Mastodon/Service/ThemeService/Theme.swift b/Mastodon/Service/ThemeService/Theme.swift index 29f90db8d..15def6f53 100644 --- a/Mastodon/Service/ThemeService/Theme.swift +++ b/Mastodon/Service/ThemeService/Theme.swift @@ -35,6 +35,7 @@ public protocol Theme { var contentWarningOverlayBackgroundColor: UIColor { get } var profileFieldCollectionViewBackgroundColor: UIColor { get } var composeToolbarBackgroundColor: UIColor { get } + var notificationStatusBorderColor: UIColor { get } } From 39cdc013176db4b8e19197e1128ccb4c869f3a2d Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 14:13:49 +0800 Subject: [PATCH 6/7] fix: search history not isolate issue --- CoreDataStack/Entity/SearchHistory.swift | 17 +++++++++++++++++ ...SearchHistoryFetchedResultController.swift | 19 +++++++++++++++++++ .../SearchHistoryViewModel.swift | 9 +++++++++ 3 files changed, 45 insertions(+) diff --git a/CoreDataStack/Entity/SearchHistory.swift b/CoreDataStack/Entity/SearchHistory.swift index 37191bbe5..3894d1c1b 100644 --- a/CoreDataStack/Entity/SearchHistory.swift +++ b/CoreDataStack/Entity/SearchHistory.swift @@ -99,3 +99,20 @@ extension SearchHistory: Managed { return [NSSortDescriptor(keyPath: \SearchHistory.updatedAt, ascending: false)] } } + +extension SearchHistory { + static func predicate(domain: String) -> NSPredicate { + return NSPredicate(format: "%K == %@", #keyPath(SearchHistory.domain), domain) + } + + static func predicate(userID: String) -> NSPredicate { + return NSPredicate(format: "%K == %@", #keyPath(SearchHistory.userID), userID) + } + + public static func predicate(domain: String, userID: String) -> NSPredicate { + return NSCompoundPredicate(andPredicateWithSubpredicates: [ + predicate(domain: domain), + predicate(userID: userID) + ]) + } +} diff --git a/Mastodon/Diffiable/FetchedResultsController/SearchHistoryFetchedResultController.swift b/Mastodon/Diffiable/FetchedResultsController/SearchHistoryFetchedResultController.swift index b83bfe662..6d4461eab 100644 --- a/Mastodon/Diffiable/FetchedResultsController/SearchHistoryFetchedResultController.swift +++ b/Mastodon/Diffiable/FetchedResultsController/SearchHistoryFetchedResultController.swift @@ -17,6 +17,8 @@ final class SearchHistoryFetchedResultController: NSObject { var disposeBag = Set() let fetchedResultsController: NSFetchedResultsController + let domain = CurrentValueSubject(nil) + let userID = CurrentValueSubject(nil) // output let objectIDs = CurrentValueSubject<[NSManagedObjectID], Never>([]) @@ -38,6 +40,23 @@ final class SearchHistoryFetchedResultController: NSObject { super.init() fetchedResultsController.delegate = self + + Publishers.CombineLatest( + self.domain.removeDuplicates(), + self.userID.removeDuplicates() + ) + .receive(on: DispatchQueue.main) + .sink { [weak self] domain, userID in + guard let self = self else { return } + let predicates = [SearchHistory.predicate(domain: domain ?? "", userID: userID ?? "")] + self.fetchedResultsController.fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates) + do { + try self.fetchedResultsController.performFetch() + } catch { + assertionFailure(error.localizedDescription) + } + } + .store(in: &disposeBag) } } diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift index d3d41e90a..934c55b1b 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift @@ -25,6 +25,15 @@ final class SearchHistoryViewModel { self.context = context self.searchHistoryFetchedResultController = SearchHistoryFetchedResultController(managedObjectContext: context.managedObjectContext) + context.authenticationService.activeMastodonAuthenticationBox + .receive(on: DispatchQueue.main) + .sink { [weak self] box in + guard let self = self else { return } + self.searchHistoryFetchedResultController.domain.value = box?.domain + self.searchHistoryFetchedResultController.userID.value = box?.userID + } + .store(in: &disposeBag) + // may block main queue by large dataset searchHistoryFetchedResultController.objectIDs .removeDuplicates() From 116d74938d6dea814fafec1876102ac91896230f Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 22 Jul 2021 14:17:44 +0800 Subject: [PATCH 7/7] chore: update version to 0.9.2 (43) --- Mastodon.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index ec03336bd..fa8b06c3d 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4308,7 +4308,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4335,7 +4335,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4600,7 +4600,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4624,7 +4624,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4648,7 +4648,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4672,7 +4672,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4761,7 +4761,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4876,7 +4876,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4995,7 +4995,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5110,7 +5110,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5164,7 +5164,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5187,7 +5187,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 42; + CURRENT_PROJECT_VERSION = 43; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = (