diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 7b28d8c715..ff3e78ec8b 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -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 }) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift index 04abdfd9fe..b474feee13 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift @@ -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) diff --git a/apps/ios/Shared/Views/Chat/ChatItemView.swift b/apps/ios/Shared/Views/Chat/ChatItemView.swift index 5375b3304d..01159d7fda 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemView.swift @@ -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)) } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index ce186a3986..04b40f4995 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -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) diff --git a/apps/ios/Shared/Views/Chat/Emoji.swift b/apps/ios/Shared/Views/Chat/Emoji.swift index 034c12c837..479336395e 100644 --- a/apps/ios/Shared/Views/Chat/Emoji.swift +++ b/apps/ios/Shared/Views/Chat/Emoji.swift @@ -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) diff --git a/apps/ios/Shared/Views/Chat/SendMessageView.swift b/apps/ios/Shared/Views/Chat/SendMessageView.swift index 21e69ab84d..d5d075b305 100644 --- a/apps/ios/Shared/Views/Chat/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/SendMessageView.swift @@ -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) }) + } } } diff --git a/apps/ios/Shared/Views/ChatList/ContactRequestView.swift b/apps/ios/Shared/Views/ChatList/ContactRequestView.swift index 3a9e945009..8447a253f3 100644 --- a/apps/ios/Shared/Views/ChatList/ContactRequestView.swift +++ b/apps/ios/Shared/Views/ChatList/ContactRequestView.swift @@ -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) } } }