2021-04-08 13:47:31 +02:00
//
// S e t t i n g s V i e w C o n t r o l l e r . s w i f t
// M a s t o d o n
//
// C r e a t e d b y i h u g o o n 2 0 2 1 / 4 / 7 .
//
import os . log
import UIKit
import Combine
import CoreData
import CoreDataStack
2021-04-26 10:57:50 +02:00
import MastodonSDK
2021-07-23 13:10:27 +02:00
import MetaTextKit
import MastodonMeta
2021-07-13 11:39:38 +02:00
import AuthenticationServices
2021-04-08 13:47:31 +02:00
class SettingsViewController : UIViewController , NeedsDependency {
weak var context : AppContext ! { willSet { precondition ( ! isViewLoaded ) } }
weak var coordinator : SceneCoordinator ! { willSet { precondition ( ! isViewLoaded ) } }
var viewModel : SettingsViewModel ! { willSet { precondition ( ! isViewLoaded ) } }
var disposeBag = Set < AnyCancellable > ( )
2021-04-26 10:57:50 +02:00
var notificationPolicySubscription : AnyCancellable ?
2021-04-08 13:47:31 +02:00
var triggerMenu : UIMenu {
let anyone = L10n . Scene . Settings . Section . Notifications . Trigger . anyone
let follower = L10n . Scene . Settings . Section . Notifications . Trigger . follower
let follow = L10n . Scene . Settings . Section . Notifications . Trigger . follow
2021-04-13 10:22:41 +02:00
let noOne = L10n . Scene . Settings . Section . Notifications . Trigger . noone
2021-04-08 13:47:31 +02:00
let menu = UIMenu (
2021-04-12 15:42:43 +02:00
image : nil ,
2021-04-08 13:47:31 +02:00
identifier : nil ,
options : . displayInline ,
children : [
UIAction ( title : anyone , image : UIImage ( systemName : " person.3 " ) , attributes : [ ] ) { [ weak self ] action in
2021-04-26 10:57:50 +02:00
self ? . updateTrigger ( policy : . all )
2021-04-08 13:47:31 +02:00
} ,
UIAction ( title : follower , image : UIImage ( systemName : " person.crop.circle.badge.plus " ) , attributes : [ ] ) { [ weak self ] action in
2021-04-26 10:57:50 +02:00
self ? . updateTrigger ( policy : . follower )
2021-04-08 13:47:31 +02:00
} ,
UIAction ( title : follow , image : UIImage ( systemName : " person.crop.circle.badge.checkmark " ) , attributes : [ ] ) { [ weak self ] action in
2021-04-26 10:57:50 +02:00
self ? . updateTrigger ( policy : . followed )
2021-04-08 13:47:31 +02:00
} ,
UIAction ( title : noOne , image : UIImage ( systemName : " nosign " ) , attributes : [ ] ) { [ weak self ] action in
2021-04-26 10:57:50 +02:00
self ? . updateTrigger ( policy : . none )
2021-04-08 13:47:31 +02:00
} ,
2021-04-17 20:02:08 +02:00
]
2021-04-08 13:47:31 +02:00
)
return menu
}
2021-05-10 12:48:04 +02:00
private let notifySectionHeaderStackView : UIStackView = {
2021-04-08 13:47:31 +02:00
let view = UIStackView ( )
view . translatesAutoresizingMaskIntoConstraints = false
view . isLayoutMarginsRelativeArrangement = true
view . axis = . horizontal
view . spacing = 4
2021-05-10 12:48:04 +02:00
return view
} ( )
2021-05-11 11:07:08 +02:00
let notifyLabel = UILabel ( )
2021-05-10 12:48:04 +02:00
private ( set ) lazy var notifySectionHeader : UIView = {
let view = notifySectionHeaderStackView
2021-08-06 11:45:07 +02:00
2021-04-08 13:47:31 +02:00
notifyLabel . translatesAutoresizingMaskIntoConstraints = false
2021-05-11 11:07:08 +02:00
notifyLabel . adjustsFontForContentSizeCategory = true
2021-05-10 12:48:04 +02:00
notifyLabel . font = UIFontMetrics ( forTextStyle : . headline ) . scaledFont ( for : UIFont . systemFont ( ofSize : 20 , weight : . semibold ) )
2021-04-08 13:47:31 +02:00
notifyLabel . textColor = Asset . Colors . Label . primary . color
notifyLabel . text = L10n . Scene . Settings . Section . Notifications . Trigger . title
2021-08-06 11:45:07 +02:00
notifyLabel . adjustsFontSizeToFitWidth = true
notifyLabel . minimumScaleFactor = 0.5
2021-04-08 13:47:31 +02:00
view . addArrangedSubview ( notifyLabel )
view . addArrangedSubview ( whoButton )
2021-05-11 11:07:08 +02:00
whoButton . setContentHuggingPriority ( . defaultHigh + 1 , for : . horizontal )
whoButton . setContentHuggingPriority ( . defaultHigh + 1 , for : . vertical )
2021-08-06 11:45:07 +02:00
2021-04-08 13:47:31 +02:00
return view
} ( )
2021-04-26 10:57:50 +02:00
private ( set ) lazy var whoButton : UIButton = {
2021-04-08 13:47:31 +02:00
let whoButton = UIButton ( type : . roundedRect )
whoButton . menu = triggerMenu
whoButton . showsMenuAsPrimaryAction = true
whoButton . setBackgroundColor ( Asset . Colors . battleshipGrey . color , for : . normal )
whoButton . setTitleColor ( Asset . Colors . Label . primary . color , for : . normal )
2021-05-11 11:07:08 +02:00
whoButton . titleLabel ? . adjustsFontForContentSizeCategory = true
2021-04-08 13:47:31 +02:00
whoButton . titleLabel ? . font = UIFontMetrics ( forTextStyle : . title3 ) . scaledFont ( for : UIFont . systemFont ( ofSize : 20 , weight : . semibold ) )
whoButton . contentEdgeInsets = UIEdgeInsets ( top : 5 , left : 5 , bottom : 5 , right : 5 )
whoButton . layer . cornerRadius = 10
whoButton . clipsToBounds = true
2021-08-06 11:45:07 +02:00
whoButton . titleLabel ? . adjustsFontSizeToFitWidth = true
whoButton . titleLabel ? . minimumScaleFactor = 0.5
2021-04-08 13:47:31 +02:00
return whoButton
} ( )
2021-04-26 10:57:50 +02:00
private ( set ) lazy var tableView : UITableView = {
2021-04-08 13:47:31 +02:00
// i n i t w i t h a f r a m e t o f i x a c o n f l i c t ( ' U I V i e w - E n c a p s u l a t e d - L a y o u t - W i d t h ' U I S t a c k V i e w : 0 x 7 f 8 c 2 b 6 c 0 5 9 0 . w i d t h = = 0 )
let tableView = UITableView ( frame : CGRect ( x : 0 , y : 0 , width : 320 , height : 320 ) , style : . grouped )
tableView . translatesAutoresizingMaskIntoConstraints = false
tableView . delegate = self
tableView . rowHeight = UITableView . automaticDimension
2021-06-08 11:26:12 +02:00
tableView . backgroundColor = . clear
2021-07-05 10:07:17 +02:00
tableView . separatorColor = ThemeService . shared . currentTheme . value . separator
2021-04-08 13:47:31 +02:00
2021-04-26 10:57:50 +02:00
tableView . register ( SettingsAppearanceTableViewCell . self , forCellReuseIdentifier : String ( describing : SettingsAppearanceTableViewCell . self ) )
tableView . register ( SettingsToggleTableViewCell . self , forCellReuseIdentifier : String ( describing : SettingsToggleTableViewCell . self ) )
tableView . register ( SettingsLinkTableViewCell . self , forCellReuseIdentifier : String ( describing : SettingsLinkTableViewCell . self ) )
2021-04-08 13:47:31 +02:00
return tableView
} ( )
2021-07-06 09:20:32 +02:00
2021-07-23 13:10:27 +02:00
let tableFooterLabel = MetaLabel ( style : . settingTableFooter )
2021-08-03 11:33:24 +02:00
lazy var tableFooterView : UIView = {
// i n i t w i t h a f r a m e t o f i x a c o n f l i c t ( ' U I V i e w - E n c a p s u l a t e d - L a y o u t - H e i g h t ' U I S t a c k V i e w : 0 x 7 f f e 4 1 e 4 7 d a 0 . h e i g h t = = 0 )
let view = UIStackView ( frame : CGRect ( x : 0 , y : 0 , width : 320 , height : 320 ) )
view . isLayoutMarginsRelativeArrangement = true
view . layoutMargins = UIEdgeInsets ( top : 20 , left : 20 , bottom : 20 , right : 20 )
view . axis = . vertical
view . alignment = . center
// t a b l e F o o t e r L a b e l . l i n k D e l e g a t e = s e l f
view . addArrangedSubview ( tableFooterLabel )
return view
} ( )
2021-04-08 13:47:31 +02:00
override func viewDidLoad ( ) {
super . viewDidLoad ( )
setupView ( )
bindViewModel ( )
viewModel . viewDidLoad . send ( )
}
2021-07-05 10:07:17 +02:00
override func viewWillAppear ( _ animated : Bool ) {
super . viewWillAppear ( animated )
// m a k e l a r g e t i t l e n o t c o l l a p s e d
navigationController ? . navigationBar . sizeToFit ( )
}
2021-04-08 13:47:31 +02:00
override func viewDidLayoutSubviews ( ) {
super . viewDidLayoutSubviews ( )
guard let footerView = self . tableView . tableFooterView else {
return
}
let width = self . tableView . bounds . size . width
let size = footerView . systemLayoutSizeFitting ( CGSize ( width : width , height : UIView . layoutFittingCompressedSize . height ) )
if footerView . frame . size . height != size . height {
footerView . frame . size . height = size . height
self . tableView . tableFooterView = footerView
}
}
2021-05-10 12:48:04 +02:00
override func traitCollectionDidChange ( _ previousTraitCollection : UITraitCollection ? ) {
super . traitCollectionDidChange ( previousTraitCollection )
updateSectionHeaderStackViewLayout ( )
}
2021-04-08 13:47:31 +02:00
// M A K R : - P r i v a t e m e t h o d s
2021-05-10 12:48:04 +02:00
private func updateSectionHeaderStackViewLayout ( ) {
2021-05-11 11:07:08 +02:00
// a c c e s s i b i l i t y
2021-05-10 12:48:04 +02:00
if traitCollection . preferredContentSizeCategory < . accessibilityMedium {
notifySectionHeaderStackView . axis = . horizontal
2021-05-11 11:07:08 +02:00
notifyLabel . numberOfLines = 1
2021-05-10 12:48:04 +02:00
} else {
notifySectionHeaderStackView . axis = . vertical
2021-05-11 11:07:08 +02:00
notifyLabel . numberOfLines = 0
2021-05-10 12:48:04 +02:00
}
}
2021-04-08 13:47:31 +02:00
private func bindViewModel ( ) {
2021-04-26 10:57:50 +02:00
self . whoButton . setTitle ( viewModel . setting . value . activeSubscription ? . policy . title , for : . normal )
viewModel . setting
. sink { [ weak self ] setting in
guard let self = self else { return }
self . notificationPolicySubscription = ManagedObjectObserver . observe ( object : setting )
. sink { _ in
// d o n o t h i n g
} receiveValue : { [ weak self ] change in
guard let self = self else { return }
guard case let . update ( object ) = change . changeType ,
let setting = object as ? Setting else { return }
if let activeSubscription = setting . activeSubscription {
self . whoButton . setTitle ( activeSubscription . policy . title , for : . normal )
} else {
assertionFailure ( )
}
}
}
. store ( in : & disposeBag )
2021-07-06 09:20:32 +02:00
2021-08-03 11:33:24 +02:00
let footer = " Mastodon v \( UIApplication . appVersion ( ) ) ( \( UIApplication . appBuild ( ) ) ) "
let metaContent = PlaintextMetaContent ( string : footer )
tableFooterLabel . configure ( content : metaContent )
// FIXME:
// n e e d s a w o r k a r o u n d f o r G i t H u b l i n k
// v i e w M o d e l . c u r r e n t I n s t a n c e
// . r e c e i v e ( o n : R u n L o o p . m a i n )
// . s i n k { [ w e a k s e l f ] i n s t a n c e i n
// g u a r d l e t s e l f = s e l f e l s e { r e t u r n }
// l e t v e r s i o n = i n s t a n c e ? . v e r s i o n ? ? " - "
// l e t l i n k = # " < a h r e f = " h t t p s : / / g i t h u b . c o m / m a s t o d o n / m a s t o d o n " > m a s t o d o n / m a s t o d o n < / a > " #
// l e t c o n t e n t = L 1 0 n . S c e n e . S e t t i n g s . F o o t e r . m a s t o d o n D e s c r i p t i o n ( l i n k , v e r s i o n )
// l e t m a s t o d o n C o n t e n t = M a s t o d o n C o n t e n t ( c o n t e n t : c o n t e n t , e m o j i s : [ : ] )
// d o {
// l e t m e t a C o n t e n t = t r y M a s t o d o n M e t a C o n t e n t . c o n v e r t ( d o c u m e n t : m a s t o d o n C o n t e n t )
// s e l f . t a b l e F o o t e r L a b e l . c o n f i g u r e ( c o n t e n t : m e t a C o n t e n t )
// } c a t c h {
// l e t m e t a C o n t e n t = P l a i n t e x t M e t a C o n t e n t ( s t r i n g : " " )
// s e l f . t a b l e F o o t e r L a b e l . c o n f i g u r e ( c o n t e n t : m e t a C o n t e n t )
// a s s e r t i o n F a i l u r e ( )
// }
// }
// . s t o r e ( i n : & d i s p o s e B a g )
2021-04-08 13:47:31 +02:00
}
private func setupView ( ) {
2021-07-06 12:00:39 +02:00
setupBackgroundColor ( theme : ThemeService . shared . currentTheme . value )
2021-07-05 10:07:17 +02:00
ThemeService . shared . currentTheme
. receive ( on : RunLoop . main )
. sink { [ weak self ] theme in
guard let self = self else { return }
2021-07-06 12:00:39 +02:00
self . setupBackgroundColor ( theme : theme )
2021-07-05 10:07:17 +02:00
}
. store ( in : & disposeBag )
2021-04-08 13:47:31 +02:00
setupNavigation ( )
view . addSubview ( tableView )
NSLayoutConstraint . activate ( [
tableView . topAnchor . constraint ( equalTo : view . topAnchor ) ,
tableView . leadingAnchor . constraint ( equalTo : view . leadingAnchor ) ,
tableView . trailingAnchor . constraint ( equalTo : view . trailingAnchor ) ,
tableView . bottomAnchor . constraint ( equalTo : view . bottomAnchor ) ,
] )
2021-04-26 10:57:50 +02:00
setupTableView ( )
2021-05-10 12:48:04 +02:00
updateSectionHeaderStackViewLayout ( )
2021-04-08 13:47:31 +02:00
}
2021-07-06 12:00:39 +02:00
private func setupBackgroundColor ( theme : Theme ) {
view . backgroundColor = UIColor ( dynamicProvider : { traitCollection in
switch traitCollection . userInterfaceLevel {
case . elevated where traitCollection . userInterfaceStyle = = . dark :
return theme . systemElevatedBackgroundColor
default :
return theme . secondarySystemBackgroundColor
}
} )
2021-07-07 11:52:06 +02:00
tableView . separatorColor = theme . separator
2021-07-06 12:00:39 +02:00
}
2021-04-08 13:47:31 +02:00
private func setupNavigation ( ) {
navigationController ? . navigationBar . prefersLargeTitles = true
navigationItem . rightBarButtonItem
= UIBarButtonItem ( barButtonSystemItem : UIBarButtonItem . SystemItem . done ,
target : self ,
action : #selector ( doneButtonDidClick ) )
navigationItem . title = L10n . Scene . Settings . title
}
private func setupTableView ( ) {
2021-04-26 10:57:50 +02:00
viewModel . setupDiffableDataSource (
for : tableView ,
settingsAppearanceTableViewCellDelegate : self ,
settingsToggleCellDelegate : self
)
2021-08-03 11:33:24 +02:00
tableView . tableFooterView = tableFooterView
2021-04-08 13:47:31 +02:00
}
2021-04-13 10:22:41 +02:00
func alertToSignout ( ) {
let alertController = UIAlertController (
title : L10n . Common . Alerts . SignOut . title ,
message : L10n . Common . Alerts . SignOut . message ,
preferredStyle : . alert
)
let cancelAction = UIAlertAction ( title : L10n . Common . Controls . Actions . cancel , style : . cancel , handler : nil )
let signOutAction = UIAlertAction ( title : L10n . Common . Alerts . SignOut . confirm , style : . destructive ) { [ weak self ] _ in
guard let self = self else { return }
2021-04-26 10:57:50 +02:00
self . signOut ( )
2021-04-13 10:22:41 +02:00
}
alertController . addAction ( cancelAction )
alertController . addAction ( signOutAction )
self . coordinator . present (
scene : . alertController ( alertController : alertController ) ,
from : self ,
transition : . alertController ( animated : true , completion : nil )
)
}
2021-04-26 10:57:50 +02:00
func signOut ( ) {
2021-04-08 13:47:31 +02:00
guard let activeMastodonAuthenticationBox = context . authenticationService . activeMastodonAuthenticationBox . value else {
return
}
2021-04-13 10:22:41 +02:00
2021-04-08 13:47:31 +02:00
context . authenticationService . signOutMastodonUser (
domain : activeMastodonAuthenticationBox . domain ,
userID : activeMastodonAuthenticationBox . userID
)
. receive ( on : DispatchQueue . main )
. sink { [ weak self ] result in
guard let self = self else { return }
switch result {
case . failure ( let error ) :
assertionFailure ( error . localizedDescription )
case . success ( let isSignOut ) :
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s: sign out %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , isSignOut ? " success " : " fail " )
guard isSignOut else { return }
self . coordinator . setup ( )
self . coordinator . setupOnboardingIfNeeds ( animated : true )
}
}
. store ( in : & disposeBag )
}
2021-04-12 15:42:43 +02:00
deinit {
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s: " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function )
}
2021-04-26 10:57:50 +02:00
}
// M a r k : - A c t i o n s
extension SettingsViewController {
@objc private func doneButtonDidClick ( ) {
2021-04-08 13:47:31 +02:00
dismiss ( animated : true , completion : nil )
}
}
extension SettingsViewController : UITableViewDelegate {
func tableView ( _ tableView : UITableView , viewForHeaderInSection section : Int ) -> UIView ? {
let sections = viewModel . dataSource . snapshot ( ) . sectionIdentifiers
guard section < sections . count else { return nil }
2021-04-26 10:57:50 +02:00
let sectionIdentifier = sections [ section ]
2021-04-08 13:47:31 +02:00
2021-04-25 06:48:29 +02:00
let header : SettingsSectionHeader
2021-04-26 10:57:50 +02:00
switch sectionIdentifier {
case . notifications :
2021-04-25 06:48:29 +02:00
header = SettingsSectionHeader (
2021-04-08 13:47:31 +02:00
frame : CGRect ( x : 0 , y : 0 , width : 375 , height : 66 ) ,
customView : notifySectionHeader )
2021-04-26 10:57:50 +02:00
header . update ( title : sectionIdentifier . title )
default :
2021-04-25 06:48:29 +02:00
header = SettingsSectionHeader ( frame : CGRect ( x : 0 , y : 0 , width : 375 , height : 66 ) )
2021-04-26 10:57:50 +02:00
header . update ( title : sectionIdentifier . title )
2021-04-08 13:47:31 +02:00
}
2021-04-25 06:48:29 +02:00
header . preservesSuperviewLayoutMargins = true
2021-04-26 10:57:50 +02:00
2021-04-25 06:48:29 +02:00
return header
2021-04-08 13:47:31 +02:00
}
2021-04-26 10:57:50 +02:00
2021-04-08 13:47:31 +02:00
// r e m o v e t h e g a p o f t a b l e ' s f o o t e r
func tableView ( _ tableView : UITableView , viewForFooterInSection section : Int ) -> UIView ? {
return UIView ( )
}
2021-04-26 10:57:50 +02:00
2021-04-08 13:47:31 +02:00
// r e m o v e t h e g a p o f t a b l e ' s f o o t e r
func tableView ( _ tableView : UITableView , heightForFooterInSection section : Int ) -> CGFloat {
2021-04-26 10:57:50 +02:00
return CGFloat . leastNonzeroMagnitude
2021-04-08 13:47:31 +02:00
}
2021-04-26 10:57:50 +02:00
2021-04-08 13:47:31 +02:00
func tableView ( _ tableView : UITableView , didSelectRowAt indexPath : IndexPath ) {
2021-04-26 10:57:50 +02:00
guard let dataSource = viewModel . dataSource else { return }
2021-06-01 08:07:44 +02:00
guard let item = dataSource . itemIdentifier ( for : indexPath ) else { return }
2021-04-26 10:57:50 +02:00
2021-04-14 10:41:30 +02:00
switch item {
2021-06-22 14:52:30 +02:00
case . appearance :
2021-07-23 14:03:54 +02:00
// d o n o t h i n g
break
2021-06-01 08:07:44 +02:00
case . notification :
// d o n o t h i n g
break
2021-07-22 07:47:56 +02:00
case . preference :
2021-07-08 09:56:52 +02:00
// d o n o t h i n g
break
2021-06-01 08:07:44 +02:00
case . boringZone ( let link ) , . spicyZone ( let link ) :
2021-07-23 14:03:54 +02:00
let feedbackGenerator = UIImpactFeedbackGenerator ( style : . light )
2021-07-23 13:40:41 +02:00
feedbackGenerator . impactOccurred ( )
2021-06-01 08:07:44 +02:00
switch link {
2021-07-13 11:39:38 +02:00
case . accountSettings :
guard let box = context . authenticationService . activeMastodonAuthenticationBox . value ,
let url = URL ( string : " https:// \( box . domain ) /auth/edit " ) else { return }
viewModel . openAuthenticationPage ( authenticateURL : url , presentationContextProvider : self )
2021-08-03 11:33:24 +02:00
case . github :
guard let url = URL ( string : " https://github.com/mastodon/mastodon-ios " ) else { break }
coordinator . present (
scene : . safari ( url : url ) ,
from : self ,
transition : . safariPresent ( animated : true , completion : nil )
)
2021-06-01 08:07:44 +02:00
case . termsOfService , . privacyPolicy :
// s a m e U R L
guard let url = viewModel . privacyURL else { break }
coordinator . present (
scene : . safari ( url : url ) ,
from : self ,
transition : . safariPresent ( animated : true , completion : nil )
)
case . clearMediaCache :
context . purgeCache ( )
. receive ( on : RunLoop . main )
. sink { [ weak self ] byteCount in
guard let self = self else { return }
2021-07-13 11:39:38 +02:00
let byteCountFormatted = AppContext . byteCountFormatter . string ( fromByteCount : Int64 ( byteCount ) )
2021-06-01 08:07:44 +02:00
let alertController = UIAlertController (
title : L10n . Common . Alerts . CleanCache . title ,
2021-07-13 11:39:38 +02:00
message : L10n . Common . Alerts . CleanCache . message ( byteCountFormatted ) ,
2021-06-01 08:07:44 +02:00
preferredStyle : . alert
)
let okAction = UIAlertAction ( title : L10n . Common . Controls . Actions . ok , style : . default , handler : nil )
alertController . addAction ( okAction )
self . coordinator . present ( scene : . alertController ( alertController : alertController ) , from : nil , transition : . alertController ( animated : true , completion : nil ) )
}
. store ( in : & disposeBag )
case . signOut :
2021-07-23 13:40:41 +02:00
feedbackGenerator . impactOccurred ( )
2021-04-14 10:41:30 +02:00
alertToSignout ( )
}
2021-04-08 13:47:31 +02:00
}
}
}
// U p d a t e s e t t i n g i n t o c o r e d a t a
extension SettingsViewController {
2021-04-26 10:57:50 +02:00
func updateTrigger ( policy : Mastodon . API . Subscriptions . Policy ) {
let objectID = self . viewModel . setting . value . objectID
let managedObjectContext = context . backgroundManagedObjectContext
2021-04-08 13:47:31 +02:00
2021-04-26 10:57:50 +02:00
managedObjectContext . performChanges {
let setting = managedObjectContext . object ( with : objectID ) as ! Setting
let ( subscription , _ ) = APIService . CoreData . createOrFetchSubscription (
into : managedObjectContext ,
setting : setting ,
policy : policy
)
let now = Date ( )
subscription . update ( activedAt : now )
setting . didUpdate ( at : now )
2021-04-08 13:47:31 +02:00
}
2021-04-26 10:57:50 +02:00
. sink { _ in
// d o n o t h i n g
} receiveValue : { _ in
// d o n o h t i n g
2021-04-08 13:47:31 +02:00
}
2021-04-26 10:57:50 +02:00
. store ( in : & disposeBag )
2021-04-08 13:47:31 +02:00
}
}
2021-04-26 10:57:50 +02:00
// MARK: - S e t t i n g s A p p e a r a n c e T a b l e V i e w C e l l D e l e g a t e
2021-04-08 13:47:31 +02:00
extension SettingsViewController : SettingsAppearanceTableViewCellDelegate {
2021-04-26 10:57:50 +02:00
func settingsAppearanceCell ( _ cell : SettingsAppearanceTableViewCell , didSelectAppearanceMode appearanceMode : SettingsItem . AppearanceMode ) {
guard let dataSource = viewModel . dataSource else { return }
guard let indexPath = tableView . indexPath ( for : cell ) else { return }
let item = dataSource . itemIdentifier ( for : indexPath )
2021-06-22 14:52:30 +02:00
guard case let . appearance ( settingObjectID ) = item else { return }
2021-04-26 10:57:50 +02:00
2021-04-08 13:47:31 +02:00
context . managedObjectContext . performChanges {
2021-04-26 10:57:50 +02:00
let setting = self . context . managedObjectContext . object ( with : settingObjectID ) as ! Setting
setting . update ( appearanceRaw : appearanceMode . rawValue )
2021-04-08 13:47:31 +02:00
}
2021-04-26 10:57:50 +02:00
. sink { _ in
2021-07-23 14:03:54 +02:00
let feedbackGenerator = UIImpactFeedbackGenerator ( style : . light )
feedbackGenerator . impactOccurred ( )
2021-04-08 13:47:31 +02:00
} . store ( in : & disposeBag )
}
}
extension SettingsViewController : SettingsToggleCellDelegate {
2021-04-26 10:57:50 +02:00
func settingsToggleCell ( _ cell : SettingsToggleTableViewCell , switchValueDidChange switch : UISwitch ) {
guard let dataSource = viewModel . dataSource else { return }
guard let indexPath = tableView . indexPath ( for : cell ) else { return }
2021-07-07 08:55:41 +02:00
let isOn = ` switch ` . isOn
2021-04-26 10:57:50 +02:00
let item = dataSource . itemIdentifier ( for : indexPath )
2021-07-07 08:55:41 +02:00
2021-04-26 10:57:50 +02:00
switch item {
2021-07-15 14:48:26 +02:00
case . notification ( let settingObjectID , let switchMode ) :
let managedObjectContext = context . backgroundManagedObjectContext
managedObjectContext . performChanges {
let setting = managedObjectContext . object ( with : settingObjectID ) as ! Setting
guard let subscription = setting . activeSubscription else { return }
let alert = subscription . alert
switch switchMode {
case . favorite : alert . update ( favourite : isOn )
case . follow : alert . update ( follow : isOn )
case . reblog : alert . update ( reblog : isOn )
case . mention : alert . update ( mention : isOn )
}
// t r i g g e r s e t t i n g u p d a t e
alert . subscription . setting ? . didUpdate ( at : Date ( ) )
}
. sink { _ in
// d o n o t h i n g
}
. store ( in : & disposeBag )
2021-07-22 07:47:56 +02:00
case . preference ( let settingObjectID , let preferenceType ) :
2021-07-07 08:55:41 +02:00
let managedObjectContext = context . backgroundManagedObjectContext
managedObjectContext . performChanges {
let setting = managedObjectContext . object ( with : settingObjectID ) as ! Setting
2021-07-22 07:47:56 +02:00
switch preferenceType {
case . darkMode :
setting . update ( preferredTrueBlackDarkMode : isOn )
case . disableAvatarAnimation :
setting . update ( preferredStaticAvatar : isOn )
2021-07-23 13:33:05 +02:00
case . disableEmojiAnimation :
setting . update ( preferredStaticEmoji : isOn )
2021-07-22 07:47:56 +02:00
case . useDefaultBrowser :
setting . update ( preferredUsingDefaultBrowser : isOn )
2021-07-07 08:55:41 +02:00
}
}
2021-07-08 09:56:52 +02:00
. sink { result in
switch result {
case . success :
2021-07-22 07:47:56 +02:00
switch preferenceType {
case . darkMode :
ThemeService . shared . set ( themeName : isOn ? . system : . mastodon )
case . disableAvatarAnimation :
UserDefaults . shared . preferredStaticAvatar = isOn
2021-07-23 13:33:05 +02:00
case . disableEmojiAnimation :
UserDefaults . shared . preferredStaticEmoji = isOn
2021-07-22 07:47:56 +02:00
case . useDefaultBrowser :
UserDefaults . shared . preferredUsingDefaultBrowser = isOn
}
2021-07-08 09:56:52 +02:00
case . failure ( let error ) :
assertionFailure ( error . localizedDescription )
break
}
}
. store ( in : & disposeBag )
2021-04-26 10:57:50 +02:00
default :
2021-07-08 09:56:52 +02:00
assertionFailure ( )
2021-04-26 10:57:50 +02:00
break
}
2021-04-08 13:47:31 +02:00
}
}
2021-07-23 13:10:27 +02:00
// MARK: - M e t a L a b e l D e l e g a t e
extension SettingsViewController : MetaLabelDelegate {
func metaLabel ( _ metaLabel : MetaLabel , didSelectMeta meta : Meta ) {
switch meta {
case . url ( _ , _ , let url , _ ) :
guard let url = URL ( string : url ) else { return }
coordinator . present ( scene : . safari ( url : url ) , from : self , transition : . safariPresent ( animated : true , completion : nil ) )
default :
assertionFailure ( )
}
2021-04-08 13:47:31 +02:00
}
}
2021-07-13 11:39:38 +02:00
// MARK: - A S A u t h o r i z a t i o n C o n t r o l l e r P r e s e n t a t i o n C o n t e x t P r o v i d i n g
extension SettingsViewController : ASWebAuthenticationPresentationContextProviding {
func presentationAnchor ( for session : ASWebAuthenticationSession ) -> ASPresentationAnchor {
return view . window !
}
}
2021-05-19 10:45:01 +02:00
// MARK: - U I A d a p t i v e P r e s e n t a t i o n C o n t r o l l e r D e l e g a t e
extension SettingsViewController : UIAdaptivePresentationControllerDelegate {
func adaptivePresentationStyle ( for controller : UIPresentationController , traitCollection : UITraitCollection ) -> UIModalPresentationStyle {
return . formSheet
}
}
extension SettingsViewController {
var closeKeyCommand : UIKeyCommand {
UIKeyCommand (
title : L10n . Scene . Settings . Keyboard . closeSettingsWindow ,
image : nil ,
action : #selector ( SettingsViewController . closeSettingsWindowKeyCommandHandler ( _ : ) ) ,
input : " w " ,
modifierFlags : . command ,
propertyList : nil ,
alternates : [ ] ,
discoverabilityTitle : nil ,
attributes : [ ] ,
state : . off
)
}
override var keyCommands : [ UIKeyCommand ] ? {
return [ closeKeyCommand ]
}
@objc private func closeSettingsWindowKeyCommandHandler ( _ sender : UIKeyCommand ) {
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function )
dismiss ( animated : true , completion : nil )
}
}
2021-04-08 13:47:31 +02:00
#if canImport ( SwiftUI ) && DEBUG
import SwiftUI
struct SettingsViewController_Previews : PreviewProvider {
static var previews : some View {
Group {
UIViewControllerPreview { ( ) -> UIViewController in
return SettingsViewController ( )
}
. previewLayout ( . fixed ( width : 390 , height : 844 ) )
}
}
}
#endif