IceCubesApp/IceCubesApp/App/Tabs/Settings/DisplaySettingsView.swift

282 lines
10 KiB
Swift
Raw Normal View History

2023-03-13 12:38:28 +00:00
import Combine
2023-01-06 16:14:34 +00:00
import DesignSystem
2023-01-22 05:38:30 +00:00
import Env
2023-01-17 10:36:01 +00:00
import Models
2023-02-18 06:26:48 +00:00
import Network
import Observation
2024-01-06 18:27:26 +00:00
import StatusKit
2023-01-17 10:36:01 +00:00
import SwiftUI
@Observable class DisplaySettingsLocalValues {
var tintColor = Theme.shared.tintColor
var primaryBackgroundColor = Theme.shared.primaryBackgroundColor
var secondaryBackgroundColor = Theme.shared.secondaryBackgroundColor
var labelColor = Theme.shared.labelColor
var lineSpacing = Theme.shared.lineSpacing
var fontSizeScale = Theme.shared.fontSizeScale
}
2023-01-06 16:14:34 +00:00
2023-09-19 07:18:20 +00:00
@MainActor
2023-01-06 16:14:34 +00:00
struct DisplaySettingsView: View {
2023-01-30 06:27:06 +00:00
typealias FontState = Theme.FontState
2023-01-22 15:59:56 +00:00
@Environment(\.colorScheme) private var colorScheme
2023-09-18 19:03:52 +00:00
@Environment(Theme.self) private var theme
2023-09-19 07:18:20 +00:00
@Environment(UserPreferences.self) private var userPreferences
2023-03-13 12:38:28 +00:00
@State private var localValues = DisplaySettingsLocalValues()
2023-01-30 06:27:06 +00:00
@State private var isFontSelectorPresented = false
2023-03-13 12:38:28 +00:00
private let previewStatusViewModel = StatusRowViewModel(status: Status.placeholder(forSettings: true, language: "la"),
client: Client(server: ""),
routerPath: RouterPath()) // translate from latin button
2023-03-13 12:38:28 +00:00
2023-01-06 16:14:34 +00:00
var body: some View {
ZStack(alignment: .top) {
Form {
2024-01-21 10:13:25 +00:00
#if !os(visionOS)
2024-02-14 11:48:14 +00:00
StatusRowView(viewModel: previewStatusViewModel)
.allowsHitTesting(false)
.opacity(0)
.hidden()
themeSection
2024-01-21 10:13:25 +00:00
#endif
fontSection
layoutSection
platformsSection
resetSection
}
.navigationTitle("settings.display.navigation-title")
#if !os(visionOS)
2024-02-14 11:48:14 +00:00
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
2024-02-14 11:48:14 +00:00
.task(id: localValues.tintColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.tintColor = localValues.tintColor
}
.task(id: localValues.primaryBackgroundColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.primaryBackgroundColor = localValues.primaryBackgroundColor
}
.task(id: localValues.secondaryBackgroundColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.secondaryBackgroundColor = localValues.secondaryBackgroundColor
}
.task(id: localValues.labelColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.labelColor = localValues.labelColor
}
.task(id: localValues.lineSpacing) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.lineSpacing = localValues.lineSpacing
}
.task(id: localValues.fontSizeScale) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.fontSizeScale = localValues.fontSizeScale
}
2024-01-21 10:13:25 +00:00
#if !os(visionOS)
2024-02-14 11:48:14 +00:00
examplePost
2024-01-21 10:13:25 +00:00
#endif
2023-03-03 07:29:45 +00:00
}
}
2023-03-13 12:38:28 +00:00
private var examplePost: some View {
VStack(spacing: 0) {
StatusRowView(viewModel: previewStatusViewModel)
2023-03-03 07:29:45 +00:00
.allowsHitTesting(false)
.padding(.layoutPadding)
.background(theme.primaryBackgroundColor)
.cornerRadius(8)
.padding(.horizontal, .layoutPadding)
.padding(.top, .layoutPadding)
.background(theme.secondaryBackgroundColor)
Rectangle()
.fill(theme.secondaryBackgroundColor)
.frame(height: 30)
.mask(LinearGradient(gradient: Gradient(colors: [theme.secondaryBackgroundColor, .clear]),
startPoint: .top, endPoint: .bottom))
2023-03-03 07:29:45 +00:00
}
}
2023-03-13 12:38:28 +00:00
2023-09-18 19:03:52 +00:00
@ViewBuilder
2023-03-03 07:29:45 +00:00
private var themeSection: some View {
2023-09-18 19:03:52 +00:00
@Bindable var theme = theme
2023-03-03 07:29:45 +00:00
Section {
Toggle("settings.display.theme.systemColor", isOn: $theme.followSystemColorScheme)
themeSelectorButton
Group {
ColorPicker("settings.display.theme.tint", selection: $localValues.tintColor)
ColorPicker("settings.display.theme.background", selection: $localValues.primaryBackgroundColor)
ColorPicker("settings.display.theme.secondary-background", selection: $localValues.secondaryBackgroundColor)
ColorPicker("settings.display.theme.text-color", selection: $localValues.labelColor)
}
2023-03-03 07:29:45 +00:00
.disabled(theme.followSystemColorScheme)
.opacity(theme.followSystemColorScheme ? 0.5 : 1.0)
.onChange(of: theme.selectedSet) {
localValues.tintColor = theme.tintColor
localValues.primaryBackgroundColor = theme.primaryBackgroundColor
localValues.secondaryBackgroundColor = theme.secondaryBackgroundColor
localValues.labelColor = theme.labelColor
2023-03-03 07:29:45 +00:00
}
} header: {
Text("settings.display.section.theme")
} footer: {
if theme.followSystemColorScheme {
Text("settings.display.section.theme.footer")
}
}
#if !os(visionOS)
2023-03-03 07:29:45 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-03-03 07:29:45 +00:00
}
2023-03-13 12:38:28 +00:00
2023-03-03 07:29:45 +00:00
private var fontSection: some View {
Section("settings.display.section.font") {
Picker("settings.display.font", selection: .init(get: { () -> FontState in
if theme.chosenFont?.fontName == "OpenDyslexic-Regular" {
return FontState.openDyslexic
} else if theme.chosenFont?.fontName == "AtkinsonHyperlegible-Regular" {
return FontState.hyperLegible
} else if theme.chosenFont?.fontName == ".AppleSystemUIFontRounded-Regular" {
return FontState.SFRounded
}
return theme.chosenFontData != nil ? FontState.custom : FontState.system
}, set: { newValue in
switch newValue {
case .system:
theme.chosenFont = nil
case .openDyslexic:
theme.chosenFont = UIFont(name: "OpenDyslexic", size: 1)
case .hyperLegible:
theme.chosenFont = UIFont(name: "Atkinson Hyperlegible", size: 1)
case .SFRounded:
theme.chosenFont = UIFont.systemFont(ofSize: 1).rounded()
case .custom:
isFontSelectorPresented = true
}
})) {
ForEach(FontState.allCases, id: \.rawValue) { fontState in
Text(fontState.title).tag(fontState)
}
}
2023-03-03 07:29:45 +00:00
.navigationDestination(isPresented: $isFontSelectorPresented, destination: { FontPicker() })
2023-02-18 06:26:48 +00:00
2023-03-03 07:29:45 +00:00
VStack {
Slider(value: $localValues.fontSizeScale, in: 0.5 ... 1.5, step: 0.1)
Text("settings.display.font.scaling-\(String(format: "%.1f", localValues.fontSizeScale))")
2023-03-03 07:29:45 +00:00
.font(.scaledBody)
}
.alignmentGuide(.listRowSeparatorLeading) { d in
d[.leading]
}
VStack {
Slider(value: $localValues.lineSpacing, in: 0.4 ... 10.0, step: 0.2)
Text("settings.display.font.line-spacing-\(String(format: "%.1f", localValues.lineSpacing))")
.font(.scaledBody)
}
.alignmentGuide(.listRowSeparatorLeading) { d in
d[.leading]
}
2023-03-03 07:29:45 +00:00
}
#if !os(visionOS)
2023-03-03 07:29:45 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-03-03 07:29:45 +00:00
}
2023-03-13 12:38:28 +00:00
2023-09-18 19:03:52 +00:00
@ViewBuilder
2023-03-03 07:29:45 +00:00
private var layoutSection: some View {
2023-09-18 19:03:52 +00:00
@Bindable var theme = theme
2023-09-19 07:18:20 +00:00
@Bindable var userPreferences = userPreferences
2023-03-03 07:29:45 +00:00
Section("settings.display.section.display") {
Picker("settings.display.avatar.position", selection: $theme.avatarPosition) {
ForEach(Theme.AvatarPosition.allCases, id: \.rawValue) { position in
Text(position.description).tag(position)
2023-01-06 16:14:34 +00:00
}
2023-03-03 07:29:45 +00:00
}
Picker("settings.display.avatar.shape", selection: $theme.avatarShape) {
ForEach(Theme.AvatarShape.allCases, id: \.rawValue) { shape in
Text(shape.description).tag(shape)
2023-01-07 16:44:25 +00:00
}
2023-01-06 16:14:34 +00:00
}
2023-03-03 07:29:45 +00:00
Toggle("settings.display.full-username", isOn: $theme.displayFullUsername)
Picker("settings.display.status.action-buttons", selection: $theme.statusActionsDisplay) {
ForEach(Theme.StatusActionsDisplay.allCases, id: \.rawValue) { buttonStyle in
Text(buttonStyle.description).tag(buttonStyle)
}
}
Picker("settings.display.status.action-secondary", selection: $theme.statusActionSecondary) {
ForEach(Theme.StatusActionSecondary.allCases, id: \.rawValue) { action in
Text(action.description).tag(action)
}
}
2023-03-03 07:29:45 +00:00
Picker("settings.display.status.media-style", selection: $theme.statusDisplayStyle) {
ForEach(Theme.StatusDisplayStyle.allCases, id: \.rawValue) { buttonStyle in
Text(buttonStyle.description).tag(buttonStyle)
}
}
2023-03-03 07:29:45 +00:00
Toggle("settings.display.translate-button", isOn: $userPreferences.showTranslateButton)
Toggle("settings.display.pending-at-bottom", isOn: $userPreferences.pendingShownAtBottom)
Toggle("settings.display.pending-left", isOn: $userPreferences.pendingShownLeft)
Toggle("settings.display.show-reply-indentation", isOn: $userPreferences.showReplyIndentation)
if userPreferences.showReplyIndentation {
VStack {
Slider(value: .init(get: {
Double(userPreferences.maxReplyIndentation)
}, set: { newVal in
userPreferences.maxReplyIndentation = UInt(newVal)
2023-12-18 07:22:59 +00:00
}), in: 1 ... 20, step: 1)
Text("settings.display.max-reply-indentation-\(String(userPreferences.maxReplyIndentation))")
.font(.scaledBody)
}
.alignmentGuide(.listRowSeparatorLeading) { d in
d[.leading]
}
}
Toggle("settings.display.show-account-popover", isOn: $userPreferences.showAccountPopover)
2023-03-03 07:29:45 +00:00
}
#if !os(visionOS)
2023-03-03 07:29:45 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-03-03 07:29:45 +00:00
}
2023-03-13 12:38:28 +00:00
2023-03-03 07:29:45 +00:00
@ViewBuilder
private var platformsSection: some View {
2023-09-19 07:18:20 +00:00
@Bindable var userPreferences = userPreferences
2023-03-03 07:29:45 +00:00
if UIDevice.current.userInterfaceIdiom == .pad {
Section("settings.display.section.platform") {
2023-03-03 07:29:45 +00:00
Toggle("settings.display.show-ipad-column", isOn: $userPreferences.showiPadSecondaryColumn)
2023-01-06 16:14:34 +00:00
}
#if !os(visionOS)
2023-01-06 16:14:34 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-01-06 16:14:34 +00:00
}
2023-03-03 07:29:45 +00:00
}
2023-03-13 12:38:28 +00:00
2023-03-03 07:29:45 +00:00
private var resetSection: some View {
Section {
Button {
2024-01-13 07:48:29 +00:00
theme.restoreDefault()
2023-03-03 07:29:45 +00:00
} label: {
Text("settings.display.restore")
}
}
#if !os(visionOS)
2023-03-03 07:29:45 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-01-06 16:14:34 +00:00
}
2023-01-06 16:14:34 +00:00
private var themeSelectorButton: some View {
NavigationLink(destination: ThemePreviewView()) {
HStack {
Text("settings.display.section.theme")
2023-01-06 16:14:34 +00:00
Spacer()
Text(theme.selectedSet.rawValue)
}
}
}
}