@@ -14,6 +14,10 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
14
14
@State private var popIn = false
15
15
@State private var willPopOut = false
16
16
@State private var screenHeight = UIScreen . main. bounds. size. height
17
+ @State private var screenWidth : CGFloat ?
18
+ @State private var initialWidth : CGFloat ?
19
+ @State private var orientationChanged = false
20
+ @State private var initialOrigin : CGFloat ?
17
21
18
22
var factory : Factory
19
23
var channel : ChatChannel
@@ -61,11 +65,17 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
61
65
62
66
public var body : some View {
63
67
ZStack ( alignment: . topLeading) {
64
- factory. makeReactionsBackgroundView (
65
- currentSnapshot: currentSnapshot,
66
- popInAnimationInProgress: !popIn
67
- )
68
- . offset ( y: spacing > 0 ? screenHeight - currentSnapshot. size. height : 0 )
68
+ ZStack {
69
+ if !orientationChanged {
70
+ factory. makeReactionsBackgroundView (
71
+ currentSnapshot: currentSnapshot,
72
+ popInAnimationInProgress: !popIn
73
+ )
74
+ . offset ( y: spacing > 0 ? screenHeight - currentSnapshot. size. height : 0 )
75
+ } else {
76
+ Color . gray. opacity ( 0.4 )
77
+ }
78
+ }
69
79
. transition ( . opacity)
70
80
. onTapGesture {
71
81
dismissReactionsOverlay ( ) { /* No additional handling. */ }
@@ -90,7 +100,10 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
90
100
GeometryReader { reader in
91
101
let frame = reader. frame ( in: . local)
92
102
let height = frame. height
103
+ let width = frame. width
93
104
Color . clear. preference ( key: HeightPreferenceKey . self, value: height)
105
+ Color . clear. preference ( key: WidthPreferenceKey . self, value: width)
106
+
94
107
VStack ( alignment: . leading) {
95
108
Group {
96
109
if messageDisplayInfo. frame. height > messageContainerHeight {
@@ -116,7 +129,7 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
116
129
. scaleEffect ( popIn || willPopOut ? 1 : 0.95 )
117
130
. animation ( willPopOut ? . easeInOut : popInAnimation, value: popIn)
118
131
. offset (
119
- x: messageDisplayInfo . frame . origin . x - diffWidth ( proxy: reader)
132
+ x: messageOriginX ( proxy: reader)
120
133
)
121
134
. overlay (
122
135
channel. config. reactionsEnabled ?
@@ -133,7 +146,7 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
133
146
. opacity ( willPopOut ? 0 : 1 )
134
147
. animation ( willPopOut ? . easeInOut : popInAnimation, value: popIn)
135
148
. offset (
136
- x: messageDisplayInfo . frame . origin . x - diffWidth ( proxy: reader) ,
149
+ x: messageOriginX ( proxy: reader) ,
137
150
y: popIn ? - 24 : - messageContainerHeight / 2
138
151
)
139
152
. accessibilityElement ( children: . contain)
@@ -182,20 +195,34 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
182
195
}
183
196
}
184
197
. offset ( y: !popIn ? ( messageDisplayInfo. frame. origin. y - spacing) : originY)
198
+ . onAppear {
199
+ self . initialOrigin = messageDisplayInfo. frame. origin. x - diffWidth( proxy: reader)
200
+ }
185
201
}
186
202
}
187
203
. onPreferenceChange ( HeightPreferenceKey . self) { value in
188
204
if let value = value, value != screenHeight {
189
205
self . screenHeight = value
190
206
}
191
207
}
208
+ . onPreferenceChange ( WidthPreferenceKey . self) { value in
209
+ if initialWidth == nil {
210
+ initialWidth = value
211
+ }
212
+ self . screenWidth = value
213
+ }
192
214
. edgesIgnoringSafeArea ( . all)
193
- . background ( Color ( colors. background) )
215
+ . background ( orientationChanged ? nil : Color ( colors. background) )
194
216
. onAppear {
195
217
popIn = true
196
218
}
197
219
. accessibilityElement ( children: . contain)
198
220
. accessibilityIdentifier ( " ReactionsOverlayView " )
221
+ . onRotate { orientation in
222
+ if isIPad {
223
+ self . orientationChanged = true
224
+ }
225
+ }
199
226
}
200
227
201
228
private func dismissReactionsOverlay( completion: @escaping ( ) -> Void ) {
@@ -215,11 +242,21 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
215
242
if popIn {
216
243
return originX
217
244
} else if willPopOut {
218
- return messageDisplayInfo . frame . origin . x - diffWidth ( proxy: reader)
245
+ return messageOriginX ( proxy: reader)
219
246
} else {
220
247
return sentByCurrentUser ? messageActionsWidth : 0
221
248
}
222
249
}
250
+
251
+ private func messageOriginX( proxy: GeometryProxy ) -> CGFloat {
252
+ let origin = messageDisplayInfo. frame. origin. x - diffWidth( proxy: proxy)
253
+ if let initialWidth, let initialOrigin, let screenWidth, abs ( initialWidth - screenWidth) > 5 {
254
+ let diff = initialWidth - initialOrigin
255
+ let newOrigin = screenWidth - diff
256
+ return newOrigin
257
+ }
258
+ return initialOrigin ?? origin
259
+ }
223
260
224
261
private var messageContainerHeight : CGFloat {
225
262
let maxAllowed = screenHeight / 2
@@ -310,3 +347,21 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
310
347
return width
311
348
}
312
349
}
350
+
351
+ struct DeviceRotationViewModifier : ViewModifier {
352
+ let action : ( UIDeviceOrientation ) -> Void
353
+
354
+ func body( content: Content ) -> some View {
355
+ content
356
+ . onAppear ( )
357
+ . onReceive ( NotificationCenter . default. publisher ( for: UIDevice . orientationDidChangeNotification) ) { _ in
358
+ action ( UIDevice . current. orientation)
359
+ }
360
+ }
361
+ }
362
+
363
+ extension View {
364
+ func onRotate( perform action: @escaping ( UIDeviceOrientation ) -> Void ) -> some View {
365
+ self . modifier ( DeviceRotationViewModifier ( action: action) )
366
+ }
367
+ }
0 commit comments