Skip to content

Commit 30b27f1

Browse files
Implemented pinning of messages
1 parent 8d9766d commit 30b27f1

File tree

61 files changed

+413
-351
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+413
-351
lines changed

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,17 @@ extension ChatMessage: Identifiable {
310310
}
311311
}
312312

313-
return baseId + statesId + reactionScoresId + repliesCountId + "\(updatedAt)"
313+
return baseId + statesId + reactionScoresId + repliesCountId + "\(updatedAt)" + pinStateId
314314
}
315315

316316
private var baseId: String {
317317
isDeleted ? "\(id)-deleted" : id
318318
}
319319

320+
private var pinStateId: String {
321+
pinDetails != nil ? "pinned" : "notPinned"
322+
}
323+
320324
var repliesCountId: String {
321325
var repliesCountId = ""
322326
if replyCount > 0 {

Sources/StreamChatSwiftUI/ChatChannel/Gallery/GalleryView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct GalleryView: View {
4848
width: reader.size.width,
4949
height: reader.size.height,
5050
resize: true,
51+
shouldSetFrame: false,
5152
onImageLoaded: { image in
5253
loadedImages[index] = image
5354
}

Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ public struct ImageAttachmentContainer: View {
5252
.background(Color(backgroundColor))
5353
}
5454
}
55-
.clipped()
5655
}
5756
.messageBubble(for: message, isFirst: isFirst)
5857
.fullScreenCover(isPresented: $galleryShown, onDismiss: {
@@ -120,7 +119,7 @@ struct ImageAttachmentView: View {
120119
MultiImageView(
121120
source: sources[0],
122121
width: width / 2,
123-
height: width,
122+
height: fullHeight,
124123
imageTapped: imageTapped,
125124
index: 0
126125
)
@@ -129,19 +128,18 @@ struct ImageAttachmentView: View {
129128
MultiImageView(
130129
source: sources[1],
131130
width: width / 2,
132-
height: width,
131+
height: fullHeight,
133132
imageTapped: imageTapped,
134133
index: 1
135134
)
136135
.withUploadingStateIndicator(for: uploadState(for: 1), url: sources[1])
137136
}
138-
.aspectRatio(1, contentMode: .fill)
139137
} else if sources.count == 3 {
140138
HStack(spacing: spacing) {
141139
MultiImageView(
142140
source: sources[0],
143141
width: width / 2,
144-
height: width,
142+
height: fullHeight,
145143
imageTapped: imageTapped,
146144
index: 0
147145
)
@@ -151,7 +149,7 @@ struct ImageAttachmentView: View {
151149
MultiImageView(
152150
source: sources[1],
153151
width: width / 2,
154-
height: width / 2,
152+
height: fullHeight / 2,
155153
imageTapped: imageTapped,
156154
index: 1
157155
)
@@ -160,51 +158,50 @@ struct ImageAttachmentView: View {
160158
MultiImageView(
161159
source: sources[2],
162160
width: width / 2,
163-
height: width / 2,
161+
height: fullHeight / 2,
164162
imageTapped: imageTapped,
165163
index: 2
166164
)
167165
.withUploadingStateIndicator(for: uploadState(for: 2), url: sources[2])
168166
}
169167
}
170-
.aspectRatio(1, contentMode: .fill)
171168
} else if sources.count > 3 {
172169
HStack(spacing: spacing) {
173170
VStack(spacing: spacing) {
174171
MultiImageView(
175172
source: sources[0],
176173
width: width / 2,
177-
height: width / 2,
174+
height: fullHeight / 2,
178175
imageTapped: imageTapped,
179176
index: 0
180177
)
181178
.withUploadingStateIndicator(for: uploadState(for: 0), url: sources[0])
182179

183180
MultiImageView(
184-
source: sources[1],
181+
source: sources[2],
185182
width: width / 2,
186-
height: width / 2,
183+
height: fullHeight / 2,
187184
imageTapped: imageTapped,
188185
index: 2
189186
)
190-
.withUploadingStateIndicator(for: uploadState(for: 1), url: sources[1])
187+
.withUploadingStateIndicator(for: uploadState(for: 2), url: sources[2])
191188
}
192189

193190
VStack(spacing: spacing) {
194191
MultiImageView(
195-
source: sources[2],
192+
source: sources[1],
196193
width: width / 2,
197-
height: width / 2,
194+
height: fullHeight / 2,
198195
imageTapped: imageTapped,
199196
index: 1
200197
)
201-
.withUploadingStateIndicator(for: uploadState(for: 2), url: sources[2])
198+
.withUploadingStateIndicator(for: uploadState(for: 1), url: sources[1])
202199

203200
ZStack {
204201
MultiImageView(
205202
source: sources[3],
206203
width: width / 2,
207-
height: width / 2,
204+
height: fullHeight / 2,
208205
imageTapped: imageTapped,
209206
index: 3
210207
)
@@ -218,13 +215,16 @@ struct ImageAttachmentView: View {
218215
.font(fonts.title)
219216
}
220217
}
221-
.frame(width: width / 2, height: width / 2)
218+
.frame(width: width / 2, height: fullHeight / 2)
222219
}
223220
}
224-
.aspectRatio(1, contentMode: .fill)
225221
}
226222
}
227-
.frame(maxWidth: width)
223+
.frame(width: width, height: fullHeight)
224+
}
225+
226+
private var fullHeight: CGFloat {
227+
3 * width / 4
228228
}
229229

230230
private var notDisplayedImages: Int {
@@ -242,15 +242,19 @@ struct SingleImageView: View {
242242
var imageTapped: ((Int) -> Void)? = nil
243243
var index: Int?
244244

245+
private var height: CGFloat {
246+
3 * width / 4
247+
}
248+
245249
var body: some View {
246250
LazyLoadingImage(
247251
source: source,
248252
width: width,
249-
height: 3 * width / 4,
253+
height: height,
250254
imageTapped: imageTapped,
251255
index: index
252256
)
253-
.frame(width: width, height: 3 * width / 4)
257+
.frame(width: width, height: height)
254258
}
255259
}
256260

@@ -270,7 +274,6 @@ struct MultiImageView: View {
270274
index: index
271275
)
272276
.frame(width: width, height: height)
273-
.clipped()
274277
}
275278
}
276279

@@ -284,6 +287,7 @@ struct LazyLoadingImage: View {
284287
let width: CGFloat
285288
let height: CGFloat
286289
var resize: Bool = true
290+
var shouldSetFrame: Bool = true
287291
var imageTapped: ((Int) -> Void)? = nil
288292
var index: Int?
289293
var onImageLoaded: (UIImage) -> Void = { _ in }
@@ -325,7 +329,7 @@ struct LazyLoadingImage: View {
325329
url: source,
326330
imageCDN: utils.imageCDN,
327331
resize: resize,
328-
preferredSize: CGSize(width: width, height: 3 * width / 4),
332+
preferredSize: CGSize(width: width, height: height),
329333
completion: { result in
330334
switch result {
331335
case let .success(image):
@@ -337,16 +341,17 @@ struct LazyLoadingImage: View {
337341
}
338342
)
339343
}
340-
.clipped()
341344
}
342345

343346
func imageView(for image: UIImage) -> some View {
344347
Image(uiImage: image)
345348
.resizable()
346349
.scaledToFill()
347350
.aspectRatio(contentMode: .fill)
348-
.clipped()
351+
.frame(width: shouldSetFrame ? width : nil, height: shouldSetFrame ? height : nil)
349352
.allowsHitTesting(false)
353+
.scaleEffect(1.0001) // Needed because of SwiftUI sometimes incorrectly displaying landscape images.
354+
.clipped()
350355
}
351356
}
352357

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct MessageContainerView<Factory: ViewFactory>: View {
2020
var width: CGFloat?
2121
var showsAllInfo: Bool
2222
var isInThread: Bool
23+
var isLast: Bool
2324
@Binding var scrolledId: String?
2425
@Binding var quotedMessage: ChatMessage?
2526
var onLongPress: (MessageDisplayInfo) -> Void
@@ -30,6 +31,7 @@ struct MessageContainerView<Factory: ViewFactory>: View {
3031
@GestureState private var offset: CGSize = .zero
3132

3233
private let replyThreshold: CGFloat = 60
34+
private let paddingValue: CGFloat = 8
3335

3436
var body: some View {
3537
HStack(alignment: .bottom) {
@@ -56,6 +58,13 @@ struct MessageContainerView<Factory: ViewFactory>: View {
5658
}
5759

5860
VStack(alignment: message.isSentByCurrentUser ? .trailing : .leading) {
61+
if isMessagePinned {
62+
MessagePinDetailsView(
63+
message: message,
64+
reactionsShown: reactionsShown
65+
)
66+
}
67+
5968
MessageView(
6069
factory: factory,
6170
message: message,
@@ -158,7 +167,16 @@ struct MessageContainerView<Factory: ViewFactory>: View {
158167
}
159168
}
160169
}
161-
.padding(.top, reactionsShown ? 24 : 0)
170+
.padding(.top, reactionsShown && !isMessagePinned ? 3 * paddingValue : 0)
171+
.padding(.horizontal, paddingValue)
172+
.padding(.bottom, showsAllInfo || isMessagePinned ? paddingValue : 2)
173+
.padding(.top, isLast ? paddingValue : 0)
174+
.background(isMessagePinned ? Color(colors.pinnedBackground) : nil)
175+
.padding(.bottom, isMessagePinned ? paddingValue / 2 : 0)
176+
}
177+
178+
private var isMessagePinned: Bool {
179+
message.pinDetails != nil
162180
}
163181

164182
private var contentWidth: CGFloat {

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListHelperViews.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import StreamChat
66
import SwiftUI
77

8+
/// View that displays the message author and the date of sending.
89
struct MessageAuthorAndDateView: View {
910
@Injected(\.fonts) private var fonts
1011
@Injected(\.colors) private var colors
@@ -22,6 +23,7 @@ struct MessageAuthorAndDateView: View {
2223
}
2324
}
2425

26+
/// View that displays the sending date of a message.
2527
struct MessageDateView: View {
2628
@Injected(\.utils) private var utils
2729
@Injected(\.fonts) private var fonts
@@ -40,6 +42,7 @@ struct MessageDateView: View {
4042
}
4143
}
4244

45+
/// View that displays the read indicator for a message.
4346
struct MessageReadIndicatorView: View {
4447
@Injected(\.images) private var images
4548
@Injected(\.fonts) private var fonts
@@ -65,6 +68,7 @@ struct MessageReadIndicatorView: View {
6568
}
6669
}
6770

71+
/// Message spacer view, used for adding space depending on who sent the message..
6872
struct MessageSpacer: View {
6973
var spacerWidth: CGFloat?
7074

@@ -74,3 +78,28 @@ struct MessageSpacer: View {
7478
.layoutPriority(-1)
7579
}
7680
}
81+
82+
/// View that's displayed when a message is pinned.
83+
struct MessagePinDetailsView: View {
84+
85+
@Injected(\.colors) private var colors
86+
@Injected(\.images) private var images
87+
@Injected(\.fonts) private var fonts
88+
89+
var message: ChatMessage
90+
var reactionsShown: Bool
91+
92+
var body: some View {
93+
HStack {
94+
Image(uiImage: images.pin)
95+
.customizable()
96+
.frame(maxHeight: 12)
97+
Text("\(L10n.Message.Cell.pinnedBy) \(message.pinDetails?.pinnedBy.name ?? L10n.Message.Cell.unknownPin)")
98+
.font(fonts.footnote)
99+
}
100+
.foregroundColor(Color(colors.textLowEmphasis))
101+
.frame(height: 16)
102+
.padding(.bottom, reactionsShown ? 16 : 0)
103+
.padding(.top, 4)
104+
}
105+
}

0 commit comments

Comments
 (0)