From 7c1d46ab776c6dab8cf37cfa5c932c179063d28f Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 21 Oct 2025 03:12:35 +0300 Subject: [PATCH] add to top bar connect --- yobble/Components/TopBarView.swift | 19 ++++++++++++- yobble/Resources/Localizable.xcstrings | 3 ++ yobble/Services/SocketService.swift | 38 ++++++++++++++++++++++++++ yobble/ViewModels/LoginViewModel.swift | 13 +++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/yobble/Components/TopBarView.swift b/yobble/Components/TopBarView.swift index dd56610..e3a1c25 100644 --- a/yobble/Components/TopBarView.swift +++ b/yobble/Components/TopBarView.swift @@ -27,6 +27,10 @@ struct TopBarView: View { return title == "Profile" } + private var shouldShowConnectionStatus: Bool { + viewModel.socketState != .connected + } + var body: some View { VStack(spacing: 0) { HStack { @@ -43,7 +47,10 @@ struct TopBarView: View { // Spacer() - if isHomeTab{ + if shouldShowConnectionStatus { + connectionStatusView + Spacer() + } else if isHomeTab{ Text("Yobble") .font(.largeTitle) .fontWeight(.bold) @@ -129,6 +136,16 @@ struct TopBarView: View { } private extension TopBarView { + var connectionStatusView: some View { + HStack(spacing: 8) { + ProgressView() + .progressViewStyle(.circular) + Text(NSLocalizedString("Подключение", comment: "")) + .font(.headline) + .foregroundColor(.primary) + } + } + private var normalizedRevealProgress: CGFloat { guard isChatsTab else { return 0 } return max(0, min(chatSearchRevealProgress, 1)) diff --git a/yobble/Resources/Localizable.xcstrings b/yobble/Resources/Localizable.xcstrings index aa378b4..45709b7 100644 --- a/yobble/Resources/Localizable.xcstrings +++ b/yobble/Resources/Localizable.xcstrings @@ -1473,6 +1473,9 @@ } } } + }, + "Подключение" : { + }, "Подтверждение пароля" : { "comment" : "Подтверждение пароля", diff --git a/yobble/Services/SocketService.swift b/yobble/Services/SocketService.swift index 40fcd08..39579b2 100644 --- a/yobble/Services/SocketService.swift +++ b/yobble/Services/SocketService.swift @@ -1,4 +1,5 @@ import Foundation +import Combine #if canImport(SocketIO) import SocketIO #endif @@ -6,10 +7,28 @@ import SocketIO final class SocketService { static let shared = SocketService() + enum ConnectionState: Equatable { + case disconnected + case connecting + case connected + } + private let syncQueue = DispatchQueue(label: "org.yobble.socket.service") private var currentToken: String? private var currentAuthPayload: [String: Any] = [:] + private let connectionStateSubject = CurrentValueSubject(.disconnected) + + var connectionStatePublisher: AnyPublisher { + connectionStateSubject + .removeDuplicates() + .eraseToAnyPublisher() + } + + var currentConnectionState: ConnectionState { + connectionStateSubject.value + } + #if canImport(SocketIO) private var manager: SocketManager? private var socket: SocketIOClient? @@ -17,6 +36,19 @@ final class SocketService { private init() {} + private func updateConnectionState(_ state: ConnectionState) { + let sendState: () -> Void = { [weak self] in + guard let self, self.connectionStateSubject.value != state else { return } + self.connectionStateSubject.send(state) + } + + if Thread.isMainThread { + sendState() + } else { + DispatchQueue.main.async(execute: sendState) + } + } + func connectForCurrentUser() { syncQueue.async { [weak self] in guard let self else { return } @@ -68,8 +100,10 @@ final class SocketService { currentToken = token currentAuthPayload = ["token": token] setupSocket(with: token) + updateConnectionState(.connecting) socket?.connect(withPayload: currentAuthPayload) #else + updateConnectionState(.disconnected) if AppConfig.DEBUG { print("[SocketService] SocketIO framework not available; skipping connection") } @@ -112,14 +146,17 @@ final class SocketService { socket.on(clientEvent: .connect) { _, _ in if AppConfig.DEBUG { print("[SocketService] Connected") } + self.updateConnectionState(.connected) } socket.on(clientEvent: .disconnect) { data, _ in if AppConfig.DEBUG { print("[SocketService] Disconnected: \(data)") } + self.updateConnectionState(.disconnected) } socket.on(clientEvent: .error) { data, _ in if AppConfig.DEBUG { print("[SocketService] Error: \(data)") } + self.updateConnectionState(.disconnected) } self.manager = manager @@ -131,6 +168,7 @@ final class SocketService { manager?.disconnect() socket = nil manager = nil + updateConnectionState(.disconnected) } #else private func disconnectInternal() { } diff --git a/yobble/ViewModels/LoginViewModel.swift b/yobble/ViewModels/LoginViewModel.swift index 9f73a08..ac6b2e1 100644 --- a/yobble/ViewModels/LoginViewModel.swift +++ b/yobble/ViewModels/LoginViewModel.swift @@ -16,9 +16,11 @@ class LoginViewModel: ObservableObject { @Published var showError: Bool = false @Published var errorMessage: String = "" @Published var isLoggedIn: Bool = false + @Published var socketState: SocketService.ConnectionState private let authService = AuthService() private let socketService = SocketService.shared + private var cancellables = Set() private enum DefaultsKeys { static let currentUser = "currentUser" @@ -26,12 +28,23 @@ class LoginViewModel: ObservableObject { } init() { + socketState = socketService.currentConnectionState + observeSocketState() // loadStoredUser() // Запускаем автологин autoLogin() } + private func observeSocketState() { + socketService.connectionStatePublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] state in + self?.socketState = state + } + .store(in: &cancellables) + } + func autoLogin() { authService.autoLogin { [weak self] success, error in DispatchQueue.main.async {