226 lines
7.7 KiB
Swift
226 lines
7.7 KiB
Swift
//
|
||
// LoginViewModel.swift
|
||
// VolnahubApp
|
||
//
|
||
// Created by cheykrym on 09/06/2025.
|
||
//
|
||
|
||
import Foundation
|
||
import Combine
|
||
|
||
class LoginViewModel: ObservableObject {
|
||
@Published var username: String = ""
|
||
@Published var userId: String = ""
|
||
@Published var password: String = ""
|
||
@Published var isLoading: Bool = true // сразу true, чтобы показать спиннер при автологине
|
||
@Published var showError: Bool = false
|
||
@Published var errorMessage: String = ""
|
||
@Published var isLoggedIn: Bool = false
|
||
@Published var socketState: SocketService.ConnectionState
|
||
@Published var chatLoadingState: ChatLoadingState = .idle
|
||
@Published var hasAcceptedTerms: Bool = false
|
||
@Published var isLoadingTerms: Bool = false
|
||
@Published var termsContent: String = ""
|
||
@Published var termsErrorMessage: String?
|
||
|
||
private let authService = AuthService()
|
||
private let socketService = SocketService.shared
|
||
private var cancellables = Set<AnyCancellable>()
|
||
|
||
enum ChatLoadingState: Equatable {
|
||
case idle
|
||
case loading
|
||
}
|
||
|
||
private enum DefaultsKeys {
|
||
static let currentUser = "currentUser"
|
||
static let userId = "userId"
|
||
}
|
||
|
||
init() {
|
||
socketState = socketService.currentConnectionState
|
||
observeSocketState()
|
||
observeChatsReload()
|
||
// loadStoredUser()
|
||
|
||
// Запускаем автологин
|
||
autoLogin()
|
||
}
|
||
|
||
private func observeSocketState() {
|
||
socketService.connectionStatePublisher
|
||
.receive(on: DispatchQueue.main)
|
||
.sink { [weak self] state in
|
||
self?.handleSocketStateChange(state)
|
||
}
|
||
.store(in: &cancellables)
|
||
}
|
||
|
||
private func observeChatsReload() {
|
||
NotificationCenter.default.publisher(for: .chatsReloadCompleted)
|
||
.receive(on: DispatchQueue.main)
|
||
.sink { [weak self] _ in
|
||
self?.chatLoadingState = .idle
|
||
}
|
||
.store(in: &cancellables)
|
||
}
|
||
|
||
private func handleSocketStateChange(_ state: SocketService.ConnectionState) {
|
||
socketState = state
|
||
if state == .connected {
|
||
triggerChatsReloadIfNeeded()
|
||
} else {
|
||
chatLoadingState = .idle
|
||
}
|
||
}
|
||
|
||
private func triggerChatsReloadIfNeeded() {
|
||
guard chatLoadingState != .loading else { return }
|
||
chatLoadingState = .loading
|
||
NotificationCenter.default.post(name: .chatsShouldRefresh, object: nil)
|
||
}
|
||
|
||
func autoLogin() {
|
||
authService.autoLogin { [weak self] success, error in
|
||
DispatchQueue.main.async {
|
||
// self?.isLoading = false
|
||
if success {
|
||
self?.loadStoredUser()
|
||
self?.isLoggedIn = true
|
||
self?.socketService.connectForCurrentUser()
|
||
} else {
|
||
self?.isLoggedIn = false
|
||
self?.errorMessage = error ?? NSLocalizedString("Произошла ошибка.", comment: "")
|
||
self?.showError = false
|
||
self?.socketService.disconnect()
|
||
}
|
||
self?.isLoading = false
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
func login() {
|
||
isLoading = true
|
||
showError = false
|
||
|
||
authService.login(username: username, password: password) { [weak self] success, error in
|
||
DispatchQueue.main.async {
|
||
self?.isLoading = false
|
||
if success {
|
||
self?.loadStoredUser()
|
||
self?.isLoggedIn = true
|
||
self?.socketService.connectForCurrentUser()
|
||
} else {
|
||
self?.errorMessage = error ?? NSLocalizedString("Неизвестная ошибка", comment: "")
|
||
self?.showError = true
|
||
self?.socketService.disconnect()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func registerUser(username: String, password: String, invite: String?, completion: @escaping (Bool, String?) -> Void) {
|
||
authService.register(username: username, password: password, invite: invite) { [weak self] success, message in
|
||
DispatchQueue.main.async {
|
||
if success {
|
||
self?.isLoggedIn = true // 👈 переключаем на главный экран после автологина
|
||
self?.loadStoredUser()
|
||
self?.socketService.connectForCurrentUser()
|
||
} else {
|
||
self?.socketService.disconnect()
|
||
}
|
||
completion(success, message)
|
||
}
|
||
}
|
||
}
|
||
|
||
func logoutCurrentUser() {
|
||
authService.logoutCurrentUser { [weak self] success, error in
|
||
DispatchQueue.main.async {
|
||
if success {
|
||
self?.loadStoredUser()
|
||
self?.password = ""
|
||
self?.isLoggedIn = true
|
||
self?.showError = false
|
||
self?.socketService.connectForCurrentUser()
|
||
} else {
|
||
self?.username = ""
|
||
self?.userId = ""
|
||
self?.password = ""
|
||
self?.isLoggedIn = false
|
||
self?.errorMessage = error ?? NSLocalizedString("Ошибка при деавторизации.", comment: "")
|
||
self?.showError = false
|
||
self?.socketService.disconnect()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// func logout() {
|
||
// username = ""
|
||
// password = ""
|
||
// isLoggedIn = false
|
||
// showError = false
|
||
// errorMessage = ""
|
||
// }
|
||
|
||
private func loadStoredUser() {
|
||
let defaults = UserDefaults.standard
|
||
username = defaults.string(forKey: DefaultsKeys.currentUser) ?? ""
|
||
userId = KeychainService.shared.get(forKey: DefaultsKeys.userId, service: username) ?? ""
|
||
|
||
if AppConfig.DEBUG{ print("username: \(username) | userId: \(userId)")}
|
||
}
|
||
|
||
func loadTermsIfNeeded() {
|
||
guard !isLoadingTerms else { return }
|
||
|
||
if !termsContent.isEmpty {
|
||
termsErrorMessage = nil
|
||
return
|
||
}
|
||
|
||
isLoadingTerms = true
|
||
termsErrorMessage = nil
|
||
|
||
NetworkClient.shared.request(
|
||
path: "/legal/terms",
|
||
headers: ["Accept": "text/plain"],
|
||
requiresAuth: false,
|
||
callbackQueue: .main
|
||
) { [weak self] result in
|
||
guard let self else { return }
|
||
|
||
self.isLoadingTerms = false
|
||
|
||
switch result {
|
||
case .success(let response):
|
||
if let content = String(data: response.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||
!content.isEmpty {
|
||
self.termsContent = content
|
||
return
|
||
}
|
||
|
||
if let jsonObject = try? JSONSerialization.jsonObject(with: response.data, options: []),
|
||
let json = jsonObject as? [String: Any],
|
||
let content = (json["content"] as? String) ?? (json["text"] as? String),
|
||
!content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||
self.termsContent = content
|
||
} else {
|
||
self.termsErrorMessage = NSLocalizedString("Не удалось загрузить текст правил.", comment: "")
|
||
}
|
||
case .failure:
|
||
self.termsErrorMessage = NSLocalizedString("Не удалось загрузить текст правил.", comment: "")
|
||
}
|
||
}
|
||
}
|
||
|
||
func reloadTerms() {
|
||
termsContent = ""
|
||
termsErrorMessage = nil
|
||
loadTermsIfNeeded()
|
||
}
|
||
}
|