multiline message entry field (#270)

This commit is contained in:
Evgeny Poberezkin
2022-02-05 14:24:23 +00:00
committed by GitHub
parent e424e9328b
commit 3d137995d8
7 changed files with 112 additions and 48 deletions

View File

@@ -55,9 +55,12 @@ final class ChatModel: ObservableObject {
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
if let ix = chats.firstIndex(where: { $0.id == cInfo.id }) {
chats[ix].chatItems = [cItem]
withAnimation {
let chat = chats.remove(at: ix)
chats.insert(chat, at: 0)
if ix > 0 {
if chatId == nil {
withAnimation { popChat(ix) }
} else {
DispatchQueue.main.async { self.popChat(ix) }
}
}
}
if chatId == cInfo.id {
@@ -67,6 +70,11 @@ final class ChatModel: ObservableObject {
}
}
private func popChat(_ ix: Int) {
let chat = chats.remove(at: ix)
chats.insert(chat, at: 0)
}
func removeChat(_ id: String) {
withAnimation {
chats.removeAll(where: { $0.id == id })

View File

@@ -15,8 +15,8 @@ struct EmojiItemView: View {
let sent = chatItem.chatDir.sent
VStack {
Text(chatItem.content.text)
.font(Font.custom("Emoji", size: 48, relativeTo: .largeTitle))
Text(chatItem.content.text.trimmingCharacters(in: .whitespaces))
.font(emojiFont)
.padding(.top, 8)
.padding(.horizontal, 6)
.frame(maxWidth: .infinity, alignment: sent ? .trailing : .leading)

View File

@@ -37,8 +37,8 @@ struct ChatItemView_Previews: PreviewProvider {
ChatItemView(chatItem: chatItemSample(1, .directSnd, .now, "hello"), width: 360)
ChatItemView(chatItem: chatItemSample(2, .directRcv, .now, "hello there too"), width: 360)
ChatItemView(chatItem: chatItemSample(1, .directSnd, .now, "🙂"), width: 360)
ChatItemView(chatItem: chatItemSample(2, .directRcv, .now, "👍👍👍"), width: 360)
ChatItemView(chatItem: chatItemSample(2, .directRcv, .now, "👍👍👍👍"), width: 360)
ChatItemView(chatItem: chatItemSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), width: 360)
ChatItemView(chatItem: chatItemSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), width: 360)
}
.previewLayout(.fixed(width: 360, height: 70))
}

View File

@@ -45,6 +45,9 @@ struct ChatView: View {
}
}
.navigationBarBackButtonHidden(true)
.onTapGesture {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
func scrollToBottom(_ proxy: ScrollViewProxy) {
@@ -76,7 +79,8 @@ struct ChatView_Previews: PreviewProvider {
chatItemSample(4, .directRcv, .now, "hello again"),
chatItemSample(5, .directSnd, .now, "hi there!!!"),
chatItemSample(6, .directSnd, .now, "how are you?"),
chatItemSample(7, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
chatItemSample(7, .directSnd, .now, "👍👍👍👍"),
chatItemSample(8, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
]
return ChatView(chatInfo: sampleDirectChatInfo)
.environmentObject(chatModel)

View File

@@ -7,6 +7,7 @@
//
import Foundation
import SwiftUI
private func isSimpleEmoji(_ c: Character) -> Bool {
guard let firstScalar = c.unicodeScalars.first else { return false }
@@ -23,5 +24,7 @@ func isEmoji(_ c: Character) -> Bool {
func isShortEmoji(_ str: String) -> Bool {
let s = str.trimmingCharacters(in: .whitespaces)
return s.count <= 3 && s.allSatisfy(isEmoji)
return s.count > 0 && s.count <= 4 && s.allSatisfy(isEmoji)
}
let emojiFont = Font.custom("Emoji", size: 48, relativeTo: .largeTitle)

View File

@@ -11,36 +11,78 @@ import SwiftUI
struct SendMessageView: View {
var sendMessage: (String) -> Void
var inProgress: Bool = false
@State var command: String = ""
@State private var message: String = "" //Lorem ipsum dolor sit amet, consectetur" // adipiscing elit, sed do eiusmod tempor incididunt ut labor7 et dolore magna aliqua. Ut enim ad minim veniam, quis"// nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."// Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
@Namespace var namespace
@State private var teHeight: CGFloat = 42
@State private var teFont: Font = .body
var maxHeight: CGFloat = 360
var minHeight: CGFloat = 37
var body: some View {
HStack {
TextField("Message...", text: $command)
.textFieldStyle(.roundedBorder)
.textInputAutocapitalization(.never)
.disableAutocorrection(true)
.onSubmit(submit)
ZStack {
HStack(alignment: .bottom) {
ZStack(alignment: .leading) {
Text(message)
.font(teFont)
.foregroundColor(.clear)
.padding(.horizontal, 10)
.padding(.vertical, 8)
.matchedGeometryEffect(id: "te", in: namespace)
.background(GeometryReader(content: updateHeight))
TextEditor(text: $message)
.onSubmit(submit)
.font(teFont)
.textInputAutocapitalization(.never)
.padding(.horizontal, 5)
.allowsTightening(false)
.frame(height: teHeight)
}
if (inProgress) {
ProgressView()
.frame(width: 40, height: 20, alignment: .center)
} else {
Button("Send", action :submit)
.disabled(command.isEmpty)
if (inProgress) {
ProgressView()
.scaleEffect(1.4)
.frame(width: 31, height: 31, alignment: .center)
.padding([.bottom, .trailing], 3)
} else {
Button(action: submit) {
Image(systemName: "arrow.up.circle.fill")
.resizable()
.foregroundColor(.accentColor)
}
.disabled(message.isEmpty)
.frame(width: 29, height: 29)
.padding([.bottom, .trailing], 4)
}
}
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)
.frame(height: teHeight)
}
.frame(minHeight: 30)
.padding(12)
.padding(.horizontal, 12)
.padding(.vertical, 8)
}
func submit() {
sendMessage(command)
command = ""
sendMessage(message)
message = ""
}
func updateHeight(_ g: GeometryProxy) -> Color {
DispatchQueue.main.async {
teHeight = min(max(g.frame(in: .local).size.height, minHeight), maxHeight)
teFont = isShortEmoji(message) ? emojiFont : .body
}
return Color.clear
}
}
struct SendMessageView_Previews: PreviewProvider {
static var previews: some View {
SendMessageView(sendMessage: { print ($0) })
VStack {
Text("")
Spacer(minLength: 0)
SendMessageView(sendMessage: { print ($0) })
}
}
}

View File

@@ -12,28 +12,35 @@ struct ContactRequestView: View {
var contactRequest: UserContactRequest
var body: some View {
return VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .top) {
Text(ChatInfo.contactRequest(contactRequest: contactRequest).chatViewName)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.blue)
.padding(.leading, 8)
.padding(.top, 4)
.frame(maxHeight: .infinity, alignment: .topLeading)
Spacer()
Text(getDateFormatter().string(from: contactRequest.createdAt))
.font(.subheadline)
.padding(.trailing, 28)
.padding(.top, 4)
.frame(minWidth: 60, alignment: .trailing)
.foregroundColor(.secondary)
return HStack(spacing: 8) {
Image(systemName: "person.crop.circle.fill")
.resizable()
.foregroundColor(Color(uiColor: .secondarySystemBackground))
.frame(width: 63, height: 63)
.padding(.leading, 4)
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .top) {
Text(ChatInfo.contactRequest(contactRequest: contactRequest).chatViewName)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.blue)
.padding(.leading, 8)
.padding(.top, 4)
.frame(maxHeight: .infinity, alignment: .topLeading)
Spacer()
Text(getDateFormatter().string(from: contactRequest.createdAt))
.font(.subheadline)
.padding(.trailing, 28)
.padding(.top, 4)
.frame(minWidth: 60, alignment: .trailing)
.foregroundColor(.secondary)
}
Text("wants to connect to you!")
.frame(minHeight: 44, maxHeight: 44, alignment: .topLeading)
.padding([.leading, .trailing], 8)
.padding(.bottom, 4)
.padding(.top, 1)
}
Text("wants to connect to you!")
.frame(minHeight: 44, maxHeight: 44, alignment: .topLeading)
.padding([.leading, .trailing], 8)
.padding(.bottom, 4)
.padding(.top, 1)
}
}
}