From cc3a09420558f1c2de7e95408f8ddc1eccf5cced Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 24 Jul 2025 12:16:23 +0200 Subject: [PATCH] Add nickname validation to prevent empty names - Validate nickname only when field loses focus or on submit - Replace empty nicknames with anon+random 4 digits (e.g. anon3847) - Remove auto-save timer to prevent validation while typing - Add FocusState to detect when user exits the field --- bitchat/ViewModels/ChatViewModel.swift | 18 +++++++++--------- bitchat/Views/ContentView.swift | 11 ++++++++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index d9f539cf..a8d84e67 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -18,14 +18,7 @@ import UIKit class ChatViewModel: ObservableObject { @Published var messages: [BitchatMessage] = [] @Published var connectedPeers: [String] = [] - @Published var nickname: String = "" { - didSet { - nicknameSaveTimer?.invalidate() - nicknameSaveTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in - self.saveNickname() - } - } - } + @Published var nickname: String = "" @Published var isConnected = false @Published var privateChats: [String: [BitchatMessage]] = [:] // peerID -> messages @Published var selectedPrivateChatPeer: String? = nil @@ -42,7 +35,6 @@ class ChatViewModel: ObservableObject { var meshService = BluetoothMeshService() private let userDefaults = UserDefaults.standard private let nicknameKey = "bitchat.nickname" - private var nicknameSaveTimer: Timer? @Published var favoritePeers: Set = [] // Now stores public key fingerprints instead of peer IDs private var peerIDToPublicKeyFingerprint: [String: String] = [:] // Maps ephemeral peer IDs to persistent fingerprints @@ -172,6 +164,14 @@ class ChatViewModel: ObservableObject { meshService.sendBroadcastAnnounce() } + func validateAndSaveNickname() { + // Check if nickname is empty or just whitespace + if nickname.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + nickname = "anon\(Int.random(in: 1000...9999))" + } + saveNickname() + } + private func loadFavorites() { // Load favorites from secure storage favoritePeers = SecureIdentityStateManager.shared.getFavorites() diff --git a/bitchat/Views/ContentView.swift b/bitchat/Views/ContentView.swift index 4f261d7c..6366f015 100644 --- a/bitchat/Views/ContentView.swift +++ b/bitchat/Views/ContentView.swift @@ -25,6 +25,7 @@ struct ContentView: View { @State private var showMessageActions = false @State private var selectedMessageSender: String? @State private var selectedMessageSenderID: String? + @FocusState private var isNicknameFieldFocused: Bool private var backgroundColor: Color { colorScheme == .dark ? Color.black : Color.white @@ -727,11 +728,15 @@ struct ContentView: View { .font(.system(size: 14, design: .monospaced)) .frame(maxWidth: 100) .foregroundColor(textColor) - .onChange(of: viewModel.nickname) { _ in - viewModel.saveNickname() + .focused($isNicknameFieldFocused) + .onChange(of: isNicknameFieldFocused) { isFocused in + if !isFocused { + // Only validate when losing focus + viewModel.validateAndSaveNickname() + } } .onSubmit { - viewModel.saveNickname() + viewModel.validateAndSaveNickname() } }