@@ -14,6 +14,10 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
1414 @State private var popIn = false
1515 @State private var willPopOut = false
1616 @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 ?
1721
1822 var factory : Factory
1923 var channel : ChatChannel
@@ -61,11 +65,17 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
6165
6266 public var body : some View {
6367 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+ }
6979 . transition ( . opacity)
7080 . onTapGesture {
7181 dismissReactionsOverlay ( ) { /* No additional handling. */ }
@@ -90,7 +100,10 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
90100 GeometryReader { reader in
91101 let frame = reader. frame ( in: . local)
92102 let height = frame. height
103+ let width = frame. width
93104 Color . clear. preference ( key: HeightPreferenceKey . self, value: height)
105+ Color . clear. preference ( key: WidthPreferenceKey . self, value: width)
106+
94107 VStack ( alignment: . leading) {
95108 Group {
96109 if messageDisplayInfo. frame. height > messageContainerHeight {
@@ -116,7 +129,7 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
116129 . scaleEffect ( popIn || willPopOut ? 1 : 0.95 )
117130 . animation ( willPopOut ? . easeInOut : popInAnimation, value: popIn)
118131 . offset (
119- x: messageDisplayInfo . frame . origin . x - diffWidth ( proxy: reader)
132+ x: messageOriginX ( proxy: reader)
120133 )
121134 . overlay (
122135 channel. config. reactionsEnabled ?
@@ -133,7 +146,7 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
133146 . opacity ( willPopOut ? 0 : 1 )
134147 . animation ( willPopOut ? . easeInOut : popInAnimation, value: popIn)
135148 . offset (
136- x: messageDisplayInfo . frame . origin . x - diffWidth ( proxy: reader) ,
149+ x: messageOriginX ( proxy: reader) ,
137150 y: popIn ? - 24 : - messageContainerHeight / 2
138151 )
139152 . accessibilityElement ( children: . contain)
@@ -182,20 +195,34 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
182195 }
183196 }
184197 . offset ( y: !popIn ? ( messageDisplayInfo. frame. origin. y - spacing) : originY)
198+ . onAppear {
199+ self . initialOrigin = messageDisplayInfo. frame. origin. x - diffWidth( proxy: reader)
200+ }
185201 }
186202 }
187203 . onPreferenceChange ( HeightPreferenceKey . self) { value in
188204 if let value = value, value != screenHeight {
189205 self . screenHeight = value
190206 }
191207 }
208+ . onPreferenceChange ( WidthPreferenceKey . self) { value in
209+ if initialWidth == nil {
210+ initialWidth = value
211+ }
212+ self . screenWidth = value
213+ }
192214 . edgesIgnoringSafeArea ( . all)
193- . background ( Color ( colors. background) )
215+ . background ( orientationChanged ? nil : Color ( colors. background) )
194216 . onAppear {
195217 popIn = true
196218 }
197219 . accessibilityElement ( children: . contain)
198220 . accessibilityIdentifier ( " ReactionsOverlayView " )
221+ . onRotate { orientation in
222+ if isIPad {
223+ self . orientationChanged = true
224+ }
225+ }
199226 }
200227
201228 private func dismissReactionsOverlay( completion: @escaping ( ) -> Void ) {
@@ -215,11 +242,21 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
215242 if popIn {
216243 return originX
217244 } else if willPopOut {
218- return messageDisplayInfo . frame . origin . x - diffWidth ( proxy: reader)
245+ return messageOriginX ( proxy: reader)
219246 } else {
220247 return sentByCurrentUser ? messageActionsWidth : 0
221248 }
222249 }
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+ }
223260
224261 private var messageContainerHeight : CGFloat {
225262 let maxAllowed = screenHeight / 2
@@ -310,3 +347,21 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
310347 return width
311348 }
312349}
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