Update 3
This commit is contained in:
@@ -6,12 +6,20 @@ struct SettingsView: View {
|
||||
@AppStorage("syncWiFiOnly") private var syncWiFiOnly = true
|
||||
@AppStorage("showComments") private var showComments = true
|
||||
@AppStorage("appTheme") private var appTheme = "system"
|
||||
@AppStorage("accentTheme") private var accentThemeRaw = AccentTheme.ocean.rawValue
|
||||
@AppStorage("loggingEnabled") private var loggingEnabled = false
|
||||
|
||||
private var selectedTheme: AccentTheme {
|
||||
AccentTheme(rawValue: accentThemeRaw) ?? .ocean
|
||||
}
|
||||
@State private var serverURL = UserDefaults.standard.string(forKey: "serverURL") ?? ""
|
||||
@State private var showSignOutAlert = false
|
||||
@State private var isSyncing = false
|
||||
@State private var lastSynced = UserDefaults.standard.object(forKey: "lastSynced") as? Date
|
||||
@State private var showSafari: URL? = nil
|
||||
@State private var selectedLanguage: LanguageManager.Language = LanguageManager.shared.current
|
||||
@State private var showLogViewer = false
|
||||
@State private var shareItems: [Any]? = nil
|
||||
|
||||
private let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0"
|
||||
private let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"
|
||||
@@ -50,6 +58,39 @@ struct SettingsView: View {
|
||||
Text(L("settings.appearance.theme.dark")).tag("dark")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
// Accent colour swatches
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text(L("settings.appearance.accent"))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 10), count: 8), spacing: 10) {
|
||||
ForEach(AccentTheme.allCases) { theme in
|
||||
Button {
|
||||
accentThemeRaw = theme.rawValue
|
||||
} label: {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(theme.shelfColor)
|
||||
.frame(width: 32, height: 32)
|
||||
if selectedTheme == theme {
|
||||
Circle()
|
||||
.strokeBorder(.white, lineWidth: 2)
|
||||
.frame(width: 32, height: 32)
|
||||
Image(systemName: "checkmark")
|
||||
.font(.system(size: 11, weight: .bold))
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(theme.displayName)
|
||||
.accessibilityAddTraits(selectedTheme == theme ? .isSelected : [])
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
// Account section
|
||||
@@ -87,6 +128,35 @@ struct SettingsView: View {
|
||||
Toggle(L("settings.reader.showcomments"), isOn: $showComments)
|
||||
}
|
||||
|
||||
// Logging section
|
||||
Section(L("settings.log")) {
|
||||
Toggle(L("settings.log.enabled"), isOn: $loggingEnabled)
|
||||
.onChange(of: loggingEnabled) { _, newValue in
|
||||
LogManager.shared.isEnabled = newValue
|
||||
}
|
||||
|
||||
if loggingEnabled {
|
||||
Button {
|
||||
showLogViewer = true
|
||||
} label: {
|
||||
Label(L("settings.log.viewer.title"), systemImage: "list.bullet.rectangle")
|
||||
}
|
||||
|
||||
Button {
|
||||
let text = LogManager.shared.exportText()
|
||||
shareItems = [text]
|
||||
} label: {
|
||||
Label(L("settings.log.share"), systemImage: "square.and.arrow.up")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
LogManager.shared.clear()
|
||||
} label: {
|
||||
Label(L("settings.log.clear"), systemImage: "trash")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync section
|
||||
Section(L("settings.sync")) {
|
||||
Toggle(L("settings.sync.wifionly"), isOn: $syncWiFiOnly)
|
||||
@@ -134,6 +204,9 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle(L("settings.title"))
|
||||
.onAppear {
|
||||
loggingEnabled = LogManager.shared.isEnabled
|
||||
}
|
||||
.alert(L("settings.signout.alert.title"), isPresented: $showSignOutAlert) {
|
||||
Button(L("settings.signout.alert.confirm"), role: .destructive) { signOut() }
|
||||
Button(L("settings.signout.alert.cancel"), role: .cancel) {}
|
||||
@@ -144,6 +217,17 @@ struct SettingsView: View {
|
||||
SafariView(url: url)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
.sheet(isPresented: $showLogViewer) {
|
||||
LogViewerView()
|
||||
}
|
||||
.sheet(isPresented: Binding(
|
||||
get: { shareItems != nil },
|
||||
set: { if !$0 { shareItems = nil } }
|
||||
)) {
|
||||
if let items = shareItems {
|
||||
ShareSheet(items: items)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +270,65 @@ extension URL: @retroactive Identifiable {
|
||||
public var id: String { absoluteString }
|
||||
}
|
||||
|
||||
// MARK: - Log Viewer
|
||||
|
||||
struct LogViewerView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var logManager = LogManager.shared
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Group {
|
||||
if logManager.entries.isEmpty {
|
||||
ContentUnavailableView(
|
||||
L("settings.log.viewer.title"),
|
||||
systemImage: "list.bullet.rectangle",
|
||||
description: Text("No log entries yet.")
|
||||
)
|
||||
} else {
|
||||
List(logManager.entries.reversed()) { entry in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(entry.formatted)
|
||||
.font(.system(.caption, design: .monospaced))
|
||||
.foregroundStyle(colorFor(entry.level))
|
||||
}
|
||||
.listRowInsets(.init(top: 4, leading: 12, bottom: 4, trailing: 12))
|
||||
}
|
||||
.listStyle(.plain)
|
||||
}
|
||||
}
|
||||
.navigationTitle(L("settings.log.viewer.title"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(L("common.ok")) { dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func colorFor(_ level: LogEntry.Level) -> Color {
|
||||
switch level {
|
||||
case .debug: return .secondary
|
||||
case .info: return .primary
|
||||
case .warning: return .orange
|
||||
case .error: return .red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Share Sheet
|
||||
|
||||
struct ShareSheet: UIViewControllerRepresentable {
|
||||
let items: [Any]
|
||||
|
||||
func makeUIViewController(context: Context) -> UIActivityViewController {
|
||||
UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ vc: UIActivityViewController, context: Context) {}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SettingsView()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user