Skip to content

Commit a1dfe8a

Browse files
implemented swipe to reply feature
1 parent 0304829 commit a1dfe8a

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public struct ChatChannelView<Factory: ViewFactory>: View {
3838
messagesGroupingInfo: viewModel.messagesGroupingInfo,
3939
scrolledId: $viewModel.scrolledId,
4040
showScrollToLatestButton: $viewModel.showScrollToLatestButton,
41+
quotedMessage: $viewModel.quotedMessage,
4142
currentDateString: viewModel.currentDateString,
4243
isGroup: !viewModel.channel.isDirectMessageChannel,
4344
unreadCount: viewModel.channel.unreadCount.messages,

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import SwiftUI
1111
struct MessageContainerView<Factory: ViewFactory>: View {
1212
@Injected(\.fonts) private var fonts
1313
@Injected(\.colors) private var colors
14+
@Injected(\.images) private var images
1415

1516
var factory: Factory
1617
let channel: ChatChannel
@@ -20,10 +21,15 @@ struct MessageContainerView<Factory: ViewFactory>: View {
2021
var showsAllInfo: Bool
2122
var isInThread: Bool
2223
@Binding var scrolledId: String?
24+
@Binding var quotedMessage: ChatMessage?
2325
var onLongPress: (MessageDisplayInfo) -> Void
2426

2527
@State private var frame: CGRect = .zero
2628
@State private var computeFrame = false
29+
@State private var offsetX: CGFloat = 0
30+
@GestureState private var offset: CGSize = .zero
31+
32+
private let replyThreshold: CGFloat = 60
2733

2834
var body: some View {
2935
HStack(alignment: .bottom) {
@@ -41,6 +47,14 @@ struct MessageContainerView<Factory: ViewFactory>: View {
4147
}
4248
}
4349

50+
if offsetX > 0 {
51+
VStack {
52+
Image(uiImage: images.messageActionInlineReply)
53+
Spacer()
54+
}
55+
.padding(.horizontal)
56+
}
57+
4458
VStack(alignment: message.isSentByCurrentUser ? .trailing : .leading) {
4559
MessageView(
4660
factory: factory,
@@ -81,6 +95,37 @@ struct MessageContainerView<Factory: ViewFactory>: View {
8195
}
8296

8397
})
98+
.offset(x: self.offsetX)
99+
.simultaneousGesture(
100+
DragGesture(
101+
minimumDistance: 10,
102+
coordinateSpace: .local
103+
)
104+
.updating($offset) { (value, gestureState, _) in
105+
if message.isDeleted {
106+
return
107+
}
108+
// Using updating since onEnded is not called if the gesture is canceled.
109+
let diff = CGSize(
110+
width: value.location.x - value.startLocation.x,
111+
height: value.location.y - value.startLocation.y
112+
)
113+
114+
if diff == .zero {
115+
gestureState = .zero
116+
} else {
117+
gestureState = value.translation
118+
}
119+
}
120+
)
121+
.onChange(of: offset, perform: { _ in
122+
if offset == .zero {
123+
// gesture ended or cancelled
124+
setOffsetX(value: 0)
125+
} else {
126+
dragChanged(to: offset.width)
127+
}
128+
})
84129

85130
if message.replyCount > 0 && !message.threadParticipants.isEmpty && !isInThread {
86131
MessageRepliesView(
@@ -124,6 +169,34 @@ struct MessageContainerView<Factory: ViewFactory>: View {
124169
private var reactionsShown: Bool {
125170
!message.reactionScores.isEmpty && !message.isDeleted
126171
}
172+
173+
private func dragChanged(to value: CGFloat) {
174+
let horizontalTranslation = value
175+
176+
if horizontalTranslation < 0 {
177+
// prevent swiping to right.
178+
return
179+
}
180+
181+
if horizontalTranslation > 0 {
182+
offsetX = horizontalTranslation
183+
} else {
184+
offsetX = 0
185+
}
186+
187+
if offsetX > replyThreshold && quotedMessage != message {
188+
triggerHapticFeedback(style: .medium)
189+
withAnimation {
190+
quotedMessage = message
191+
}
192+
}
193+
}
194+
195+
private func setOffsetX(value: CGFloat) {
196+
withAnimation {
197+
self.offsetX = value
198+
}
199+
}
127200
}
128201

129202
struct MessageAuthorAndDateView: View {

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
1414
var messagesGroupingInfo: [String: [String]]
1515
@Binding var scrolledId: String?
1616
@Binding var showScrollToLatestButton: Bool
17+
@Binding var quotedMessage: ChatMessage?
1718
var currentDateString: String?
1819
var isGroup: Bool
1920
var unreadCount: Int
@@ -60,6 +61,7 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
6061
showsAllInfo: showsAllData(for: message),
6162
isInThread: isMessageThread,
6263
scrolledId: $scrolledId,
64+
quotedMessage: $quotedMessage,
6365
onLongPress: { messageDisplayInfo in
6466
if keyboardShown {
6567
resignFirstResponder()

0 commit comments

Comments
 (0)