178 lines
6.1 KiB
Swift
178 lines
6.1 KiB
Swift
//
|
|
// ChatView.swift
|
|
// Training
|
|
//
|
|
// Created by mrtuxa on 28.08.22.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
|
|
struct ChatView: View {
|
|
|
|
@EnvironmentObject var viewModel: ChatsViewModel
|
|
let chat: Chat
|
|
|
|
@State private var text = ""
|
|
@FocusState private var isFocused
|
|
|
|
@State private var messageIDToScroll: UUID?
|
|
|
|
var navBarLeadingBtn: some View {
|
|
Button(action: {}) {
|
|
HStack {
|
|
Image(chat.person.imgString)
|
|
.resizable()
|
|
.frame(width: 40, height: 40)
|
|
.clipShape(Circle())
|
|
|
|
Text(chat.person.name).bold()
|
|
}.foregroundColor(.black)
|
|
}
|
|
}
|
|
|
|
var navBarTrailingBtn: some View {
|
|
HStack {
|
|
Button(action: {}) {
|
|
Image(systemName: "video")
|
|
}
|
|
Button(action: {}) {
|
|
Image(systemName: "phone")
|
|
}
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
VStack {
|
|
GeometryReader { reader in
|
|
ScrollView {
|
|
ScrollViewReader { scrollReader in
|
|
getMessageView(viewWidth: reader.size.width)
|
|
.padding(.horizontal)
|
|
.onChange(of: messageIDToScroll) { _ in
|
|
if let messageID = messageIDToScroll {
|
|
scrollTo(messageID: messageID, shouldAnimate: true, scrollReader: scrollReader)
|
|
}
|
|
}
|
|
.onAppear {
|
|
if let messageID = chat.messages.last?.id {
|
|
scrollTo(messageID: messageID, anchor: .bottom, shouldAnimate: false, scrollReader: scrollReader)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
.padding(.bottom, 5)
|
|
|
|
toolbarView()
|
|
}
|
|
.padding(.top, 1)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.navigationBarItems(leading: navBarLeadingBtn, trailing: navBarTrailingBtn)
|
|
.onAppear {
|
|
viewModel.markAsUnread(false, chat: chat)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func scrollTo(messageID: UUID, anchor: UnitPoint? = nil, shouldAnimate: Bool, scrollReader: ScrollViewProxy) {
|
|
DispatchQueue.main.async {
|
|
withAnimation(shouldAnimate ? Animation.easeIn : nil) {
|
|
scrollReader.scrollTo(messageID, anchor: anchor)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
func toolbarView() -> some View {
|
|
VStack {
|
|
let height: CGFloat = 37
|
|
HStack {
|
|
TextField("Message ...", text: $text)
|
|
.padding(.horizontal, 10)
|
|
.frame(height: height)
|
|
.background(Color(UIColor.lightGray))
|
|
.clipShape(RoundedRectangle(cornerRadius: 13))
|
|
.focused($isFocused)
|
|
|
|
Button(action: sendMessage) {
|
|
Image(systemName: "paperplane.fill")
|
|
.foregroundColor(.white)
|
|
.frame(width: height, height: height)
|
|
.background(
|
|
Circle()
|
|
.foregroundColor(text.isEmpty ? .blue : .green)
|
|
)
|
|
}.disabled(text.isEmpty)
|
|
}.frame(height: height)
|
|
}
|
|
.padding(.vertical)
|
|
.padding(.horizontal)
|
|
.background(.thickMaterial)
|
|
}
|
|
|
|
func sendMessage() {
|
|
if let message = viewModel.sendMessage(text, in: chat) {
|
|
text = ""
|
|
messageIDToScroll = message.id
|
|
}
|
|
}
|
|
|
|
|
|
let columns = [GridItem(.flexible(minimum: 10))]
|
|
|
|
func getMessageView(viewWidth: CGFloat) -> some View {
|
|
LazyVGrid(columns: columns, spacing: 0, pinnedViews: [.sectionHeaders]) {
|
|
let sectionMessages = viewModel.getSectionMessages(for: chat)
|
|
ForEach(sectionMessages.indices, id: \.self) { sectionIndex in
|
|
let messages = sectionMessages[sectionIndex]
|
|
Section(header: sectionHeader(firstMessage: messages.first!)) {
|
|
ForEach(messages) { message in
|
|
let isReceived = message.type == .Received
|
|
HStack {
|
|
ZStack {
|
|
Text(message.text)
|
|
.padding(.horizontal)
|
|
.padding(.vertical, 12)
|
|
.background(isReceived ? Color.black.opacity(0.2) : .green.opacity(0.9))
|
|
.cornerRadius(13)
|
|
}
|
|
.background(Color.blue)
|
|
.padding(.vertical)
|
|
.frame(width: viewWidth * 0.7, alignment: isReceived ? .leading : .trailing)
|
|
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: isReceived ? .leading : .trailing)
|
|
.id(message.id) // important for automatic scrolling later!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func sectionHeader(firstMessage message: Message) -> some View {
|
|
ZStack {
|
|
Text(message.date.descriptiveString(dateStyle: .medium))
|
|
.foregroundColor(.white)
|
|
.font(.system(size: 14, weight: .regular))
|
|
.frame(width: 120)
|
|
.padding(.vertical, 5)
|
|
.background(Capsule().foregroundColor(.cyan))
|
|
}
|
|
.padding(.vertical, 5)
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|
|
|
|
|
|
struct ChatView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
NavigationView {
|
|
ChatView(chat: Chat.sampleChat[0])
|
|
.environmentObject(ChatsViewModel())
|
|
}
|
|
}
|
|
}
|