More preferences reorganizing

This commit is contained in:
Shadowfacts 2024-04-14 23:36:35 -04:00
parent c7a56a9f61
commit 4665df228d
7 changed files with 143 additions and 124 deletions

View File

@ -169,7 +169,6 @@
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */; };
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */; };
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */; };
D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68015412401A74600D6103B /* MediaPrefsView.swift */; };
D681E4D7246E32290053414F /* StatusActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D6246E32290053414F /* StatusActivityItemSource.swift */; };
D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */; };
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */; };
@ -592,7 +591,6 @@
D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeAccountDetailView.swift; sourceTree = "<group>"; };
D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Uniques.swift"; sourceTree = "<group>"; };
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposingPrefsView.swift; sourceTree = "<group>"; };
D68015412401A74600D6103B /* MediaPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPrefsView.swift; sourceTree = "<group>"; };
D681E4D6246E32290053414F /* StatusActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActivityItemSource.swift; sourceTree = "<group>"; };
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivityItemSource.swift; sourceTree = "<group>"; };
D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeDrawingViewController.swift; sourceTree = "<group>"; };
@ -1172,12 +1170,9 @@
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */,
04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
D64B96802BC3279D002C8990 /* PrefsAccountView.swift */,
D61F75892932E1FC00C0B37F /* SwipeActionsPrefsView.swift */,
D6958F3C2AA383D90062FE52 /* WidescreenNavigationPrefsView.swift */,
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */,
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */,
D68015412401A74600D6103B /* MediaPrefsView.swift */,
D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */,
0427033922B31269000D31B6 /* AdvancedPrefsView.swift */,
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */,
@ -1492,6 +1487,8 @@
children = (
D6C4532C2BCB86AC00E26A0E /* AppearancePrefsView.swift */,
D6C4532E2BCB873400E26A0E /* MockStatusView.swift */,
D6958F3C2AA383D90062FE52 /* WidescreenNavigationPrefsView.swift */,
D61F75892932E1FC00C0B37F /* SwipeActionsPrefsView.swift */,
);
path = Appearance;
sourceTree = "<group>";
@ -2348,7 +2345,6 @@
D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */,
D6CF5B892AC9BA6E00F15D83 /* MastodonSearchController.swift in Sources */,
D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */,
D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */,
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */,
D6E77D09286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift in Sources */,
D6D79F292A0D596B00AB2315 /* StatusEditHistoryViewController.swift in Sources */,

View File

@ -7,18 +7,49 @@
//
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<Void, Never> {
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 {
Section {
themeSection
interfaceSection
Section("Post Preview") {
MockStatusView()
.padding(.top, 8)
.padding(.horizontal, UIDevice.current.userInterfaceIdiom == .pad ? 8 : 4)
}
.appGroupedListRowBackground()
.listRowBackground(preferences.pureBlackDarkMode ? colorScheme == .dark ? Color.black : Color.white : Color.appBackground)
accountsSection
postsSection
@ -29,6 +60,53 @@ struct AppearancePrefsView: View {
.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: {
@ -65,15 +143,16 @@ struct AppearancePrefsView: View {
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")
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()
}

View File

@ -161,7 +161,7 @@ private actor MockAttachmentsGenerator {
return attachmentURLs
}
let size = CGSize(width: 100, height: 100)
let size = CGSize(width: 200, height: 200)
let bounds = CGRect(origin: .zero, size: size)
let format = UIGraphicsImageRendererFormat()
format.scale = displayScale
@ -171,24 +171,24 @@ private actor MockAttachmentsGenerator {
UIColor(red: 0x56 / 255, green: 0x03 / 255, blue: 0xad / 255, alpha: 1).setFill()
ctx.fill(bounds)
ctx.cgContext.concatenate(CGAffineTransform(1, 0, -0.5, 1, 0, 0))
for minX in stride(from: 0, through: 100, by: 30) {
for x in 0..<9 {
UIColor(red: 0x83 / 255, green: 0x67 / 255, blue: 0xc7 / 255, alpha: 1).setFill()
ctx.fill(CGRect(x: minX + 20, y: 0, width: 15, height: 100))
ctx.fill(CGRect(x: CGFloat(x) * 30 + 20, y: 0, width: 15, height: bounds.height))
}
}
let secondImage = renderer.image { ctx in
UIColor(red: 0x00 / 255, green: 0x43 / 255, blue: 0x85 / 255, alpha: 1).setFill()
ctx.fill(bounds)
UIColor(red: 0x05 / 255, green: 0xb2 / 255, blue: 0xdc / 255, alpha: 1).setFill()
for y in 0..<2 {
for x in 0..<4 {
for y in 0..<4 {
for x in 0..<5 {
let rect = CGRect(x: x * 45 - 5, y: y * 50 + 15, width: 20, height: 20)
ctx.cgContext.fillEllipse(in: rect)
}
}
UIColor(red: 0x08 / 255, green: 0x7c / 255, blue: 0xa7 / 255, alpha: 1).setFill()
for y in 0..<3 {
for x in 0..<2 {
for y in 0..<5 {
for x in 0..<4 {
let rect = CGRect(x: CGFloat(x) * 45 + 22.5, y: CGFloat(y) * 50 - 5, width: 10, height: 10)
ctx.cgContext.fillEllipse(in: rect)
}

View File

@ -12,26 +12,32 @@ import TuskerPreferences
struct WidescreenNavigationPrefsView: View {
@ObservedObject private var preferences = Preferences.shared
@State private var startAnimation = PassthroughSubject<Void, Never>()
@State private var startAnimation = CurrentValueSubject<Bool, Never>(false)
private var startAnimationSignal: some Publisher<Void, Never> {
startAnimation.filter { $0 }.removeDuplicates().map { _ in () }
}
var body: some View {
HStack {
Spacer()
OptionView<StackNavigationPreview>(
OptionView(
content: StackNavigationPreview.self,
value: .stack,
selection: $preferences.widescreenNavigationMode,
startAnimation: startAnimation
startAnimation: startAnimationSignal
) {
Text("Stack")
}
Spacer(minLength: 32)
OptionView<SplitNavigationPreview>(
OptionView(
content: SplitNavigationPreview.self,
value: .splitScreen,
selection: $preferences.widescreenNavigationMode,
startAnimation: startAnimation
startAnimation: startAnimationSignal
) {
Text("Split Screen")
}
@ -39,10 +45,11 @@ struct WidescreenNavigationPrefsView: View {
if preferences.hasFeatureFlag(.iPadMultiColumn) {
Spacer(minLength: 32)
OptionView<MultiColumnNavigationPreview>(
OptionView(
content: MultiColumnNavigationPreview.self,
value: .multiColumn,
selection: $preferences.widescreenNavigationMode,
startAnimation: startAnimation
startAnimation: startAnimationSignal
) {
Text("Multi-Column")
}
@ -53,19 +60,26 @@ struct WidescreenNavigationPrefsView: View {
.frame(height: 100)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
startAnimation.send()
startAnimation.send(true)
}
}
}
}
private struct OptionView<Content: NavigationModePreview>: View {
private struct OptionView<Content: NavigationModePreview, P: Publisher<Void, Never>>: View {
let value: WidescreenNavigationMode
@Binding var selection: WidescreenNavigationMode
let startAnimation: PassthroughSubject<Void, Never>
@ViewBuilder let label: Text
let startAnimation: P
let label: Text
@Environment(\.colorScheme) private var colorScheme
init(content _: Content.Type, value: WidescreenNavigationMode, selection: Binding<WidescreenNavigationMode>, startAnimation: P, @ViewBuilder label: () -> Text) {
self.value = value
self._selection = selection
self.startAnimation = startAnimation
self.label = label()
}
private var selected: Bool {
selection == value
}
@ -84,7 +98,7 @@ private struct OptionView<Content: NavigationModePreview>: View {
}
private var preview: some View {
NavigationModeRepresentable<Content>(startAnimation: startAnimation)
NavigationModeRepresentable(content: Content.self, startAnimation: startAnimation)
.clipShape(RoundedRectangle(cornerRadius: 12.5, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: 12.5, style: .continuous)
@ -106,11 +120,15 @@ private struct WideCapsule: Shape {
@MainActor
private protocol NavigationModePreview: UIView {
init(startAnimation: PassthroughSubject<Void, Never>)
init(startAnimation: some Publisher<Void, Never>)
}
private struct NavigationModeRepresentable<UIViewType: NavigationModePreview>: UIViewRepresentable {
let startAnimation: PassthroughSubject<Void, Never>
private struct NavigationModeRepresentable<UIViewType: NavigationModePreview, P: Publisher<Void, Never>>: UIViewRepresentable {
let startAnimation: P
init(content _: UIViewType.Type, startAnimation: P) {
self.startAnimation = startAnimation
}
func makeUIView(context: Context) -> UIViewType {
UIViewType(startAnimation: startAnimation)
@ -128,7 +146,7 @@ private final class StackNavigationPreview: UIView, NavigationModePreview {
private let destinationView = UIView()
private var cancellable: AnyCancellable?
init(startAnimation: PassthroughSubject<Void, Never>) {
init(startAnimation: some Publisher<Void, Never>) {
super.init(frame: .zero)
backgroundColor = .appBackground
@ -203,7 +221,7 @@ private final class SplitNavigationPreview: UIView, NavigationModePreview {
private var cellStackTrailingConstraint: NSLayoutConstraint!
private var cancellable: AnyCancellable?
init(startAnimation: PassthroughSubject<Void, Never>) {
init(startAnimation: some Publisher<Void, Never>) {
super.init(frame: .zero)
backgroundColor = .appBackground
@ -297,7 +315,7 @@ private final class MultiColumnNavigationPreview: UIView, NavigationModePreview
private var startedAnimation = false
init(startAnimation: PassthroughSubject<Void, Never>) {
init(startAnimation: some Publisher<Void, Never>) {
super.init(frame: .zero)
backgroundColor = .appSecondaryBackground

View File

@ -1,64 +0,0 @@
//
// MediaPrefsView.swift
// Tusker
//
// Created by Shadowfacts on 2/22/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import SwiftUI
import TuskerPreferences
struct MediaPrefsView: View {
@ObservedObject var preferences = Preferences.shared
var body: some View {
List {
viewingSection
}
.listStyle(.insetGrouped)
.appGroupedListBackground(container: PreferencesNavigationController.self)
.navigationBarTitle("Media")
}
var viewingSection: some View {
Section(header: Text("Viewing")) {
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()
}
}
struct MediaPrefsView_Previews: PreviewProvider {
static var previews: some View {
MediaPrefsView()
}
}

View File

@ -23,7 +23,6 @@ struct PreferencesView: View {
var body: some View {
List {
accountsSection
notificationsSection
preferencesSection
aboutSection
}
@ -92,31 +91,22 @@ struct PreferencesView: View {
.appGroupedListRowBackground()
}
private var notificationsSection: some View {
Section {
NavigationLink(isActive: $navigationState.showNotificationPreferences) {
NotificationsPrefsView()
} label: {
Text("Notifications")
}
}
.appGroupedListRowBackground()
}
private var preferencesSection: some View {
Section {
NavigationLink(destination: AppearancePrefsView()) {
Text("Appearance")
}
NavigationLink(destination: ComposingPrefsView()) {
Text("Composing")
}
NavigationLink(destination: MediaPrefsView()) {
Text("Media")
}
NavigationLink(destination: BehaviorPrefsView()) {
Text("Behavior")
}
NavigationLink(isActive: $navigationState.showNotificationPreferences) {
NotificationsPrefsView()
} label: {
Text("Notifications")
}
NavigationLink(destination: ComposingPrefsView()) {
Text("Composing")
}
NavigationLink(destination: WellnessPrefsView()) {
Text("Digital Wellness")
}