Compare commits
No commits in common. "2396a707ec4c52490982cd375b30622382e1b9b3" and "b1d91128063c1c53145952f56f958f37ad019a2e" have entirely different histories.
2396a707ec
...
b1d9112806
@ -61,20 +61,20 @@ struct PrivateChatView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle(toolbarTitle)
|
.navigationTitle(toolbarTitle)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
// .navigationBarBackButtonHidden(true)
|
.navigationBarBackButtonHidden(true)
|
||||||
// .toolbar {
|
// .toolbar {
|
||||||
// ToolbarItem(placement: .principal) {
|
// ToolbarItem(placement: .principal) {
|
||||||
// chatToolbarContent
|
// chatToolbarContent
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
.toolbar {
|
.toolbar {
|
||||||
// ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
// Button(action: { dismiss() }) {
|
Button(action: { dismiss() }) {
|
||||||
// Image(systemName: "chevron.left")
|
Image(systemName: "chevron.left")
|
||||||
// .font(.system(size: 17, weight: .semibold))
|
.font(.system(size: 17, weight: .semibold))
|
||||||
// .foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
Button(action: openProfile) {
|
Button(action: openProfile) {
|
||||||
@ -117,48 +117,47 @@ struct PrivateChatView: View {
|
|||||||
} else if let error = viewModel.errorMessage, viewModel.messages.isEmpty {
|
} else if let error = viewModel.errorMessage, viewModel.messages.isEmpty {
|
||||||
errorView(message: error)
|
errorView(message: error)
|
||||||
} else {
|
} else {
|
||||||
messagesList
|
ScrollView {
|
||||||
}
|
LazyVStack(alignment: .leading, spacing: 12) {
|
||||||
}
|
if viewModel.isLoadingMore {
|
||||||
|
loadingMoreView
|
||||||
|
} else if viewModel.messages.isEmpty {
|
||||||
|
emptyState
|
||||||
|
}
|
||||||
|
|
||||||
private var messagesList: some View {
|
ForEach(viewModel.messages) { message in
|
||||||
ScrollView {
|
messageRow(for: message)
|
||||||
LazyVStack(alignment: .leading, spacing: 12) {
|
.id(message.id)
|
||||||
if viewModel.isLoadingMore {
|
.onAppear {
|
||||||
loadingMoreView
|
guard hasPositionedToBottom else { return }
|
||||||
} else if viewModel.messages.isEmpty {
|
viewModel.loadMoreIfNeeded(for: message)
|
||||||
emptyState
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
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
|
||||||
.simultaneousGesture(
|
guard value.translation.height > 0 else { return }
|
||||||
DragGesture().onChanged { value in
|
isComposerFocused = false
|
||||||
guard value.translation.height > 0 else { return }
|
}
|
||||||
isComposerFocused = false
|
)
|
||||||
|
.refreshable {
|
||||||
|
viewModel.refresh()
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var emptyState: some View {
|
private var emptyState: some View {
|
||||||
@ -200,36 +199,37 @@ struct PrivateChatView: View {
|
|||||||
return HStack(alignment: .bottom, spacing: 12) {
|
return HStack(alignment: .bottom, spacing: 12) {
|
||||||
if isCurrentUser { Spacer(minLength: 32) }
|
if isCurrentUser { Spacer(minLength: 32) }
|
||||||
|
|
||||||
messageBubble(for: message, isCurrentUser: isCurrentUser)
|
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)
|
||||||
|
|
||||||
if !isCurrentUser { Spacer(minLength: 32) }
|
if !isCurrentUser { Spacer(minLength: 32) }
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 16)
|
.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 {
|
private var messageBubbleMaxWidth: CGFloat {
|
||||||
min(UIScreen.main.bounds.width * 0.72, 360)
|
min(UIScreen.main.bounds.width * 0.72, 360)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user