// // AppearancePrefsView.swift // Tusker // // Created by Shadowfacts on 6/13/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import SwiftUI import Combine import TuskerPreferences struct AppearancePrefsView: View { @ObservedObject private var preferences = Preferences.shared @Environment(\.colorScheme) private var colorScheme private var appearanceChangePublisher: some Publisher { preferences.$theme .map { _ in () } .merge(with: preferences.$pureBlackDarkMode.map { _ in () }, preferences.$accentColor.map { _ in () } ) // the prefrence publishers are all willSet, but want to notify after the change, so wait one runloop iteration .receive(on: DispatchQueue.main) } private static let accentColorsAndImages: [(AccentColor, UIImage?)] = AccentColor.allCases.map { color in var image: UIImage? if let color = color.color { if #available(iOS 16.0, *) { image = UIImage(systemName: "circle.fill")!.withTintColor(color, renderingMode: .alwaysTemplate).withRenderingMode(.alwaysOriginal) } else { image = UIGraphicsImageRenderer(size: CGSize(width: 20, height: 20)).image { context in color.setFill() context.cgContext.fillEllipse(in: CGRect(x: 0, y: 0, width: 20, height: 20)) } } } return (color, image) } var body: some View { List { themeSection interfaceSection Section("Post Preview") { MockStatusView() .padding(.top, 8) .padding(.horizontal, UIDevice.current.userInterfaceIdiom == .pad ? 8 : 4) } .listRowBackground(preferences.pureBlackDarkMode ? colorScheme == .dark ? Color.black : Color.white : Color.appBackground) accountsSection postsSection mediaSection } .listStyle(.insetGrouped) .appGroupedListBackground(container: PreferencesNavigationController.self) .navigationTitle("Appearance") } private var themeSection: some View { Section { #if !os(visionOS) Picker(selection: $preferences.theme, label: Text("Theme")) { Text("Use System Theme").tag(Theme.unspecified) Text("Light").tag(Theme.light) Text("Dark").tag(Theme.dark) } // macOS system dark mode isn't pure black, so this isn't necessary if !ProcessInfo.processInfo.isMacCatalystApp && !ProcessInfo.processInfo.isiOSAppOnMac { Toggle(isOn: $preferences.pureBlackDarkMode) { Text("Pure Black Dark Mode") } } #endif Picker(selection: $preferences.accentColor, label: Text("Accent Color")) { ForEach(Self.accentColorsAndImages, id: \.0.rawValue) { (color, image) in HStack { Text(color.name) if let image { Spacer() Image(uiImage: image) } } .tag(color) } } } .onReceive(appearanceChangePublisher) { _ in NotificationCenter.default.post(name: .themePreferenceChanged, object: nil) } .appGroupedListRowBackground() } @ViewBuilder private var interfaceSection: some View { let visionIdiom = UIUserInterfaceIdiom(rawValue: 6) if [visionIdiom, .pad, .mac].contains(UIDevice.current.userInterfaceIdiom) { Section(header: Text("Interface")) { WidescreenNavigationPrefsView() } .appGroupedListRowBackground() } } private var accountsSection: some View { Section("Accounts") { Toggle(isOn: Binding(get: { preferences.avatarStyle == .circle }, set: { preferences.avatarStyle = $0 ? .circle : .roundRect })) { Text("Use Circular Avatars") } Toggle(isOn: $preferences.hideCustomEmojiInUsernames) { Text("Hide Custom Emoji in Usernames") } } .appGroupedListRowBackground() } private var postsSection: some View { Section("Posts") { Toggle(isOn: $preferences.showIsStatusReplyIcon) { Text("Show Status Reply Icons") } Toggle(isOn: $preferences.alwaysShowStatusVisibilityIcon) { Text("Always Show Status Visibility Icons") } Toggle(isOn: $preferences.showLinkPreviews) { Text("Show Link Previews") } Toggle(isOn: $preferences.showAttachmentsInTimeline) { Text("Show Attachments on Timeline") } Toggle(isOn: $preferences.hideActionsInTimeline) { Text("Hide Actions on Timeline") } Toggle(isOn: $preferences.underlineTextLinks) { Text("Underline Links") } NavigationLink("Leading Swipe Actions") { SwipeActionsPrefsView(selection: $preferences.leadingStatusSwipeActions) .edgesIgnoringSafeArea(.all) .navigationTitle("Leading Swipe Actions") } NavigationLink("Trailing Swipe Actions") { SwipeActionsPrefsView(selection: $preferences.trailingStatusSwipeActions) .edgesIgnoringSafeArea(.all) .navigationTitle("Trailing Swipe Actions") } } .appGroupedListRowBackground() } private var mediaSection: some View { Section("Media") { Picker(selection: $preferences.attachmentBlurMode) { ForEach(AttachmentBlurMode.allCases, id: \.self) { mode in Text(mode.displayName).tag(mode) } } label: { Text("Blur Media") } Toggle(isOn: $preferences.blurMediaBehindContentWarning) { Text("Blur Media Behind Content Warning") } .disabled(preferences.attachmentBlurMode != .useStatusSetting) Toggle(isOn: $preferences.automaticallyPlayGifs) { Text("Automatically Play GIFs") } Toggle(isOn: $preferences.showUncroppedMediaInline) { Text("Show Uncropped Media Inline") } Toggle(isOn: $preferences.showAttachmentBadges) { Text("Show GIF/\(Text("Alt").font(.body.lowercaseSmallCaps())) Badges") } Toggle(isOn: $preferences.attachmentAltBadgeInverted) { Text("Show Badge when Missing \(Text("Alt").font(.body.lowercaseSmallCaps()))") } .disabled(!preferences.showAttachmentBadges) } .appGroupedListRowBackground() } } #if DEBUG struct AppearancePrefsView_Previews : PreviewProvider { static var previews: some View { AppearancePrefsView() } } #endif