fix: make interface style preference as global setting

This commit is contained in:
CMK 2021-10-08 18:47:46 +08:00
parent 6b1d3f8738
commit b5052cca5e
8 changed files with 55 additions and 50 deletions

View File

@ -191,7 +191,6 @@
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="searchHistories" inverseEntity="Status"/> <relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="searchHistories" inverseEntity="Status"/>
</entity> </entity>
<entity name="Setting" representedClassName=".Setting" syncable="YES"> <entity name="Setting" representedClassName=".Setting" syncable="YES">
<attribute name="appearanceRaw" attributeType="String"/>
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/> <attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="domain" attributeType="String"/> <attribute name="domain" attributeType="String"/>
<attribute name="preferredStaticAvatar" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="preferredStaticAvatar" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@ -289,7 +288,7 @@
<element name="PollOption" positionX="0" positionY="0" width="128" height="134"/> <element name="PollOption" positionX="0" positionY="0" width="128" height="134"/>
<element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/> <element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/>
<element name="SearchHistory" positionX="0" positionY="0" width="128" height="149"/> <element name="SearchHistory" positionX="0" positionY="0" width="128" height="149"/>
<element name="Setting" positionX="72" positionY="162" width="128" height="179"/> <element name="Setting" positionX="72" positionY="162" width="128" height="164"/>
<element name="Status" positionX="0" positionY="0" width="128" height="599"/> <element name="Status" positionX="0" positionY="0" width="128" height="599"/>
<element name="Subscription" positionX="81" positionY="171" width="128" height="179"/> <element name="Subscription" positionX="81" positionY="171" width="128" height="179"/>
<element name="SubscriptionAlerts" positionX="72" positionY="162" width="128" height="14"/> <element name="SubscriptionAlerts" positionX="72" positionY="162" width="128" height="14"/>

View File

@ -13,7 +13,7 @@ public final class Setting: NSManagedObject {
@NSManaged public var domain: String @NSManaged public var domain: String
@NSManaged public var userID: String @NSManaged public var userID: String
@NSManaged public var appearanceRaw: String // @NSManaged public var appearanceRaw: String
@NSManaged public var preferredTrueBlackDarkMode: Bool @NSManaged public var preferredTrueBlackDarkMode: Bool
@NSManaged public var preferredStaticAvatar: Bool @NSManaged public var preferredStaticAvatar: Bool
@NSManaged public var preferredStaticEmoji: Bool @NSManaged public var preferredStaticEmoji: Bool
@ -41,17 +41,17 @@ extension Setting {
property: Property property: Property
) -> Setting { ) -> Setting {
let setting: Setting = context.insertObject() let setting: Setting = context.insertObject()
setting.appearanceRaw = property.appearanceRaw // setting.appearanceRaw = property.appearanceRaw
setting.domain = property.domain setting.domain = property.domain
setting.userID = property.userID setting.userID = property.userID
return setting return setting
} }
public func update(appearanceRaw: String) { // public func update(appearanceRaw: String) {
guard appearanceRaw != self.appearanceRaw else { return } // guard appearanceRaw != self.appearanceRaw else { return }
self.appearanceRaw = appearanceRaw // self.appearanceRaw = appearanceRaw
didUpdate(at: Date()) // didUpdate(at: Date())
} // }
public func update(preferredTrueBlackDarkMode: Bool) { public func update(preferredTrueBlackDarkMode: Bool) {
guard preferredTrueBlackDarkMode != self.preferredTrueBlackDarkMode else { return } guard preferredTrueBlackDarkMode != self.preferredTrueBlackDarkMode else { return }
@ -87,12 +87,16 @@ extension Setting {
public struct Property { public struct Property {
public let domain: String public let domain: String
public let userID: String public let userID: String
public let appearanceRaw: String // public let appearanceRaw: String
public init(domain: String, userID: String, appearanceRaw: String) { public init(
domain: String,
userID: String
// appearanceRaw: String
) {
self.domain = domain self.domain = domain
self.userID = userID self.userID = userID
self.appearanceRaw = appearanceRaw // self.appearanceRaw = appearanceRaw
} }
} }
} }

View File

@ -41,21 +41,17 @@ extension SettingsSection {
switch item { switch item {
case .appearance(let objectID): case .appearance(let objectID):
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell
managedObjectContext.performAndWait { UserDefaults.shared.observe(\.customUserInterfaceStyle, options: [.initial, .new]) { [weak cell] defaults, _ in
let setting = managedObjectContext.object(with: objectID) as! Setting guard let cell = cell else { return }
cell.update(with: setting.appearance) switch defaults.customUserInterfaceStyle {
ManagedObjectObserver.observe(object: setting) case .unspecified: cell.update(with: .automatic)
.receive(on: DispatchQueue.main) case .dark: cell.update(with: .dark)
.sink(receiveCompletion: { _ in case .light: cell.update(with: .light)
// do nothing @unknown default:
}, receiveValue: { [weak cell] change in assertionFailure()
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)
} }
.store(in: &cell.observations)
cell.delegate = settingsAppearanceTableViewCellDelegate cell.delegate = settingsAppearanceTableViewCellDelegate
return cell return cell
case .notification(let objectID, let switchMode): case .notification(let objectID, let switchMode):

View File

@ -11,9 +11,9 @@ import MastodonSDK
extension Setting { extension Setting {
var appearance: SettingsItem.AppearanceMode { // var appearance: SettingsItem.AppearanceMode {
return SettingsItem.AppearanceMode(rawValue: appearanceRaw) ?? .automatic // return SettingsItem.AppearanceMode(rawValue: appearanceRaw) ?? .automatic
} // }
var activeSubscription: Subscription? { var activeSubscription: Subscription? {
return (subscriptions ?? Set()) return (subscriptions ?? Set())

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>

View File

@ -439,7 +439,7 @@ extension SettingsViewController {
.sink { _ in .sink { _ in
// do nothing // do nothing
} receiveValue: { _ in } receiveValue: { _ in
// do nohting // do nothing
} }
.store(in: &disposeBag) .store(in: &disposeBag)
} }
@ -451,16 +451,19 @@ extension SettingsViewController: SettingsAppearanceTableViewCellDelegate {
guard let dataSource = viewModel.dataSource else { return } guard let dataSource = viewModel.dataSource else { return }
guard let indexPath = tableView.indexPath(for: cell) else { return } guard let indexPath = tableView.indexPath(for: cell) else { return }
let item = dataSource.itemIdentifier(for: indexPath) let item = dataSource.itemIdentifier(for: indexPath)
guard case let .appearance(settingObjectID) = item else { return } guard case .appearance = item else { return }
context.managedObjectContext.performChanges { switch appearanceMode {
let setting = self.context.managedObjectContext.object(with: settingObjectID) as! Setting case .automatic:
setting.update(appearanceRaw: appearanceMode.rawValue) UserDefaults.shared.customUserInterfaceStyle = .unspecified
case .light:
UserDefaults.shared.customUserInterfaceStyle = .light
case .dark:
UserDefaults.shared.customUserInterfaceStyle = .dark
} }
.sink { _ in
let feedbackGenerator = UIImpactFeedbackGenerator(style: .light) let feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
feedbackGenerator.impactOccurred() feedbackGenerator.impactOccurred()
}.store(in: &disposeBag)
} }
} }

View File

@ -15,6 +15,7 @@ protocol SettingsAppearanceTableViewCellDelegate: AnyObject {
class SettingsAppearanceTableViewCell: UITableViewCell { class SettingsAppearanceTableViewCell: UITableViewCell {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var observations = Set<NSKeyValueObservation>()
static let spacing: CGFloat = 18 static let spacing: CGFloat = 18
@ -59,6 +60,7 @@ class SettingsAppearanceTableViewCell: UITableViewCell {
super.prepareForReuse() super.prepareForReuse()
disposeBag.removeAll() disposeBag.removeAll()
observations.removeAll()
} }
// MARK: - Methods // MARK: - Methods

View File

@ -54,8 +54,7 @@ final class SettingService {
into: managedObjectContext, into: managedObjectContext,
property: Setting.Property( property: Setting.Property(
domain: domain, domain: domain,
userID: userID, userID: userID
appearanceRaw: SettingsItem.AppearanceMode.automatic.rawValue
) )
) )
} // end for } // end for
@ -190,16 +189,16 @@ extension SettingService {
static func updatePreference(setting: Setting) { static func updatePreference(setting: Setting) {
// set appearance // set appearance
let userInterfaceStyle: UIUserInterfaceStyle = { // let userInterfaceStyle: UIUserInterfaceStyle = {
switch setting.appearance { // switch setting.appearance {
case .automatic: return .unspecified // case .automatic: return .unspecified
case .light: return .light // case .light: return .light
case .dark: return .dark // case .dark: return .dark
} // }
}() // }()
if UserDefaults.shared.customUserInterfaceStyle != userInterfaceStyle { // if UserDefaults.shared.customUserInterfaceStyle != userInterfaceStyle {
UserDefaults.shared.customUserInterfaceStyle = userInterfaceStyle // UserDefaults.shared.customUserInterfaceStyle = userInterfaceStyle
} // }
// set theme // set theme
let themeName: ThemeName = setting.preferredTrueBlackDarkMode ? .system : .mastodon let themeName: ThemeName = setting.preferredTrueBlackDarkMode ? .system : .mastodon