MobileMkch-iOS/MobileMkch/NotificationSettingsView.swift

299 lines
14 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
struct NotificationSettingsView: View {
@EnvironmentObject var settings: Settings
@EnvironmentObject var notificationManager: NotificationManager
@EnvironmentObject var apiClient: APIClient
@State private var showingPermissionAlert = false
@State private var boards: [Board] = []
@State private var isLoadingBoards = false
@State private var isCheckingThreads = false
@State private var showingTestNotification = false
var body: some View {
VStack {
if !settings.enableUnstableFeatures {
VStack(spacing: 16) {
Image(systemName: "lock.fill")
.font(.system(size: 50))
.foregroundColor(.gray)
Text("Функция заблокирована")
.font(.title2)
.fontWeight(.bold)
Text("Для использования уведомлений необходимо включить нестабильные функции в настройках.")
.font(.body)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.orange)
Text("BETA Функция")
.font(.headline)
.foregroundColor(.orange)
}
.padding()
.background(Color.orange.opacity(0.1))
.cornerRadius(8)
.padding(.horizontal)
Text("Уведомления находятся в бета-версии и могут работать нестабильно.")
.font(.caption)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal)
.padding(.bottom)
Form {
Section(header: Text("Уведомления")) {
Toggle("Включить уведомления", isOn: $settings.notificationsEnabled)
.onChange(of: settings.notificationsEnabled) { newValue in
if newValue {
requestNotificationPermission()
}
settings.saveSettings()
}
if settings.notificationsEnabled {
HStack {
Text("Интервал проверки")
Spacer()
Picker("", selection: $settings.notificationInterval) {
Text("5 мин").tag(300)
Text("15 мин").tag(900)
Text("30 мин").tag(1800)
Text("1 час").tag(3600)
}
.pickerStyle(MenuPickerStyle())
}
.onChange(of: settings.notificationInterval) { _ in
settings.saveSettings()
BackgroundTaskManager.shared.scheduleBackgroundTask()
}
Button(action: {
showingTestNotification = true
notificationManager.scheduleTestNotification()
}) {
HStack {
Image(systemName: "bell.badge")
Text("Отправить тестовое уведомление")
}
}
.foregroundColor(.blue)
.disabled(!notificationManager.isNotificationsEnabled)
Button(action: {
isCheckingThreads = true
checkNewThreadsNow()
}) {
HStack {
if isCheckingThreads {
ProgressView()
.scaleEffect(0.8)
} else {
Image(systemName: "arrow.clockwise")
}
Text("Проверить новые треды сейчас")
}
}
.foregroundColor(.blue)
.disabled(isCheckingThreads || notificationManager.subscribedBoards.isEmpty)
Button(action: {
syncAllBoards()
}) {
HStack {
Image(systemName: "arrow.triangle.2.circlepath")
Text("Синхронизировать все доски")
}
}
.foregroundColor(.orange)
.disabled(notificationManager.subscribedBoards.isEmpty)
Button(action: {
notificationManager.clearAllSavedThreads()
}) {
HStack {
Image(systemName: "trash")
Text("Очистить все сохраненные треды")
}
}
.foregroundColor(.red)
.disabled(notificationManager.subscribedBoards.isEmpty)
}
}
if settings.notificationsEnabled {
Section(header: Text("Подписки на доски")) {
if isLoadingBoards {
HStack {
ProgressView()
.scaleEffect(0.8)
Text("Загрузка досок...")
.foregroundColor(.secondary)
}
} else if boards.isEmpty {
Text("Не удалось загрузить доски")
.foregroundColor(.secondary)
} else {
ForEach(boards) { board in
HStack {
VStack(alignment: .leading, spacing: 2) {
Text("/\(board.code)/")
.font(.headline)
Text(board.description.isEmpty ? "Без описания" : board.description)
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(1)
}
Spacer()
Toggle("", isOn: Binding(
get: { notificationManager.subscribedBoards.contains(board.code) },
set: { isSubscribed in
if isSubscribed {
notificationManager.subscribeToBoard(board.code)
} else {
notificationManager.unsubscribeFromBoard(board.code)
}
}
))
}
}
}
}
Section(header: Text("Статус")) {
HStack {
Text("Разрешения")
Spacer()
Text(notificationManager.isNotificationsEnabled ? "Включены" : "Отключены")
.foregroundColor(notificationManager.isNotificationsEnabled ? .green : .red)
}
HStack {
Text("Подписки")
Spacer()
Text("\(notificationManager.subscribedBoards.count) досок")
.foregroundColor(.secondary)
}
}
}
}
}
}
.navigationTitle("Уведомления")
.onAppear {
if boards.isEmpty {
loadBoards()
}
}
.alert("Разрешить уведомления", isPresented: $showingPermissionAlert) {
Button("Настройки") {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}
Button("Отмена", role: .cancel) {
settings.notificationsEnabled = false
}
} message: {
Text("Для получения уведомлений о новых тредах необходимо разрешить уведомления в настройках")
}
.alert("Тестовое уведомление", isPresented: $showingTestNotification) {
Button("OK") { }
} message: {
Text("Тестовое уведомление отправлено. Проверьте, получили ли вы его.")
}
}
}
extension NotificationSettingsView {
private func requestNotificationPermission() {
notificationManager.requestPermission { granted in
if !granted {
showingPermissionAlert = true
}
}
}
private func checkNewThreadsNow() {
guard !notificationManager.subscribedBoards.isEmpty else { return }
let group = DispatchGroup()
var foundNewThreads = false
for boardCode in notificationManager.subscribedBoards {
group.enter()
apiClient.checkNewThreads(forBoard: boardCode, lastKnownThreadId: 0) { result in
DispatchQueue.main.async {
switch result {
case .success(let newThreads):
if !newThreads.isEmpty {
foundNewThreads = true
for thread in newThreads {
notificationManager.scheduleNotification(for: thread, boardCode: boardCode)
}
}
case .failure(let error):
print("Ошибка проверки тредов для /\(boardCode)/: \(error)")
}
group.leave()
}
}
}
group.notify(queue: .main) {
isCheckingThreads = false
if !foundNewThreads {
print("Новых тредов не найдено")
}
}
}
private func syncAllBoards() {
for boardCode in notificationManager.subscribedBoards {
let savedThreadsKey = "savedThreads_\(boardCode)"
UserDefaults.standard.removeObject(forKey: savedThreadsKey)
let url = URL(string: "https://mkch.pooziqo.xyz/api/board/\(boardCode)")!
var request = URLRequest(url: url)
request.setValue("MobileMkch/2.1.0-ios-alpha", forHTTPHeaderField: "User-Agent")
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data,
let threads = try? JSONDecoder().decode([Thread].self, from: data) {
if let encodedData = try? JSONEncoder().encode(threads) {
UserDefaults.standard.set(encodedData, forKey: savedThreadsKey)
print("Синхронизировано \(threads.count) тредов для /\(boardCode)/")
}
}
}.resume()
}
}
private func loadBoards() {
isLoadingBoards = true
apiClient.getBoards { result in
DispatchQueue.main.async {
isLoadingBoards = false
switch result {
case .success(let loadedBoards):
boards = loadedBoards
case .failure:
boards = []
}
}
}
}
}