diff --git a/yobble/Views/Chat/PrivateChatView.swift b/yobble/Views/Chat/PrivateChatView.swift index f6d8b99..d19838f 100644 --- a/yobble/Views/Chat/PrivateChatView.swift +++ b/yobble/Views/Chat/PrivateChatView.swift @@ -117,49 +117,50 @@ struct PrivateChatView: View { } else if let error = viewModel.errorMessage, viewModel.messages.isEmpty { errorView(message: error) } else { - ScrollView { - LazyVStack(alignment: .leading, spacing: 12) { - if viewModel.isLoadingMore { - loadingMoreView - } else if viewModel.messages.isEmpty { - emptyState - } - - ForEach(viewModel.messages) { message in - messageRow(for: message) - .id(message.id) - .onAppear { - guard hasPositionedToBottom else { return } - viewModel.loadMoreIfNeeded(for: message) - } - } - - if let message = viewModel.errorMessage, - !message.isEmpty, - !viewModel.messages.isEmpty { - errorBanner(message: message) - } - - Color.clear - .frame(height: 1) - .id(bottomAnchorId) - .onAppear { isBottomAnchorVisible = true } - .onDisappear { isBottomAnchorVisible = false } - } - .padding(.vertical, 12) - } - .simultaneousGesture( - DragGesture().onChanged { value in - guard value.translation.height > 0 else { return } - isComposerFocused = false - } - ) - .refreshable { - viewModel.refresh() - } + messagesList } } + private var messagesList: some View { + ScrollView { + LazyVStack(alignment: .leading, spacing: 12) { + if viewModel.isLoadingMore { + loadingMoreView + } else if viewModel.messages.isEmpty { + emptyState + } + + ForEach(viewModel.messages) { message in + messageRow(for: message) + .id(message.id) + .onAppear { + guard hasPositionedToBottom else { return } + viewModel.loadMoreIfNeeded(for: message) + } + } + + if let message = viewModel.errorMessage, + !message.isEmpty, + !viewModel.messages.isEmpty { + errorBanner(message: message) + } + + Color.clear + .frame(height: 1) + .id(bottomAnchorId) + .onAppear { isBottomAnchorVisible = true } + .onDisappear { isBottomAnchorVisible = false } + } + .padding(.vertical, 12) + } + .simultaneousGesture( + DragGesture().onChanged { value in + guard value.translation.height > 0 else { return } + isComposerFocused = false + } + ) + } + private var emptyState: some View { Text(NSLocalizedString("В чате пока нет сообщений.", comment: "")) .font(.footnote) @@ -199,37 +200,36 @@ struct PrivateChatView: View { return HStack(alignment: .bottom, spacing: 12) { if isCurrentUser { Spacer(minLength: 32) } - VStack(alignment: isCurrentUser ? .trailing : .leading, spacing: 6) { -// if !isCurrentUser { -// Text(senderName(for: message)) -// .font(.caption) -// .foregroundColor(.secondary) -// } - - HStack(alignment: .bottom) { - Text(contentText(for: message)) - .font(.body) - .foregroundColor(isCurrentUser ? .white : .primary) - .multilineTextAlignment(.leading) - - Text(timestamp(for: message)) - .font(.caption2) - .foregroundColor(isCurrentUser ? Color.white.opacity(0.8) : .secondary) - - } - } - .padding(.vertical, 10) - .padding(.horizontal, 12) - .background(isCurrentUser ? Color.accentColor : Color(.secondarySystemBackground)) - .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) - .frame(maxWidth: messageBubbleMaxWidth, alignment: isCurrentUser ? .trailing : .leading) - .fixedSize(horizontal: false, vertical: true) + messageBubble(for: message, isCurrentUser: isCurrentUser) if !isCurrentUser { Spacer(minLength: 32) } } .padding(.horizontal, 16) } + private func messageBubble(for message: MessageItem, isCurrentUser: Bool) -> some View { + let timeText = timestamp(for: message) + + return VStack(alignment: isCurrentUser ? .trailing : .leading, spacing: 4) { + Text(contentText(for: message)) + .font(.body) + .foregroundColor(isCurrentUser ? .white : .primary) + .multilineTextAlignment(.leading) + + if !timeText.isEmpty { + Text(timeText) + .font(.caption2) + .foregroundColor(isCurrentUser ? Color.white.opacity(0.85) : .secondary) + } + } + .padding(.vertical, 10) + .padding(.horizontal, 12) + .background(isCurrentUser ? Color.accentColor : Color(.secondarySystemBackground)) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + .frame(maxWidth: messageBubbleMaxWidth, alignment: isCurrentUser ? .trailing : .leading) + .fixedSize(horizontal: false, vertical: true) + } + private var messageBubbleMaxWidth: CGFloat { min(UIScreen.main.bounds.width * 0.72, 360) }