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()
 | 
						||
    }
 | 
						||
}
 |