@@ -11,6 +11,7 @@ import SwiftUI
11
11
struct MessageContainerView < Factory: ViewFactory > : View {
12
12
@Injected ( \. fonts) private var fonts
13
13
@Injected ( \. colors) private var colors
14
+ @Injected ( \. images) private var images
14
15
15
16
var factory : Factory
16
17
let channel : ChatChannel
@@ -20,10 +21,15 @@ struct MessageContainerView<Factory: ViewFactory>: View {
20
21
var showsAllInfo : Bool
21
22
var isInThread : Bool
22
23
@Binding var scrolledId : String ?
24
+ @Binding var quotedMessage : ChatMessage ?
23
25
var onLongPress : ( MessageDisplayInfo ) -> Void
24
26
25
27
@State private var frame : CGRect = . zero
26
28
@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
27
33
28
34
var body : some View {
29
35
HStack ( alignment: . bottom) {
@@ -41,6 +47,14 @@ struct MessageContainerView<Factory: ViewFactory>: View {
41
47
}
42
48
}
43
49
50
+ if offsetX > 0 {
51
+ VStack {
52
+ Image ( uiImage: images. messageActionInlineReply)
53
+ Spacer ( )
54
+ }
55
+ . padding ( . horizontal)
56
+ }
57
+
44
58
VStack ( alignment: message. isSentByCurrentUser ? . trailing : . leading) {
45
59
MessageView (
46
60
factory: factory,
@@ -81,6 +95,37 @@ struct MessageContainerView<Factory: ViewFactory>: View {
81
95
}
82
96
83
97
} )
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
+ } )
84
129
85
130
if message. replyCount > 0 && !message. threadParticipants. isEmpty && !isInThread {
86
131
MessageRepliesView (
@@ -124,6 +169,34 @@ struct MessageContainerView<Factory: ViewFactory>: View {
124
169
private var reactionsShown : Bool {
125
170
!message. reactionScores. isEmpty && !message. isDeleted
126
171
}
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
+ }
127
200
}
128
201
129
202
struct MessageAuthorAndDateView : View {
0 commit comments