diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
index 1d517412a..98ed7f356 100644
--- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
+++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
@@ -191,6 +191,7 @@
+
@@ -282,7 +283,7 @@
-
+
diff --git a/CoreDataStack/Entity/Setting.swift b/CoreDataStack/Entity/Setting.swift
index 88768eda4..0be80f973 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 preferredUsingDefaultBrowser: Bool
@NSManaged public private(set) var createdAt: Date
@NSManaged public private(set) var updatedAt: Date
@@ -62,6 +63,12 @@ extension Setting {
self.preferredStaticAvatar = preferredStaticAvatar
didUpdate(at: Date())
}
+
+ public func update(preferredUsingDefaultBrowser: Bool) {
+ guard preferredUsingDefaultBrowser != self.preferredUsingDefaultBrowser else { return }
+ self.preferredUsingDefaultBrowser = preferredUsingDefaultBrowser
+ didUpdate(at: Date())
+ }
public func didUpdate(at networkDate: Date) {
self.updatedAt = networkDate
diff --git a/Localization/app.json b/Localization/app.json
index 5c8dd5cc0..50abb54b8 100644
--- a/Localization/app.json
+++ b/Localization/app.json
@@ -495,12 +495,8 @@
"dark": "Always Dark"
},
"appearance_settings": {
- "dark_mode": {
- "title": "True black Dark Mode"
- },
- "avatar_animation": {
- "title": "Disable avatar animation"
- }
+ "true_black_dark_mode": "True black Dark Mode",
+ "disable_avatar_animation": "Disable avatar animation"
},
"notifications": {
"title": "Notifications",
@@ -516,6 +512,10 @@
"title": "Notify me when"
}
},
+ "preference": {
+ "title": "Preference",
+ "using_default_browser": "Using default browser open link"
+ },
"boringzone": {
"title": "The Boring zone",
"terms": "Terms of Service",
diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj
index b6c0cf11b..b8b64df83 100644
--- a/Mastodon.xcodeproj/project.pbxproj
+++ b/Mastodon.xcodeproj/project.pbxproj
@@ -416,6 +416,7 @@
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */; };
DBA1DB80268F84F80052DB59 /* NotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA1DB7F268F84F80052DB59 /* NotificationType.swift */; };
DBA465932696B495002B41DB /* APIService+WebFinger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA465922696B495002B41DB /* APIService+WebFinger.swift */; };
+ DBA465952696E387002B41DB /* AppPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA465942696E387002B41DB /* AppPreference.swift */; };
DBA5E7A3263AD0A3004598BB /* PhotoLibraryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A2263AD0A3004598BB /* PhotoLibraryService.swift */; };
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A4263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift */; };
DBA5E7A9263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A8263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift */; };
@@ -1046,6 +1047,7 @@
DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = ""; };
DBA1DB7F268F84F80052DB59 /* NotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationType.swift; sourceTree = ""; };
DBA465922696B495002B41DB /* APIService+WebFinger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+WebFinger.swift"; sourceTree = ""; };
+ DBA465942696E387002B41DB /* AppPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPreference.swift; sourceTree = ""; };
DBA5E7A2263AD0A3004598BB /* PhotoLibraryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryService.swift; sourceTree = ""; };
DBA5E7A4263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuImagePreviewViewModel.swift; sourceTree = ""; };
DBA5E7A8263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuImagePreviewViewController.swift; sourceTree = ""; };
@@ -1987,6 +1989,7 @@
isa = PBXGroup;
children = (
DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */,
+ DBA465942696E387002B41DB /* AppPreference.swift */,
DB6D1B3C2636857500ACB481 /* AppearancePreference.swift */,
DBE54AC52636C89F004E7C0B /* NotificationPreference.swift */,
DB1D842F26566512000346B3 /* KeyboardPreference.swift */,
@@ -3228,6 +3231,7 @@
DBAC649B267DF8C8007FE9FD /* ActivityIndicatorNode.swift in Sources */,
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
2DA504692601ADE7008F4E6C /* SawToothView.swift in Sources */,
+ DBA465952696E387002B41DB /* AppPreference.swift in Sources */,
DB87D4572609DD5300D12C0D /* DeleteBackwardResponseTextField.swift in Sources */,
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */,
DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */,
@@ -4837,7 +4841,7 @@
repositoryURL = "https://github.com/TwidereProject/MetaTextView.git";
requirement = {
kind = exactVersion;
- version = 1.3.0;
+ version = 1.3.1;
};
};
DB0E2D2C26833FF600865C3C /* XCRemoteSwiftPackageReference "Nuke-FLAnimatedImage-Plugin" */ = {
diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist
index ecde1bcc5..ca6031c48 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
- 20
+ 21
Mastodon - ASDK.xcscheme_^#shared#^_
@@ -37,7 +37,7 @@
NotificationService.xcscheme_^#shared#^_
orderHint
- 21
+ 22
SuppressBuildableAutocreation
diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 1f3cc3145..5118c78b0 100644
--- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -114,8 +114,8 @@
"repositoryURL": "https://github.com/TwidereProject/MetaTextView.git",
"state": {
"branch": null,
- "revision": "e2049e14ef411c6810d53c1baf553b5161c6678f",
- "version": "1.3.0"
+ "revision": "9021b330dd72898583f62ee7f4c98768d72e7654",
+ "version": "1.3.1"
}
},
{
diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift
index b918e1812..f1f288fa1 100644
--- a/Mastodon/Coordinator/SceneCoordinator.swift
+++ b/Mastodon/Coordinator/SceneCoordinator.swift
@@ -192,8 +192,12 @@ extension SceneCoordinator {
sender?.navigationController?.pushViewController(viewController, animated: true)
case .safariPresent(let animated, let completion):
- viewController.modalPresentationCapturesStatusBarAppearance = true
- presentingViewController.present(viewController, animated: animated, completion: completion)
+ if UserDefaults.shared.preferredUsingDefaultBrowser, case let .safari(url) = scene {
+ UIApplication.shared.open(url, options: [:], completionHandler: nil)
+ } else {
+ viewController.modalPresentationCapturesStatusBarAppearance = true
+ presentingViewController.present(viewController, animated: animated, completion: completion)
+ }
case .alertController(let animated, let completion):
viewController.modalPresentationCapturesStatusBarAppearance = true
diff --git a/Mastodon/Diffiable/Item/SettingsItem.swift b/Mastodon/Diffiable/Item/SettingsItem.swift
index aca02474e..7320e9fc5 100644
--- a/Mastodon/Diffiable/Item/SettingsItem.swift
+++ b/Mastodon/Diffiable/Item/SettingsItem.swift
@@ -13,6 +13,7 @@ enum SettingsItem: Hashable {
case appearanceDarkMode(settingObjectID: NSManagedObjectID)
case appearanceDisableAvatarAnimation(settingObjectID: NSManagedObjectID)
case notification(settingObjectID: NSManagedObjectID, switchMode: NotificationSwitchMode)
+ case preferenceUsingDefaultBrowser(settingObjectID: NSManagedObjectID)
case boringZone(item: Link)
case spicyZone(item: Link)
}
diff --git a/Mastodon/Diffiable/Section/SettingsSection.swift b/Mastodon/Diffiable/Section/SettingsSection.swift
index 9a248dca3..46ac82a1a 100644
--- a/Mastodon/Diffiable/Section/SettingsSection.swift
+++ b/Mastodon/Diffiable/Section/SettingsSection.swift
@@ -11,6 +11,7 @@ enum SettingsSection: Hashable {
case appearance
case appearanceSettings
case notifications
+ case preference
case boringZone
case spicyZone
@@ -19,6 +20,7 @@ enum SettingsSection: Hashable {
case .appearance: return L10n.Scene.Settings.Section.Appearance.title
case .appearanceSettings: return ""
case .notifications: return L10n.Scene.Settings.Section.Notifications.title
+ case .preference: return L10n.Scene.Settings.Section.Preference.title
case .boringZone: return L10n.Scene.Settings.Section.Boringzone.title
case .spicyZone: return L10n.Scene.Settings.Section.Spicyzone.title
}
diff --git a/Mastodon/Generated/Strings.swift b/Mastodon/Generated/Strings.swift
index ab2b75f40..6ea2504a4 100644
--- a/Mastodon/Generated/Strings.swift
+++ b/Mastodon/Generated/Strings.swift
@@ -934,14 +934,10 @@ internal enum L10n {
internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.Appearance.Title")
}
internal enum AppearanceSettings {
- internal enum AvatarAnimation {
- /// Disable avatar animation
- internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.AppearanceSettings.AvatarAnimation.Title")
- }
- internal enum DarkMode {
- /// True black Dark Mode
- internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.AppearanceSettings.DarkMode.Title")
- }
+ /// Disable avatar animation
+ 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 {
/// Privacy Policy
@@ -975,6 +971,12 @@ internal enum L10n {
internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.Notifications.Trigger.Title")
}
}
+ internal enum Preference {
+ /// Preference
+ internal static let title = L10n.tr("Localizable", "Scene.Settings.Section.Preference.Title")
+ /// Using default browser open link
+ internal static let usingDefaultBrowser = L10n.tr("Localizable", "Scene.Settings.Section.Preference.UsingDefaultBrowser")
+ }
internal enum Spicyzone {
/// Clear Media Cache
internal static let clear = L10n.tr("Localizable", "Scene.Settings.Section.Spicyzone.Clear")
diff --git a/Mastodon/Preference/AppPreference.swift b/Mastodon/Preference/AppPreference.swift
new file mode 100644
index 000000000..4ede61cf8
--- /dev/null
+++ b/Mastodon/Preference/AppPreference.swift
@@ -0,0 +1,20 @@
+//
+// AppPreference.swift
+// Mastodon
+//
+// Created by MainasuK Cirno on 2021-7-8.
+//
+
+import UIKit
+
+extension UserDefaults {
+
+ @objc dynamic var preferredUsingDefaultBrowser: Bool {
+ get {
+ register(defaults: [#function: false])
+ return bool(forKey: #function)
+ }
+ set { self[#function] = newValue }
+ }
+
+}
diff --git a/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift b/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift
index eff4ad124..e732789ef 100644
--- a/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift
+++ b/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift
@@ -128,12 +128,10 @@ extension StatusProviderFacade {
static func responseToStatusActiveLabelAction(provider: StatusProvider, cell: UITableViewCell, activeLabel: ActiveLabel, didTapEntity entity: ActiveEntity) {
switch entity.type {
- case .hashtag(let text, _):
- let hashtagTimelienViewModel = HashtagTimelineViewModel(context: provider.context, hashtag: text)
- provider.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelienViewModel), from: provider, transition: .show)
- case .mention(let text, _):
- coordinateToStatusMentionProfileScene(for: .primary, provider: provider, cell: cell, mention: text)
- case .url(_, _, let url, _):
+ case .url(_, _, let url, _),
+ .mention(let url, _) where url.lowercased().hasPrefix("http"):
+ // note:
+ // some server mark the normal url as "u-url" class. :
guard let url = URL(string: url) else { return }
if let domain = provider.context.authenticationService.activeMastodonAuthenticationBox.value?.domain, url.host == domain,
url.pathComponents.count >= 4,
@@ -146,6 +144,12 @@ extension StatusProviderFacade {
} else {
provider.coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
}
+ case .hashtag(let text, _):
+ let hashtagTimelienViewModel = HashtagTimelineViewModel(context: provider.context, hashtag: text)
+ provider.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelienViewModel), from: provider, transition: .show)
+ case .mention(let text, let userInfo):
+ let href = userInfo?["href"] as? String
+ coordinateToStatusMentionProfileScene(for: .primary, provider: provider, cell: cell, mention: text, href: href)
default:
break
}
@@ -153,7 +157,10 @@ extension StatusProviderFacade {
static func responseToStatusMetaTextAction(provider: StatusProvider, cell: UITableViewCell, metaText: MetaText, didSelectMeta meta: Meta) {
switch meta {
- case .url(_, _, let url, _):
+ case .url(_, _, let url, _),
+ .mention(_, let url, _) where url.lowercased().hasPrefix("http"):
+ // note:
+ // some server mark the normal url as "u-url" class. highlighted content is a URL
guard let url = URL(string: url) else { return }
if let domain = provider.context.authenticationService.activeMastodonAuthenticationBox.value?.domain, url.host == domain,
url.pathComponents.count >= 4,
@@ -169,8 +176,9 @@ extension StatusProviderFacade {
case .hashtag(_, let hashtag, _):
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: provider.context, hashtag: hashtag)
provider.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: provider, transition: .show)
- case .mention(_, let mention, _):
- coordinateToStatusMentionProfileScene(for: .primary, provider: provider, cell: cell, mention: mention)
+ case .mention(_, let mention, let userInfo):
+ let href = userInfo?["href"] as? String
+ coordinateToStatusMentionProfileScene(for: .primary, provider: provider, cell: cell, mention: mention, href: href)
default:
break
}
@@ -208,17 +216,17 @@ extension StatusProviderFacade {
}
#endif
- private static func coordinateToStatusMentionProfileScene(for target: Target, provider: StatusProvider, cell: UITableViewCell, mention: String) {
+ private static func coordinateToStatusMentionProfileScene(for target: Target, provider: StatusProvider, cell: UITableViewCell, mention: String, href: String?) {
provider.status(for: cell, indexPath: nil)
.sink { [weak provider] status in
guard let provider = provider else { return }
guard let status = status else { return }
- coordinateToStatusMentionProfileScene(for: target, provider: provider, status: status, mention: mention)
+ coordinateToStatusMentionProfileScene(for: target, provider: provider, status: status, mention: mention, href: href)
}
.store(in: &provider.disposeBag)
}
- private static func coordinateToStatusMentionProfileScene(for target: Target, provider: StatusProvider, status: Status, mention: String) {
+ private static func coordinateToStatusMentionProfileScene(for target: Target, provider: StatusProvider, status: Status, mention: String, href: String?) {
guard let activeMastodonAuthenticationBox = provider.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
let domain = activeMastodonAuthenticationBox.domain
@@ -230,7 +238,13 @@ extension StatusProviderFacade {
}()
// cannot continue without meta
- guard let mentionMeta = (status.mentions ?? Set()).first(where: { $0.username == mention }) else { return }
+ guard let mentionMeta = (status.mentions ?? Set()).first(where: { $0.username == mention }) else {
+ // present web page if possible
+ if let url = href.flatMap({ URL(string: $0) }) {
+ provider.coordinator.present(scene: .safari(url: url), from: provider, transition: .safariPresent(animated: true, completion: nil))
+ }
+ return
+ }
let userID = mentionMeta.id
diff --git a/Mastodon/Resources/ar.lproj/Localizable.strings b/Mastodon/Resources/ar.lproj/Localizable.strings
index f6211e838..82663ba28 100644
--- a/Mastodon/Resources/ar.lproj/Localizable.strings
+++ b/Mastodon/Resources/ar.lproj/Localizable.strings
@@ -317,8 +317,8 @@ 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.AvatarAnimation.Title" = "Disable avatar animation";
-"Scene.Settings.Section.AppearanceSettings.DarkMode.Title" = "True black Dark Mode";
+"Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable avatar animation";
+"Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black Dark Mode";
"Scene.Settings.Section.Boringzone.Privacy" = "Privacy Policy";
"Scene.Settings.Section.Boringzone.Terms" = "Terms of Service";
"Scene.Settings.Section.Boringzone.Title" = "The Boring zone";
@@ -332,6 +332,8 @@ 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.Title" = "Preference";
+"Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Using default browser open link";
"Scene.Settings.Section.Spicyzone.Clear" = "Clear Media Cache";
"Scene.Settings.Section.Spicyzone.Signout" = "Sign Out";
"Scene.Settings.Section.Spicyzone.Title" = "The spicy zone";
diff --git a/Mastodon/Resources/en.lproj/Localizable.strings b/Mastodon/Resources/en.lproj/Localizable.strings
index f6211e838..82663ba28 100644
--- a/Mastodon/Resources/en.lproj/Localizable.strings
+++ b/Mastodon/Resources/en.lproj/Localizable.strings
@@ -317,8 +317,8 @@ 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.AvatarAnimation.Title" = "Disable avatar animation";
-"Scene.Settings.Section.AppearanceSettings.DarkMode.Title" = "True black Dark Mode";
+"Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable avatar animation";
+"Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black Dark Mode";
"Scene.Settings.Section.Boringzone.Privacy" = "Privacy Policy";
"Scene.Settings.Section.Boringzone.Terms" = "Terms of Service";
"Scene.Settings.Section.Boringzone.Title" = "The Boring zone";
@@ -332,6 +332,8 @@ 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.Title" = "Preference";
+"Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Using default browser open link";
"Scene.Settings.Section.Spicyzone.Clear" = "Clear Media Cache";
"Scene.Settings.Section.Spicyzone.Signout" = "Sign Out";
"Scene.Settings.Section.Spicyzone.Title" = "The spicy zone";
diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift
index 6a08bd45d..aa2ff0cf9 100644
--- a/Mastodon/Scene/Settings/SettingsViewController.swift
+++ b/Mastodon/Scene/Settings/SettingsViewController.swift
@@ -364,6 +364,9 @@ extension SettingsViewController: UITableViewDelegate {
case .notification:
// do nothing
break
+ case .preferenceUsingDefaultBrowser:
+ // do nothing
+ break
case .boringZone(let link), .spicyZone(let link):
switch link {
case .termsOfService, .privacyPolicy:
@@ -501,7 +504,24 @@ extension SettingsViewController: SettingsToggleCellDelegate {
// do nothing
}
.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
+ case .failure(let error):
+ assertionFailure(error.localizedDescription)
+ break
+ }
+ }
+ .store(in: &disposeBag)
default:
+ assertionFailure()
break
}
}
diff --git a/Mastodon/Scene/Settings/SettingsViewModel.swift b/Mastodon/Scene/Settings/SettingsViewModel.swift
index 142de7dcf..5d60f1669 100644
--- a/Mastodon/Scene/Settings/SettingsViewModel.swift
+++ b/Mastodon/Scene/Settings/SettingsViewModel.swift
@@ -102,13 +102,18 @@ extension SettingsViewModel {
]
snapshot.appendSections([.appearanceSettings])
snapshot.appendItems(appearanceSettingItems, toSection: .appearanceSettings)
-
+
+ // notification
let notificationItems = SettingsItem.NotificationSwitchMode.allCases.map { mode in
SettingsItem.notification(settingObjectID: setting.objectID, switchMode: mode)
}
snapshot.appendSections([.notifications])
snapshot.appendItems(notificationItems, toSection: .notifications)
+ // preference
+ snapshot.appendSections([.preference])
+ snapshot.appendItems([.preferenceUsingDefaultBrowser(settingObjectID: setting.objectID)], toSection: .preference)
+
// boring zone
let boringZoneSettingsItems: [SettingsItem] = {
let links: [SettingsItem.Link] = [
@@ -170,7 +175,8 @@ extension SettingsViewModel {
cell.delegate = settingsAppearanceTableViewCellDelegate
return cell
case .appearanceDarkMode(let objectID),
- .appearanceDisableAvatarAnimation(let objectID):
+ .appearanceDisableAvatarAnimation(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 {
@@ -231,11 +237,14 @@ extension SettingsViewModel {
) {
switch item {
case .appearanceDarkMode:
- cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.DarkMode.title
+ cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.trueBlackDarkMode
cell.switchButton.isOn = setting.preferredTrueBlackDarkMode
case .appearanceDisableAvatarAnimation:
- cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.AvatarAnimation.title
+ 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()
}
diff --git a/Mastodon/Scene/Share/View/Decoration/SawToothView.swift b/Mastodon/Scene/Share/View/Decoration/SawToothView.swift
index 94dab47fa..e344b62ef 100644
--- a/Mastodon/Scene/Share/View/Decoration/SawToothView.swift
+++ b/Mastodon/Scene/Share/View/Decoration/SawToothView.swift
@@ -52,7 +52,7 @@ final class SawToothView: UIView {
}
bezierPath.addLine(to: CGPoint(x: 0, y: bottomY))
bezierPath.close()
- ThemeService.shared.currentTheme.value.systemBackgroundColor.setFill()
+ ThemeService.shared.currentTheme.value.tableViewCellBackgroundColor.setFill()
bezierPath.fill()
bezierPath.lineWidth = 0
bezierPath.stroke()
diff --git a/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift
index b18314df2..8c329d31e 100644
--- a/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift
+++ b/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift
@@ -86,7 +86,7 @@ class TimelineLoaderTableViewCell: UITableViewCell {
loadMoreButton.heightAnchor.constraint(equalToConstant: TimelineLoaderTableViewCell.buttonHeight).priority(.required - 1),
])
- // use stack view to alignlment content center
+ // use stack view to alignment content center
stackView.spacing = 4
stackView.axis = .horizontal
stackView.alignment = .center
@@ -127,7 +127,7 @@ class TimelineLoaderTableViewCell: UITableViewCell {
}
private func setupBackgroundColor(theme: Theme) {
- loadMoreButton.backgroundColor = theme.systemBackgroundColor
+ loadMoreButton.backgroundColor = theme.tableViewCellBackgroundColor
}
}
diff --git a/Mastodon/Scene/Transition/MediaPreview/MediaPreviewableViewController.swift b/Mastodon/Scene/Transition/MediaPreview/MediaPreviewableViewController.swift
index 63cf10c3e..1fedf0d40 100644
--- a/Mastodon/Scene/Transition/MediaPreview/MediaPreviewableViewController.swift
+++ b/Mastodon/Scene/Transition/MediaPreview/MediaPreviewableViewController.swift
@@ -18,9 +18,9 @@ extension MediaPreviewableViewController {
case .mosaic(let mosaicImageViewContainer):
guard index < mosaicImageViewContainer.imageViews.count else { return nil }
let imageView = mosaicImageViewContainer.imageViews[index]
- return imageView.superview!.convert(imageView.frame, to: nil)
+ return imageView.superview?.convert(imageView.frame, to: nil)
case .profileAvatar(let profileHeaderView):
- return profileHeaderView.avatarImageView.superview!.convert(profileHeaderView.avatarImageView.frame, to: nil)
+ return profileHeaderView.avatarImageView.superview?.convert(profileHeaderView.avatarImageView.frame, to: nil)
case .profileBanner:
return nil // fallback to snapshot.frame
}
diff --git a/Mastodon/Service/AuthenticationService.swift b/Mastodon/Service/AuthenticationService.swift
index 4a460b1d0..a0bbca57a 100644
--- a/Mastodon/Service/AuthenticationService.swift
+++ b/Mastodon/Service/AuthenticationService.swift
@@ -81,26 +81,6 @@ final class AuthenticationService: NSObject {
.assign(to: \.value, on: activeMastodonAuthenticationBox)
.store(in: &disposeBag)
- activeMastodonAuthenticationBox
- .receive(on: RunLoop.main)
- .sink { [weak self] authenticationBox in
- guard let _ = self else { return }
- guard let authenticationBox = authenticationBox else { return }
- let request = Setting.sortedFetchRequest
- request.predicate = Setting.predicate(domain: authenticationBox.domain, userID: authenticationBox.userID)
- guard let setting = managedObjectContext.safeFetch(request).first else { return }
-
- let themeName: ThemeName = setting.preferredTrueBlackDarkMode ? .system : .mastodon
- if UserDefaults.shared.currentThemeNameRawValue != themeName.rawValue {
- ThemeService.shared.set(themeName: themeName)
- os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: update theme style", ((#file as NSString).lastPathComponent), #line, #function)
- }
- if UserDefaults.shared.preferredStaticAvatar != setting.preferredStaticAvatar {
- UserDefaults.shared.preferredStaticAvatar = setting.preferredStaticAvatar
- }
- }
- .store(in: &disposeBag)
-
do {
try mastodonAuthenticationFetchedResultsController.performFetch()
mastodonAuthentications.value = mastodonAuthenticationFetchedResultsController.fetchedObjects ?? []
diff --git a/Mastodon/Service/SettingService.swift b/Mastodon/Service/SettingService.swift
index 7a25d6c7b..7da8c3683 100644
--- a/Mastodon/Service/SettingService.swift
+++ b/Mastodon/Service/SettingService.swift
@@ -91,20 +91,16 @@ final class SettingService {
self.currentSettingUpdateSubscription = nil
return
}
-
+
+ SettingService.updatePreference(setting: setting)
self.currentSettingUpdateSubscription = ManagedObjectObserver.observe(object: setting)
.sink(receiveCompletion: { _ in
// do nothing
}, receiveValue: { change in
guard case .update(let object) = change.changeType,
let setting = object as? Setting else { return }
-
- // observe apparance mode
- switch setting.appearance {
- case .automatic: UserDefaults.shared.customUserInterfaceStyle = .unspecified
- case .light: UserDefaults.shared.customUserInterfaceStyle = .light
- case .dark: UserDefaults.shared.customUserInterfaceStyle = .dark
- }
+
+ SettingService.updatePreference(setting: setting)
})
}
.store(in: &disposeBag)
@@ -187,3 +183,37 @@ extension SettingService {
}
}
+
+extension SettingService {
+
+ static func updatePreference(setting: Setting) {
+ // set appearance
+ let userInterfaceStyle: UIUserInterfaceStyle = {
+ switch setting.appearance {
+ case .automatic: return .unspecified
+ case .light: return .light
+ case .dark: return .dark
+ }
+ }()
+ if UserDefaults.shared.customUserInterfaceStyle != userInterfaceStyle {
+ UserDefaults.shared.customUserInterfaceStyle = userInterfaceStyle
+ }
+
+ // set theme
+ let themeName: ThemeName = setting.preferredTrueBlackDarkMode ? .system : .mastodon
+ if UserDefaults.shared.currentThemeNameRawValue != themeName.rawValue {
+ ThemeService.shared.set(themeName: themeName)
+ os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: update theme style", ((#file as NSString).lastPathComponent), #line, #function)
+ }
+
+ // set avatar mode
+ if UserDefaults.shared.preferredStaticAvatar != setting.preferredStaticAvatar {
+ UserDefaults.shared.preferredStaticAvatar = setting.preferredStaticAvatar
+ }
+
+ // set browser
+ if UserDefaults.shared.preferredUsingDefaultBrowser != setting.preferredUsingDefaultBrowser {
+ UserDefaults.shared.preferredUsingDefaultBrowser = setting.preferredUsingDefaultBrowser
+ }
+ }
+}