2024-06-27 11:39:41 +00:00
|
|
|
import Foundation
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
struct ConversationMessageRow: View {
|
2024-09-03 15:13:58 +00:00
|
|
|
@EnvironmentObject var messages: MessagesStore
|
2024-06-27 11:39:41 +00:00
|
|
|
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())
|
2024-07-04 08:21:12 +00:00
|
|
|
.background(isOutgoing() ? Color.Material.Shape.alternate : Color.Material.Shape.white)
|
2024-06-27 11:39:41 +00:00
|
|
|
.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(
|
2024-07-01 11:54:43 +00:00
|
|
|
DragGesture(minimumDistance: 30, coordinateSpace: .local)
|
2024-07-01 08:47:39 +00:00
|
|
|
.onChanged { value in
|
|
|
|
var width = value.translation.width
|
|
|
|
width = width > 0 ? 0 : width
|
|
|
|
offset = CGSize(width: width, height: 0)
|
|
|
|
}
|
2024-06-27 11:39:41 +00:00
|
|
|
.onEnded { value in
|
2024-07-04 13:40:32 +00:00
|
|
|
let targetWidth: CGFloat = -90
|
2024-06-27 11:39:41 +00:00
|
|
|
withAnimation(.easeOut(duration: 0.1)) {
|
2024-07-04 13:40:32 +00:00
|
|
|
if value.translation.width <= targetWidth {
|
2024-06-27 11:39:41 +00:00
|
|
|
Vibration.success.vibrate()
|
2024-07-01 08:47:39 +00:00
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
2024-06-27 11:39:41 +00:00
|
|
|
withAnimation(.easeOut(duration: 0.1)) {
|
|
|
|
offset = .zero
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
offset = .zero
|
|
|
|
}
|
|
|
|
}
|
2024-07-04 13:40:32 +00:00
|
|
|
if value.translation.width <= targetWidth {
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
|
2024-09-03 15:13:58 +00:00
|
|
|
messages.replyText = message.body ?? ""
|
2024-07-04 13:40:32 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-27 11:39:41 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2024-08-07 19:07:39 +00:00
|
|
|
.listRowInsets(.zero)
|
|
|
|
.listRowSeparator(.hidden)
|
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
.background(Color.Material.Background.light)
|
2024-06-27 11:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private func isOutgoing() -> Bool {
|
2024-09-03 15:13:58 +00:00
|
|
|
message.from == messages.roster.bareJid
|
2024-06-27 11:39:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|