329 lines
13 KiB
Swift
329 lines
13 KiB
Swift
import SwiftUI
|
||
import Combine
|
||
import Darwin
|
||
|
||
struct SettingsView: View {
|
||
@EnvironmentObject var settings: Settings
|
||
@EnvironmentObject var apiClient: APIClient
|
||
@Environment(\.dismiss) private var dismiss
|
||
|
||
@State private var showingAbout = false
|
||
@State private var showingInfo = false
|
||
@State private var testKeyResult: String?
|
||
@State private var testPasscodeResult: String?
|
||
@State private var isTestingKey = false
|
||
@State private var isTestingPasscode = false
|
||
@State private var debugTapCount = 0
|
||
@State private var showingDebugMenu = false
|
||
|
||
var body: some View {
|
||
NavigationView {
|
||
Form {
|
||
Section("Внешний вид") {
|
||
Picker("Тема", selection: $settings.theme) {
|
||
Text("Темная").tag("dark")
|
||
Text("Светлая").tag("light")
|
||
}
|
||
.onReceive(Just(settings.theme)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
|
||
Toggle("Авторефреш", isOn: $settings.autoRefresh)
|
||
.onReceive(Just(settings.autoRefresh)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
|
||
Toggle("Показывать файлы", isOn: $settings.showFiles)
|
||
.onReceive(Just(settings.showFiles)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
|
||
Toggle("Компактный режим", isOn: $settings.compactMode)
|
||
.onReceive(Just(settings.compactMode)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
|
||
Picker("Размер страницы", selection: $settings.pageSize) {
|
||
Text("5").tag(5)
|
||
Text("10").tag(10)
|
||
Text("15").tag(15)
|
||
Text("20").tag(20)
|
||
}
|
||
.onReceive(Just(settings.pageSize)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
}
|
||
|
||
Section("Последняя доска") {
|
||
Text(settings.lastBoard.isEmpty ? "Не выбрана" : settings.lastBoard)
|
||
.foregroundColor(.secondary)
|
||
}
|
||
|
||
Section("Аутентификация") {
|
||
SecureField("Passcode для постинга", text: $settings.passcode)
|
||
.onReceive(Just(settings.passcode)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
|
||
SecureField("Ключ аутентификации", text: $settings.key)
|
||
.onReceive(Just(settings.key)) { _ in
|
||
settings.saveSettings()
|
||
}
|
||
|
||
HStack {
|
||
Button("Тест ключа") {
|
||
testKey()
|
||
}
|
||
.disabled(settings.key.isEmpty || isTestingKey)
|
||
|
||
if isTestingKey {
|
||
ProgressView()
|
||
.scaleEffect(0.8)
|
||
}
|
||
|
||
if let result = testKeyResult {
|
||
Text(result)
|
||
.font(.caption)
|
||
.foregroundColor(result.contains("успешно") ? .green : .red)
|
||
}
|
||
}
|
||
|
||
HStack {
|
||
Button("Тест passcode") {
|
||
testPasscode()
|
||
}
|
||
.disabled(settings.passcode.isEmpty || isTestingPasscode)
|
||
|
||
if isTestingPasscode {
|
||
ProgressView()
|
||
.scaleEffect(0.8)
|
||
}
|
||
|
||
if let result = testPasscodeResult {
|
||
Text(result)
|
||
.font(.caption)
|
||
.foregroundColor(result.contains("успешно") ? .green : .red)
|
||
}
|
||
}
|
||
}
|
||
|
||
Section("Уведомления") {
|
||
NavigationLink("Настройки уведомлений") {
|
||
NotificationSettingsView()
|
||
.environmentObject(apiClient)
|
||
}
|
||
}
|
||
|
||
Section("Управление кэшем") {
|
||
Button("Очистить кэш досок") {
|
||
Cache.shared.delete("boards")
|
||
}
|
||
|
||
Button("Очистить кэш тредов") {
|
||
apiClient.getBoards { result in
|
||
if case .success(let boards) = result {
|
||
for board in boards {
|
||
Cache.shared.delete("threads_\(board.code)")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Button("Очистить весь кэш") {
|
||
Cache.shared.clear()
|
||
}
|
||
}
|
||
|
||
Section("Сброс") {
|
||
Button("Сбросить настройки") {
|
||
settings.resetSettings()
|
||
}
|
||
.foregroundColor(.red)
|
||
}
|
||
|
||
Section {
|
||
Button("Об аппке") {
|
||
showingAbout = true
|
||
}
|
||
|
||
Button("Я думаю тебя направили сюда") {
|
||
showingInfo = true
|
||
}
|
||
}
|
||
|
||
Section("Информация об устройстве") {
|
||
VStack(alignment: .leading, spacing: 4) {
|
||
Text("Устройство: \(getDeviceModel())")
|
||
Text("Система: \(UIDevice.current.systemName) \(UIDevice.current.systemVersion)")
|
||
Text("Тип: \(UIDevice.current.name.isEmpty ? "Не удалось определить, увы" : UIDevice.current.name)")
|
||
Text("Идентификатор: \(UIDevice.current.identifierForVendor?.uuidString ?? "Неизвестно")")
|
||
}
|
||
.font(.caption)
|
||
.foregroundColor(.secondary)
|
||
.onTapGesture {
|
||
debugTapCount += 1
|
||
if debugTapCount >= 5 {
|
||
showingDebugMenu = true
|
||
debugTapCount = 0
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.navigationTitle("Настройки")
|
||
.navigationBarTitleDisplayMode(.inline)
|
||
.toolbar {
|
||
ToolbarItem(placement: .navigationBarTrailing) {
|
||
Button("Готово") {
|
||
dismiss()
|
||
}
|
||
}
|
||
}
|
||
.sheet(isPresented: $showingAbout) {
|
||
AboutView()
|
||
}
|
||
.sheet(isPresented: $showingDebugMenu) {
|
||
DebugMenuView()
|
||
.environmentObject(NotificationManager.shared)
|
||
}
|
||
.alert("Информация о НЕОЖИДАНЫХ проблемах", isPresented: $showingInfo) {
|
||
Button("Закрыть") { }
|
||
} message: {
|
||
Text("Если тебя направили сюда, то значит ты попал на НЕИЗВЕДАННЫЕ ТЕРРИТОРИИ\n\nДА ДА, не ослышались, это не ошибка, это особенность\n\nУвы, разработчик имиджборда вставил палки в колеса\n\nИ без доната ему, например, постинг работать не будет\n\nУвы, постинг не работает без доната, а разработчик боится что на его сайте будут спам\n\nВкратце - на сайте работает капча, а наличие пасскода ее для вас отключает\n\nувы, конфет много, но на всех не хватит")
|
||
}
|
||
}
|
||
}
|
||
|
||
private func testKey() {
|
||
guard !settings.key.isEmpty else { return }
|
||
|
||
isTestingKey = true
|
||
testKeyResult = nil
|
||
|
||
apiClient.authenticate(authKey: settings.key) { error in
|
||
DispatchQueue.main.async {
|
||
self.isTestingKey = false
|
||
|
||
if let error = error {
|
||
self.testKeyResult = "Ошибка: \(error.localizedDescription)"
|
||
} else {
|
||
self.testKeyResult = "Аутентификация успешна"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private func testPasscode() {
|
||
guard !settings.passcode.isEmpty else { return }
|
||
|
||
isTestingPasscode = true
|
||
testPasscodeResult = nil
|
||
|
||
apiClient.loginWithPasscode(passcode: settings.passcode) { error in
|
||
DispatchQueue.main.async {
|
||
self.isTestingPasscode = false
|
||
|
||
if let error = error {
|
||
self.testPasscodeResult = "Ошибка: \(error.localizedDescription)"
|
||
} else {
|
||
self.testPasscodeResult = "Вход успешен"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private func getDeviceModel() -> String {
|
||
var systemInfo = utsname()
|
||
uname(&systemInfo)
|
||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||
let identifier = machineMirror.children.reduce("") { identifier, element in
|
||
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
||
return identifier + String(UnicodeScalar(UInt8(value)))
|
||
}
|
||
|
||
return identifier
|
||
}
|
||
}
|
||
|
||
struct AboutView: View {
|
||
@Environment(\.dismiss) private var dismiss
|
||
|
||
var body: some View {
|
||
NavigationView {
|
||
VStack(spacing: 20) {
|
||
Text("MobileMkch")
|
||
.font(.largeTitle)
|
||
.fontWeight(.bold)
|
||
|
||
Text("Мобильный клиент для мкача")
|
||
.font(.title3)
|
||
.foregroundColor(.secondary)
|
||
|
||
Divider()
|
||
|
||
VStack(alignment: .leading, spacing: 8) {
|
||
Text("Версия: 1.1.0-ios-alpha (Always in alpha lol)")
|
||
Text("Автор: w^x (лейн, платон, а похуй как угодно)")
|
||
Text("Разработано с ❤️ на Свифт")
|
||
}
|
||
.font(.body)
|
||
|
||
Spacer()
|
||
|
||
Button("Закрыть") {
|
||
dismiss()
|
||
}
|
||
.buttonStyle(.bordered)
|
||
}
|
||
.padding()
|
||
.navigationTitle("Об аппке")
|
||
.navigationBarTitleDisplayMode(.inline)
|
||
}
|
||
}
|
||
}
|
||
|
||
struct DebugMenuView: View {
|
||
@Environment(\.dismiss) private var dismiss
|
||
@EnvironmentObject var notificationManager: NotificationManager
|
||
|
||
var body: some View {
|
||
NavigationView {
|
||
VStack(spacing: 20) {
|
||
Text("Debug Menu")
|
||
.font(.largeTitle)
|
||
.fontWeight(.bold)
|
||
|
||
VStack(spacing: 15) {
|
||
Button("Тест краша") {
|
||
CrashHandler.shared.triggerTestCrash()
|
||
dismiss()
|
||
}
|
||
.buttonStyle(.borderedProminent)
|
||
.foregroundColor(.red)
|
||
|
||
Button("Тест уведомления") {
|
||
notificationManager.scheduleTestNotification()
|
||
}
|
||
.buttonStyle(.borderedProminent)
|
||
.foregroundColor(.blue)
|
||
}
|
||
|
||
Spacer()
|
||
|
||
Button("Закрыть") {
|
||
dismiss()
|
||
}
|
||
.buttonStyle(.bordered)
|
||
}
|
||
.padding()
|
||
.navigationTitle("Debug")
|
||
.navigationBarTitleDisplayMode(.inline)
|
||
}
|
||
}
|
||
}
|
||
|
||
#Preview {
|
||
SettingsView()
|
||
.environmentObject(Settings())
|
||
.environmentObject(APIClient())
|
||
}
|