Skip to content

Commit 66cacf8

Browse files
ZryteZryte
authored andcommitted
Update AmityUIKit v4.17.0-beta05
1 parent 7e26185 commit 66cacf8

17 files changed

Lines changed: 109 additions & 76 deletions

File tree

UpstraUIKit/AmityUIKit.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@
371371
78DA0324263C715C007C11CE /* AmityMyCommunityPreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78DA0320263C715C007C11CE /* AmityMyCommunityPreviewViewController.xib */; };
372372
78DA0325263C715C007C11CE /* AmityMyCommunityPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78DA0321263C715C007C11CE /* AmityMyCommunityPreviewViewController.swift */; };
373373
921C3FCB2C379FDB00BF403E /* AmitySocialV4Compatible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 921C3FCA2C379FDB00BF403E /* AmitySocialV4Compatible.swift */; };
374-
92EAD78F2F6D78380084BA8C /* SharedFrameworks in Frameworks */ = {isa = PBXBuildFile; productRef = 92EAD78E2F6D78380084BA8C /* SharedFrameworks */; };
374+
92E9596A2F716BB000B6317A /* SharedFrameworks in Frameworks */ = {isa = PBXBuildFile; productRef = 92E959692F716BB000B6317A /* SharedFrameworks */; };
375375
970E784726429FA500E5FCEE /* ChatSettingsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970E784526429FA500E5FCEE /* ChatSettingsTableViewCell.swift */; };
376376
970E784826429FA500E5FCEE /* ChatSettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 970E784626429FA500E5FCEE /* ChatSettingsTableViewCell.xib */; };
377377
970E784E26429FB300E5FCEE /* AmityChatSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970E784C26429FB300E5FCEE /* AmityChatSettingsViewController.swift */; };
@@ -1340,7 +1340,7 @@
13401340
isa = PBXFrameworksBuildPhase;
13411341
buildActionMask = 2147483647;
13421342
files = (
1343-
92EAD78F2F6D78380084BA8C /* SharedFrameworks in Frameworks */,
1343+
92E9596A2F716BB000B6317A /* SharedFrameworks in Frameworks */,
13441344
68F5D9FA2B481E4000A9FA0D /* AmityUIKit4.framework in Frameworks */,
13451345
);
13461346
runOnlyForDeploymentPostprocessing = 0;
@@ -4515,7 +4515,7 @@
45154515
);
45164516
name = AmityUIKit;
45174517
packageProductDependencies = (
4518-
92EAD78E2F6D78380084BA8C /* SharedFrameworks */,
4518+
92E959692F716BB000B6317A /* SharedFrameworks */,
45194519
);
45204520
productName = UpstraUIKit;
45214521
productReference = 72A3503024EA811500DA9D46 /* AmityUIKit.framework */;
@@ -5531,7 +5531,7 @@
55315531
/* End XCConfigurationList section */
55325532

55335533
/* Begin XCSwiftPackageProductDependency section */
5534-
92EAD78E2F6D78380084BA8C /* SharedFrameworks */ = {
5534+
92E959692F716BB000B6317A /* SharedFrameworks */ = {
55355535
isa = XCSwiftPackageProductDependency;
55365536
productName = SharedFrameworks;
55375537
};

UpstraUIKit/AmityUIKit4/AmityUIKit4.xcodeproj/project.pbxproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
684AE1012B0C5B0200FD7270 /* AmityUIKit4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 684AE1002B0C5B0200FD7270 /* AmityUIKit4Tests.swift */; };
1212
685F16C22BFCABAD0016685F /* AmityReactionList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 685F16C12BFCABAD0016685F /* AmityReactionList.swift */; };
1313
685F16C62BFCABB50016685F /* ReactionTabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 685F16C52BFCABB50016685F /* ReactionTabBarView.swift */; };
14-
92EAD78D2F6D782E0084BA8C /* SharedFrameworks in Frameworks */ = {isa = PBXBuildFile; productRef = 92EAD78C2F6D782E0084BA8C /* SharedFrameworks */; };
14+
92E959682F716BA800B6317A /* SharedFrameworks in Frameworks */ = {isa = PBXBuildFile; productRef = 92E959672F716BA800B6317A /* SharedFrameworks */; };
1515
/* End PBXBuildFile section */
1616

1717
/* Begin PBXContainerItemProxy section */
@@ -762,7 +762,7 @@
762762
isa = PBXFrameworksBuildPhase;
763763
buildActionMask = 2147483647;
764764
files = (
765-
92EAD78D2F6D782E0084BA8C /* SharedFrameworks in Frameworks */,
765+
92E959682F716BA800B6317A /* SharedFrameworks in Frameworks */,
766766
);
767767
runOnlyForDeploymentPostprocessing = 0;
768768
};
@@ -1239,7 +1239,7 @@
12391239
/* End XCConfigurationList section */
12401240

12411241
/* Begin XCSwiftPackageProductDependency section */
1242-
92EAD78C2F6D782E0084BA8C /* SharedFrameworks */ = {
1242+
92E959672F716BA800B6317A /* SharedFrameworks */ = {
12431243
isa = XCSwiftPackageProductDependency;
12441244
productName = SharedFrameworks;
12451245
};

UpstraUIKit/AmityUIKit4/AmityUIKit4/Core/Utilities/Constants/AmityViewId.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public enum ComponentId: String {
132132
case myEventFeedComponent = "my_event_feed_component"
133133
case exploreEventFeedComponent = "explore_event_feed_component"
134134

135-
case productTagListBottomsheet = "product_tag_list_bottomsheet"
135+
case productTagListBottomsheet = "product_tag_list"
136136
case productTagSelectionBottomsheet = "product_tag_selection_bottomsheet"
137137

138138
case manageProductTagList = "manage_product_tag_list"
@@ -358,7 +358,7 @@ public enum ElementId: String {
358358

359359
// ProductTagList
360360
case productTagListHeader = "product_tag_list_header"
361-
case productTagListItem = "product_tag_list_item"
361+
case productTagListItem = "product_tag"
362362
// Livestream Pinned Product
363363
case livestreamPinnedProduct = "livestream_pinned_product"
364364
}

UpstraUIKit/AmityUIKit4/AmityUIKit4/LiveStream/Shared/Element/LivestreamPinnedProductElement.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct LivestreamPinnedProductElement: AmityElementView {
4949
var roomId: String? = nil
5050

5151
// Static set for session-level view deduplication
52-
private static var viewedProductIds = Set<String>()
52+
static var viewedProductIds = Set<String>()
5353

5454
@EnvironmentObject var viewConfig: AmityViewConfigController
5555

@@ -69,14 +69,13 @@ struct LivestreamPinnedProductElement: AmityElementView {
6969

7070
/// Track product view analytics when element becomes visible
7171
private func trackProductView() {
72-
guard !Self.viewedProductIds.contains(product.productId) else {
72+
guard !LivestreamPinnedProductElement.viewedProductIds.contains(product.productId) else {
7373
return
7474
}
75-
Self.viewedProductIds.insert(product.productId)
76-
7775
let component = componentId?.rawValue ?? "*"
78-
let location = "\(pageId?.rawValue ?? "")/\(component)/\(id.rawValue)"
76+
let location = "\(pageId?.rawValue ?? "*")/\(component)/\(id.rawValue)"
7977
let resolvedRoomId = roomId ?? ""
78+
LivestreamPinnedProductElement.viewedProductIds.insert(product.productId)
8079

8180
product.analytics.markAsViewed(
8281
location: location,
@@ -88,7 +87,7 @@ struct LivestreamPinnedProductElement: AmityElementView {
8887
/// Track product click analytics when user taps card
8988
private func trackProductClick() {
9089
let component = componentId?.rawValue ?? "*"
91-
let location = "\(pageId?.rawValue ?? "")/\(component)/\(id.rawValue)"
90+
let location = "\(pageId?.rawValue ?? "*")/\(component)/\(id.rawValue)"
9291
let resolvedRoomId = roomId ?? ""
9392

9493
product.analytics.markAsClicked(
@@ -104,7 +103,7 @@ struct LivestreamPinnedProductElement: AmityElementView {
104103
let visibleHeight = min(screenHeight, frame.maxY) - max(0, frame.minY)
105104
let visiblePercentage = (visibleHeight / frame.height) * 100
106105

107-
if visiblePercentage > 60 && !Self.viewedProductIds.contains(product.productId) {
106+
if visiblePercentage > 60/* && !Self.viewedProductIds.contains(product.productId)*/ {
108107
trackProductView()
109108
}
110109
}
@@ -225,6 +224,9 @@ struct LivestreamPinnedProductElement: AmityElementView {
225224
}
226225
.background(GeometryReader { geometry in
227226
Color.clear
227+
.onAppear {
228+
checkVisibilityAndTrackView(frame: geometry.frame(in: .global))
229+
}
228230
.onChange(of: geometry.frame(in: .global)) { frame in
229231
checkVisibilityAndTrackView(frame: frame)
230232
}

UpstraUIKit/AmityUIKit4/AmityUIKit4/Social/Components/PostDetail/AmityPostContentComponent.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ public struct AmityPostContentComponent: AmityComponentView {
297297
case .image, .video:
298298
postContentTextView()
299299

300-
PostContentMediaView(post: post, viewConfig: viewConfig)
300+
PostContentMediaView(post: post, viewConfig: viewConfig, pageId: pageId)
301301
.frame(height: 328)
302302
.clipShape(RoundedCorner(radius: 8))
303303

@@ -315,13 +315,13 @@ public struct AmityPostContentComponent: AmityComponentView {
315315
case .liveStream:
316316
livestreamPostContentTextView()
317317

318-
PostContentLiveStreamView(post: post)
318+
PostContentLiveStreamView(post: post, pageId: pageId)
319319
.padding([.leading, .trailing, .top], -16)
320320

321321
case .room:
322322
roomPostContentTextView()
323323

324-
PostContentLiveStreamView(post: post)
324+
PostContentLiveStreamView(post: post, pageId: pageId)
325325
.padding([.leading, .trailing, .top], -16)
326326

327327
case .clip:
@@ -467,7 +467,7 @@ public struct AmityPostContentComponent: AmityComponentView {
467467
@ViewBuilder
468468
private func postProductCarouselView(_ post: AmityPostModel) -> some View {
469469
if !post.allProductTags.isEmpty {
470-
AmityProductCarouselView(allProductTags: post.allProductTags, postId: post.postId)
470+
AmityProductCarouselView(allProductTags: post.allProductTags, postId: post.postId, pageId: pageId)
471471
.environmentObject(viewConfig)
472472
.environmentObject(host)
473473
}

UpstraUIKit/AmityUIKit4/AmityUIKit4/Social/Components/PostDetail/PostContent/AmityProductCarouselView.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct AmityProductCarouselView: View {
1515

1616
let allProductTags: [AmityProductTagModel]
1717
let postId: String
18+
var pageId: PageId? = nil
1819

1920
@State private var showProductList: Bool = false
2021

@@ -36,6 +37,7 @@ struct AmityProductCarouselView: View {
3637
ProductCarouselCardView(
3738
productTag: productTag,
3839
postId: postId,
40+
pageId: pageId,
3941
viewConfig: viewConfig
4042
)
4143
}
@@ -72,6 +74,7 @@ struct AmityProductCarouselView: View {
7274
@ViewBuilder
7375
private var productTagListSheet: some View {
7476
let component = AmityProductTagListComponent(
77+
pageId: pageId,
7578
productTags: allProductTags,
7679
renderMode: .post,
7780
sourceId: postId,
@@ -150,6 +153,7 @@ struct ProductCarouselSkeletonView: View {
150153
private struct ProductCarouselCardView: View {
151154
let productTag: AmityProductTagModel
152155
let postId: String
156+
let pageId: PageId?
153157
let viewConfig: AmityViewConfigController
154158

155159
private var isUnavailable: Bool {
@@ -240,8 +244,9 @@ private struct ProductCarouselCardView: View {
240244
private func trackProductView() {
241245
guard !AmityProductCarouselView.viewedProductIds.contains(productTag.productId) else { return }
242246
AmityProductCarouselView.viewedProductIds.insert(productTag.productId)
243-
244-
let location = "*/post_content_component/*"
247+
248+
let page = pageId?.rawValue ?? "*"
249+
let location = "\(page)/post_content/product_tag"
245250
productTag.object.analytics.markAsViewed(
246251
location: location,
247252
sourceType: .post,
@@ -250,7 +255,8 @@ private struct ProductCarouselCardView: View {
250255
}
251256

252257
private func trackProductClick() {
253-
let location = "*/post_content_component/*"
258+
let page = pageId?.rawValue ?? "*"
259+
let location = "\(page)/post_content/product_tag"
254260
productTag.object.analytics.markAsClicked(
255261
location: location,
256262
sourceType: .post,
@@ -263,7 +269,7 @@ private struct ProductCarouselCardView: View {
263269
let visibleHeight = min(screenHeight, frame.maxY) - max(0, frame.minY)
264270
let visiblePercentage = (visibleHeight / frame.height) * 100
265271

266-
if visiblePercentage > 60 && !AmityProductCarouselView.viewedProductIds.contains(productTag.productId) {
272+
if visiblePercentage > 60/* && !AmityProductCarouselView.viewedProductIds.contains(productTag.productId) */{
267273
trackProductView()
268274
}
269275
}

UpstraUIKit/AmityUIKit4/AmityUIKit4/Social/Components/PostDetail/PostContent/Livestream/PostContentLiveStreamView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ struct PostContentLiveStreamView: View {
2222
let room: AmityRoom?
2323
let isStreamDeleted: Bool
2424
let isStreamBanned: Bool
25+
var pageId: PageId? = nil
2526

2627
private var postManager = PostManager()
2728

28-
init(post: AmityPostModel) {
29+
init(post: AmityPostModel, pageId: PageId? = nil) {
2930
self.post = post
31+
self.pageId = pageId
3032
self.room = post.room
3133
self.isStreamDeleted = room?.isDeleted ?? false
3234
self.isStreamBanned = false
@@ -193,6 +195,7 @@ struct PostContentLiveStreamView: View {
193195
}
194196
if !productTags.isEmpty {
195197
AmityProductTagListComponent(
198+
pageId: pageId,
196199
productTags: productTags,
197200
renderMode: .livestream,
198201
sourceId: post.room?.roomId ?? "",

UpstraUIKit/AmityUIKit4/AmityUIKit4/Social/Components/PostDetail/PostContent/PostContentMediaView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ struct PostContentMediaView: View {
1515
@StateObject private var viewModel: PostContentMediaViewModel = PostContentMediaViewModel()
1616
@ObservedObject var viewConfig: AmityViewConfigController
1717
let post: AmityPostModel
18+
var pageId: PageId? = nil
1819

19-
init(post: AmityPostModel, viewConfig: AmityViewConfigController) {
20+
init(post: AmityPostModel, viewConfig: AmityViewConfigController, pageId: PageId? = nil) {
2021
self.post = post
2122
self.viewConfig = viewConfig
23+
self.pageId = pageId
2224
}
2325

2426
var body: some View {
@@ -119,6 +121,7 @@ struct PostContentMediaView: View {
119121
if let media = viewModel.selectedProductTagMedia {
120122
let renderMode: ProductTagListRenderMode = media.type == .video ? .video : .image
121123
let component = AmityProductTagListComponent(
124+
pageId: pageId,
122125
productTags: media.produtTags,
123126
renderMode: renderMode,
124127
sourceId: post.postId, onProductClick: { productTag in

UpstraUIKit/AmityUIKit4/AmityUIKit4/Social/Components/Products/ProductTagList/Components/AmityProductTagListComponent.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public struct AmityProductTagListComponent: AmityComponentView {
5656
.productTagListBottomsheet
5757
}
5858

59+
static var viewedProductIds = Set<String>()
60+
5961
// MARK: - Properties
6062

6163
/// Array of product tags to display

UpstraUIKit/AmityUIKit4/AmityUIKit4/Social/Components/Products/ProductTagList/Elements/AmityProductTagElement.swift

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,11 @@
88
import SwiftUI
99
import AmitySDK
1010

11-
/// ProductTagElement v2
11+
/// ProductTagElement
1212
/// Displays a single product tag with context-aware behavior and pin status.
1313
/// Used within ProductTagListComponent to render individual product tags.
1414
///
1515
/// Element ID: `product_tag_list_item`
16-
///
17-
/// ## Key Features (v2)
18-
/// - Render mode support (post vs livestream contexts)
19-
/// - Pin status display with banner overlay
20-
/// - Click interaction on entire card
21-
/// - Context-aware action button (View for livestream viewers)
22-
/// - Analytics tracking for view and click events
2316
struct AmityProductTagElement: AmityElementView {
2417

2518
var pageId: PageId?
@@ -33,20 +26,15 @@ struct AmityProductTagElement: AmityElementView {
3326

3427
/// The product tag data to display
3528
let productTag: AmityProductTagModel
36-
/// Context mode for rendering (v2) - affects button display
29+
/// Context mode for rendering
3730
let renderMode: ProductTagListRenderMode
38-
/// Whether this product is currently pinned (v2) - shows banner (REQ-001)
3931
var isPinned: Bool = false
40-
/// Called when user clicks anywhere on the product card (REQ-004)
4132
let onClick: () -> Void
4233
/// Source ID for analytics (postId or roomId)
4334
let sourceId: String
4435
/// Source type for analytics (room or post)
4536
let sourceType: AmityAnalyticsSourceType
4637

47-
// Static set for session-level view deduplication
48-
private static var viewedProductIds = Set<String>()
49-
5038
/// Whether the product is unavailable (inactive)
5139
private var isInactive: Bool {
5240
return productTag.object.status != .active
@@ -56,13 +44,14 @@ struct AmityProductTagElement: AmityElementView {
5644

5745
/// Track product view analytics when element becomes visible
5846
private func trackProductView() {
59-
guard !Self.viewedProductIds.contains(productTag.productId) else {
47+
guard !AmityProductTagListComponent.viewedProductIds.contains(productTag.productId) else {
6048
return
6149
}
62-
Self.viewedProductIds.insert(productTag.productId)
50+
AmityProductTagListComponent.viewedProductIds.insert(productTag.productId)
6351

52+
let page = pageId?.rawValue ?? "*"
6453
let component = componentId?.rawValue ?? "*"
65-
let location = "\(pageId?.rawValue ?? "")/\(component)/\(id.rawValue)"
54+
let location = "\(page)/\(component)/\(id.rawValue)"
6655

6756
productTag.object.analytics.markAsViewed(
6857
location: location,
@@ -73,8 +62,9 @@ struct AmityProductTagElement: AmityElementView {
7362

7463
/// Track product click analytics when user taps card
7564
private func trackProductClick() {
65+
let page = pageId?.rawValue ?? "*"
7666
let component = componentId?.rawValue ?? "*"
77-
let location = "\(pageId?.rawValue ?? "")/\(component)/\(id.rawValue)"
67+
let location = "\(page)/\(component)/\(id.rawValue)"
7868

7969
productTag.object.analytics.markAsClicked(
8070
location: location,
@@ -89,14 +79,13 @@ struct AmityProductTagElement: AmityElementView {
8979
let visibleHeight = min(screenHeight, frame.maxY) - max(0, frame.minY)
9080
let visiblePercentage = (visibleHeight / frame.height) * 100
9181

92-
if visiblePercentage > 60 && !Self.viewedProductIds.contains(productTag.productId) {
82+
if visiblePercentage > 60 /*&& !Self.viewedProductIds.contains(productTag.productId) */{
9383
trackProductView()
9484
}
9585
}
9686

9787
var body: some View {
9888
HStack(spacing: 12) {
99-
// Product Thumbnail with Pin Status Banner (REQ-001, REQ-002, REQ-003)
10089
ZStack(alignment: .bottom) {
10190
AsyncImage(
10291
placeholderView: {
@@ -200,16 +189,16 @@ struct AmityProductTagElement: AmityElementView {
200189
.frame(maxWidth: .infinity, alignment: .leading)
201190
}
202191
.contentShape(Rectangle())
203-
// REQ-004, REQ-005: Entire card is clickable (including button)
204192
.onTapGesture {
205193
guard !isInactive else { return }
206-
// REQ-007: Track click before opening URL
207194
trackProductClick()
208195
onClick()
209196
}
210-
// REQ-006: Track view when element is 60% visible
211197
.background(GeometryReader { geometry in
212198
Color.clear
199+
.onAppear {
200+
checkVisibilityAndTrackView(frame: geometry.frame(in: .global))
201+
}
213202
.onChange(of: geometry.frame(in: .global)) { frame in
214203
checkVisibilityAndTrackView(frame: frame)
215204
}

0 commit comments

Comments
 (0)