import Foundation import SwiftUI struct ConversationMessageRow: View { // @EnvironmentObject var messages: MessagesStore let message: Message @State private var offset: CGSize = .zero var body: some View { VStack(spacing: 0) { HStack(spacing: 0) { if isOutgoing() { Spacer() // MessageAttr(message: message) // .padding(.trailing, 4) } // ConversationMessageContainer(message: message, isOutgoing: isOutgoing()) // .background(isOutgoing() ? Color.Material.Shape.alternate : Color.Material.Shape.white) // .clipShape(ConversationMessageBubble(isOutgoing: isOutgoing())) if !isOutgoing() { // MessageAttr(message: message) // .padding(.leading, 4) Spacer() } } .padding(.vertical, 10) .padding(.horizontal, 16) .background(Color.clearTappable) .offset(offset) .gesture( DragGesture(minimumDistance: 30, coordinateSpace: .local) .onChanged { value in var width = value.translation.width width = width > 0 ? 0 : width offset = CGSize(width: width, height: 0) } .onEnded { value in let targetWidth: CGFloat = -90 withAnimation(.easeOut(duration: 0.1)) { if value.translation.width <= targetWidth { Vibration.success.vibrate() DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { withAnimation(.easeOut(duration: 0.1)) { offset = .zero } } } else { offset = .zero } } if value.translation.width <= targetWidth { DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) { // messages.replyText = message.body ?? "" } } } ) } .listRowInsets(.zero) .listRowSeparator(.hidden) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.Material.Background.light) } private func isOutgoing() -> Bool { true // message.from == messages.roster.bareJid } } struct ConversationMessageBubble: Shape { let isOutgoing: Bool func path(in rect: CGRect) -> Path { let path = UIBezierPath( roundedRect: rect, byRoundingCorners: isOutgoing ? [.topLeft, .bottomLeft, .bottomRight] : [.topRight, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: 8, height: 10) ) return Path(path.cgPath) } }