// // ChatsTab.swift // VolnahubApp // // Created by cheykrym on 09/06/2025. // import SwiftUI struct ChatsTab: View { @StateObject private var viewModel = PrivateChatsViewModel() var body: some View { content .background(Color(UIColor.systemBackground)) .onAppear { viewModel.loadInitialChats() } } @ViewBuilder private var content: some View { if viewModel.isInitialLoading && viewModel.chats.isEmpty { loadingState } else if let message = viewModel.errorMessage, viewModel.chats.isEmpty { errorState(message: message) } else if viewModel.chats.isEmpty { emptyState } else { chatList } } private var chatList: some View { List { if let message = viewModel.errorMessage { Section { HStack(alignment: .top, spacing: 8) { Image(systemName: "exclamationmark.triangle.fill") .foregroundColor(.orange) Text(message) .font(.subheadline) .foregroundColor(.orange) Spacer(minLength: 0) Button(action: { viewModel.refresh() }) { Text(NSLocalizedString("Обновить", comment: "")) .font(.subheadline) } } .padding(.vertical, 4) } .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)) } ForEach(viewModel.chats) { chat in ChatRowView(chat: chat) .contentShape(Rectangle()) .onAppear { viewModel.loadMoreIfNeeded(currentItem: chat) } } if viewModel.isLoadingMore { loadingMoreRow } } .listStyle(.plain) } private var loadingState: some View { VStack(spacing: 12) { ProgressView() Text(NSLocalizedString("Загружаем чаты…", comment: "")) .font(.subheadline) .foregroundColor(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) } private func errorState(message: String) -> some View { VStack(spacing: 12) { Image(systemName: "exclamationmark.bubble") .font(.system(size: 48)) .foregroundColor(.orange) Text(message) .font(.body) .multilineTextAlignment(.center) .foregroundColor(.primary) Button(action: { viewModel.loadInitialChats(force: true) }) { Text(NSLocalizedString("Повторить", comment: "")) .font(.headline) } .buttonStyle(.borderedProminent) } .padding() .frame(maxWidth: .infinity, maxHeight: .infinity) } private var emptyState: some View { VStack(spacing: 12) { Image(systemName: "bubble.left") .font(.system(size: 48)) .foregroundColor(.secondary) Text(NSLocalizedString("Пока что у вас нет чатов", comment: "")) .font(.body) .foregroundColor(.secondary) Button(action: { viewModel.refresh() }) { Text(NSLocalizedString("Обновить", comment: "")) } .buttonStyle(.bordered) } .padding() .frame(maxWidth: .infinity, maxHeight: .infinity) } private var loadingMoreRow: some View { HStack { Spacer() ProgressView() .padding(.vertical, 12) Spacer() } .listRowSeparator(.hidden) } } private struct ChatRowView: View { let chat: PrivateChatListItem private var title: String { switch chat.chatType { case .self: return NSLocalizedString("Избранные сообщения", comment: "") case .privateChat, .unknown: if let custom = chat.chatData?.customName, !custom.isEmpty { return custom } if let full = chat.chatData?.fullName, !full.isEmpty { return full } if let login = chat.chatData?.login, !login.isEmpty { return "@\(login)" } return NSLocalizedString("Неизвестный пользователь", comment: "") } } private var subtitle: String { guard let message = chat.lastMessage else { return NSLocalizedString("Нет сообщений", comment: "") } if let content = message.content, !content.isEmpty { return content } if message.mediaLink != nil { return NSLocalizedString("Вложение", comment: "") } return NSLocalizedString("Сообщение", comment: "") } private var timestamp: String? { let date = chat.lastMessage?.createdAt ?? chat.createdAt guard let date else { return nil } return ChatRowView.timeFormatter.string(from: date) } private var initial: String { return String(title.prefix(1)).uppercased() } private var subtitleColor: Color { chat.unreadCount > 0 ? .primary : .secondary } var body: some View { HStack(spacing: 12) { Circle() .fill(Color.accentColor.opacity(0.15)) .frame(width: 44, height: 44) .overlay( Text(initial) .font(.headline) .foregroundColor(Color.accentColor) ) VStack(alignment: .leading, spacing: 4) { Text(title) .fontWeight(chat.unreadCount > 0 ? .semibold : .regular) .foregroundColor(.primary) .lineLimit(1) Text(subtitle) .font(.subheadline) .foregroundColor(subtitleColor) .lineLimit(2) } Spacer() VStack(alignment: .trailing, spacing: 6) { if let timestamp { Text(timestamp) .font(.caption) .foregroundColor(.secondary) } if chat.unreadCount > 0 { Text("\(chat.unreadCount)") .font(.caption2.bold()) .foregroundColor(.white) .padding(.horizontal, 8) .padding(.vertical, 4) .background( Capsule().fill(Color.accentColor) ) } } } .padding(.vertical, 8) } private static let timeFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .short return formatter }() } struct ChatsTab_Previews: PreviewProvider { static var previews: some View { ChatsTab() .environmentObject(ThemeManager()) } }