Skip to content

Commit 7ed3660

Browse files
committed
Fix extra height animations causes by reaction size calculations
1 parent 2ea8fc5 commit 7ed3660

10 files changed

Lines changed: 65 additions & 33 deletions

Sources/ExyteChat/Model/Reaction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public enum ReactionType: Codable, Equatable, Hashable, Sendable {
1010
//case sticker(Image / Giphy / Memoji)
1111
//case other...
1212

13-
var toString:String {
13+
var toString: String {
1414
switch self {
1515
case .emoji(let emoji):
1616
return emoji

Sources/ExyteChat/Utils/CachedAsyncImage.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public struct CachedAsyncImage<Content>: View where Content: View {
9696
) { result in
9797
switch result {
9898
case .success(let value):
99-
print("[CachedAsyncImage] Loaded image from: \(value.cacheType)")
99+
//print("[CachedAsyncImage] Loaded image from: \(value.cacheType)")
100100
continuation.resume(returning: value.image)
101101
case .failure(let error):
102102
print("[CachedAsyncImage] Failed to load image: \(error)")
@@ -120,4 +120,4 @@ public struct CachedAsyncImage<Content>: View where Content: View {
120120
}
121121
}
122122
}
123-
}
123+
}

Sources/ExyteChat/Views/ChatView.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public struct ChatView<MessageContent: View, InputViewContent: View, MenuAction:
6868
var mainHeaderBuilder: (()->AnyView)?
6969

7070
/// date section header builder
71-
var headerBuilder: ((Date)->AnyView)?
71+
var dateHeaderBuilder: ((Date)->AnyView)?
7272

7373
/// content to display in between the chat list view and the input view
7474
var betweenListAndInputViewBuilder: (()->AnyView)?
@@ -96,6 +96,7 @@ public struct ChatView<MessageContent: View, InputViewContent: View, MenuAction:
9696
@State private var tableContentHeight: CGFloat = 0
9797
@State private var inputViewSize = CGSize.zero
9898
@State private var timeViewSize = CGSize.zero
99+
@State private var reactionViewSize = CGSize.zero
99100
@State private var cellFrames = [String: CGRect]()
100101

101102
@State private var giphyConfigured = false
@@ -184,10 +185,15 @@ public struct ChatView<MessageContent: View, InputViewContent: View, MenuAction:
184185
.background {
185186
// assume all the time views have same width, like "00:00"
186187
if let anyMessage = sections.first?.rows.first?.message, timeViewSize == .zero {
187-
FinalMeasuringTrickView(size: $timeViewSize, id: "uu") {
188+
FinalMeasuringTrickView(size: $timeViewSize) {
188189
MessageTimeView(text: anyMessage.time, userType: anyMessage.user.type)
189190
}
190191
}
192+
if let anyMessage = sections.first?.rows.first?.message, reactionViewSize == .zero {
193+
FinalMeasuringTrickView(size: $reactionViewSize) {
194+
ReactionBubble(reaction: Reaction(id: "0", user: anyMessage.user, createdAt: anyMessage.createdAt, type: .emoji("🙃️️️️"), status: .sent), font: messageCustomizationParameters.font)
195+
}
196+
}
191197
}
192198
}
193199

@@ -277,7 +283,7 @@ public struct ChatView<MessageContent: View, InputViewContent: View, MenuAction:
277283

278284
messageBuilder: messageBuilder,
279285
mainHeaderBuilder: mainHeaderBuilder,
280-
headerBuilder: headerBuilder,
286+
dateHeaderBuilder: dateHeaderBuilder,
281287

282288
// MARK: - Data / type
283289

@@ -289,7 +295,8 @@ public struct ChatView<MessageContent: View, InputViewContent: View, MenuAction:
289295

290296
chatParams: chatCustomizationParameters,
291297
messageParams: messageCustomizationParameters,
292-
timeViewWidth: $timeViewSize.width
298+
timeViewWidth: $timeViewSize.width,
299+
reactionViewWidth: $reactionViewSize.width
293300
)
294301
.applyIf(!chatCustomizationParameters.isScrollEnabled) {
295302
$0.frame(height: tableContentHeight)
@@ -397,6 +404,7 @@ public struct ChatView<MessageContent: View, InputViewContent: View, MenuAction:
397404
chatType: type,
398405
messageParams: messageCustomizationParameters,
399406
timeViewWidth: $timeViewSize.width,
407+
reactionViewWidth: $reactionViewSize.width,
400408
isDisplayingMessageMenu: true
401409
)
402410
.onTapGesture {

Sources/ExyteChat/Views/MessageView/ChatMessageView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct ChatMessageView<MessageContent: View>: View {
1919
let chatType: ChatType
2020
let messageParams: MessageCustomizationParameters
2121
@Binding var timeViewWidth: CGFloat
22+
@Binding var reactionViewWidth: CGFloat
2223
let isDisplayingMessageMenu: Bool
2324

2425
@State var timeViewSize: CGSize?
@@ -48,6 +49,7 @@ struct ChatMessageView<MessageContent: View>: View {
4849
chatType: chatType,
4950
params: messageParams,
5051
timeViewWidth: $timeViewWidth,
52+
reactionViewWidth: $reactionViewWidth,
5153
isDisplayingMessageMenu: isDisplayingMessageMenu
5254
)
5355
} else {
@@ -56,7 +58,7 @@ struct ChatMessageView<MessageContent: View>: View {
5658
.environment(\.chatMessageType, chatType)
5759
.environment(\.messageCustomizationParams, messageParams)
5860
.environment(\.timeViewWidthBinding, $timeViewWidth)
59-
.environment(\.isDisplayingMessageMenu, isDisplayingMessageMenu)
61+
.environment(\.reactionViewWidthBinding, $reactionViewWidth)
6062
}
6163
}
6264
.id(row.message.id)

Sources/ExyteChat/Views/MessageView/DefaultMessageView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct DefaultMessageView: View {
77
@Environment(\.chatMessageType) private var chatType
88
@Environment(\.messageCustomizationParams) private var customizationParams
99
@Environment(\.timeViewWidthBinding) private var timeViewWidth
10-
@Environment(\.isDisplayingMessageMenu) private var isDisplayingMessageMenu
10+
@Environment(\.reactionViewWidthBinding) private var reactionViewWidth
1111

1212
init(params: MessageBuilderParameters) {
1313
self.params = params
@@ -22,7 +22,8 @@ struct DefaultMessageView: View {
2222
chatType: chatType,
2323
params: customizationParams,
2424
timeViewWidth: timeViewWidth,
25-
isDisplayingMessageMenu: isDisplayingMessageMenu
25+
reactionViewWidth: reactionViewWidth,
26+
isDisplayingMessageMenu: false
2627
)
2728
}
2829
}

Sources/ExyteChat/Views/MessageView/MessageReactionView.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extension MessageView {
1212
let preparedReactions = prepareReactions(message: message, maxReactions: maxReactions)
1313
let overflowBubbleText = "+\(message.reactions.count - maxReactions + 1)"
1414

15-
HStack(spacing: -bubbleSize.width / 5) {
15+
HStack(spacing: -reactionViewWidth / 5) {
1616
if !message.user.isCurrentUser {
1717
overflowBubbleView(
1818
leadingSpacer: true,
@@ -23,10 +23,9 @@ extension MessageView {
2323
}
2424

2525
ForEach(Array(preparedReactions.reactions.enumerated()), id: \.element) { index, reaction in
26-
ReactionBubble(reaction: reaction, font: Font(params.font))
26+
ReactionBubble(reaction: reaction, font: params.font)
2727
.transition(.scaleAndFade)
2828
.zIndex(message.user.isCurrentUser ? Double(preparedReactions.reactions.count - index) : Double(index + 1))
29-
.sizeGetter($bubbleSize)
3029
}
3130

3231
if message.user.isCurrentUser {
@@ -39,7 +38,7 @@ extension MessageView {
3938
}
4039
}
4140
.offset(
42-
x: message.user.isCurrentUser ? -(bubbleSize.height / 2) : (bubbleSize.height / 2),
41+
x: message.user.isCurrentUser ? -(reactionViewWidth / 2) : (reactionViewWidth / 2),
4342
y: 0
4443
)
4544
}
@@ -105,7 +104,17 @@ struct ReactionBubble: View {
105104

106105
let reaction: Reaction
107106
let font: Font
108-
107+
108+
init(reaction: Reaction, font: UIFont) {
109+
self.reaction = reaction
110+
self.font = Font(font)
111+
}
112+
113+
init(reaction: Reaction, font: Font) {
114+
self.reaction = reaction
115+
self.font = font
116+
}
117+
109118
@State private var phase = 0.0
110119

111120
var fillColor: Color {

Sources/ExyteChat/Views/MessageView/MessageView.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,9 @@ struct MessageView: View {
1919
let chatType: ChatType
2020
let params: MessageCustomizationParameters
2121
@Binding var timeViewWidth: CGFloat // hack to pass real updates through UIKit
22+
@Binding var reactionViewWidth: CGFloat // hack to pass real updates through UIKit
2223
let isDisplayingMessageMenu: Bool
2324

24-
// The size of our reaction bubbles are based on the users font size,
25-
// Therefore we need to capture its rendered size in order to place it correctly
26-
@State var bubbleSize: CGSize = .zero
27-
2825
@State var giphyAspectRatio: CGFloat = 1
2926

3027
static let widthWithMedia: CGFloat = 204
@@ -144,7 +141,7 @@ struct MessageView: View {
144141
func bubbleView(_ message: Message) -> some View {
145142
VStack(
146143
alignment: message.user.isCurrentUser ? .leading : .trailing,
147-
spacing: -bubbleSize.height / 3
144+
spacing: -reactionViewWidth / 3
148145
) {
149146
if !isDisplayingMessageMenu && !message.reactions.isEmpty {
150147
reactionsView(message)

Sources/ExyteChat/Views/MessageView/MessageViewEnvironment.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ private struct TimeViewWidthBindingEnvironmentKey: EnvironmentKey {
1212
static let defaultValue: Binding<CGFloat> = .constant(0)
1313
}
1414

15-
private struct IsDisplayingMessageMenuEnvironmentKey: EnvironmentKey {
16-
static let defaultValue = false
15+
private struct ReactionViewWidthBindingEnvironmentKey: EnvironmentKey {
16+
static let defaultValue: Binding<CGFloat> = .constant(0)
1717
}
1818

1919
extension EnvironmentValues {
@@ -32,8 +32,8 @@ extension EnvironmentValues {
3232
set { self[TimeViewWidthBindingEnvironmentKey.self] = newValue }
3333
}
3434

35-
var isDisplayingMessageMenu: Bool {
36-
get { self[IsDisplayingMessageMenuEnvironmentKey.self] }
37-
set { self[IsDisplayingMessageMenuEnvironmentKey.self] = newValue }
35+
var reactionViewWidthBinding: Binding<CGFloat> {
36+
get { self[ReactionViewWidthBindingEnvironmentKey.self] }
37+
set { self[ReactionViewWidthBindingEnvironmentKey.self] = newValue }
3838
}
3939
}

Sources/ExyteChat/Views/PublicAPI.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,18 @@ public extension ChatView {
2121
return view
2222
}
2323

24+
func dateHeaderBuilder<V: View>(_ builder: @escaping (Date)->V) -> ChatView {
25+
var view = self
26+
view.dateHeaderBuilder = { date in
27+
AnyView(builder(date))
28+
}
29+
return view
30+
}
31+
32+
@available(*, deprecated, message: "use dateHeaderBuilder instead")
2433
func headerBuilder<V: View>(_ builder: @escaping (Date)->V) -> ChatView {
2534
var view = self
26-
view.headerBuilder = { date in
35+
view.dateHeaderBuilder = { date in
2736
AnyView(builder(date))
2837
}
2938
return view

Sources/ExyteChat/Views/UIList.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
2929

3030
let messageBuilder: MessageBuilderParamsClosure
3131
let mainHeaderBuilder: (()->AnyView)?
32-
let headerBuilder: ((Date)->AnyView)?
32+
let dateHeaderBuilder: ((Date)->AnyView)?
3333

3434
// MARK: - Data / type
3535

@@ -42,6 +42,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
4242
let chatParams: ChatCustomizationParameters
4343
let messageParams: MessageCustomizationParameters
4444
@Binding var timeViewWidth: CGFloat
45+
@Binding var reactionViewWidth: CGFloat
4546

4647
// MARK: - State
4748

@@ -456,7 +457,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
456457

457458
messageBuilder: messageBuilder,
458459
mainHeaderBuilder: mainHeaderBuilder,
459-
headerBuilder: headerBuilder,
460+
dateHeaderBuilder: dateHeaderBuilder,
460461

461462
type: type,
462463
sections: sections,
@@ -465,6 +466,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
465466
chatParams: chatParams,
466467
messageParams: messageParams,
467468
timeViewWidth: $timeViewWidth,
469+
reactionViewWidth: $reactionViewWidth,
468470
mainBackgroundColor: theme.colors.mainBG
469471
)
470472
}
@@ -481,7 +483,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
481483

482484
let messageBuilder: MessageBuilderParamsClosure
483485
let mainHeaderBuilder: (()->AnyView)?
484-
let headerBuilder: ((Date)->AnyView)?
486+
let dateHeaderBuilder: ((Date)->AnyView)?
485487

486488
// MARK: - Data / type
487489

@@ -500,6 +502,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
500502
let chatParams: ChatCustomizationParameters
501503
let messageParams: MessageCustomizationParameters
502504
@Binding var timeViewWidth: CGFloat
505+
@Binding var reactionViewWidth: CGFloat
503506
let mainBackgroundColor: Color
504507

505508
/// call pagination handler when this row is reached
@@ -516,7 +519,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
516519

517520
messageBuilder: @escaping MessageBuilderParamsClosure,
518521
mainHeaderBuilder: (() -> AnyView)?,
519-
headerBuilder: ((Date) -> AnyView)?,
522+
dateHeaderBuilder: ((Date) -> AnyView)?,
520523

521524
type: ChatType,
522525
sections: [MessagesSection],
@@ -525,6 +528,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
525528
chatParams: ChatCustomizationParameters,
526529
messageParams: MessageCustomizationParameters,
527530
timeViewWidth: Binding<CGFloat>,
531+
reactionViewWidth: Binding<CGFloat>,
528532
mainBackgroundColor: Color
529533
) {
530534
self.viewModel = viewModel
@@ -534,7 +538,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
534538

535539
self.messageBuilder = messageBuilder
536540
self.mainHeaderBuilder = mainHeaderBuilder
537-
self.headerBuilder = headerBuilder
541+
self.dateHeaderBuilder = dateHeaderBuilder
538542

539543
self.type = type
540544
self.sections = sections
@@ -543,6 +547,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
543547
self.chatParams = chatParams
544548
self.messageParams = messageParams
545549
self._timeViewWidth = timeViewWidth
550+
self._reactionViewWidth = reactionViewWidth
546551
self.mainBackgroundColor = mainBackgroundColor
547552
}
548553

@@ -610,8 +615,8 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
610615
@ViewBuilder
611616
func dateViewBuilder(_ section: Int) -> some View {
612617
if chatParams.showDateHeaders {
613-
if let headerBuilder {
614-
headerBuilder(sections[section].date)
618+
if let dateHeaderBuilder {
619+
dateHeaderBuilder(sections[section].date)
615620
} else {
616621
Text(sections[section].formattedDate)
617622
.font(.system(size: 11))
@@ -636,6 +641,7 @@ struct UIList<MessageContent: View>: UIViewRepresentable {
636641
chatType: type,
637642
messageParams: messageParams,
638643
timeViewWidth: $timeViewWidth,
644+
reactionViewWidth: $reactionViewWidth,
639645
isDisplayingMessageMenu: false
640646
)
641647
.background(MessageMenuPreferenceViewSetter(id: row.id))

0 commit comments

Comments
 (0)