@shortkitsdk/react-native 0.2.15 → 0.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle.kts +1 -1
- package/android/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +5 -9
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +8 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +3 -2
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +4 -0
- package/ios/DebugPanelView.swift +302 -0
- package/ios/ShortKitBridge.swift +5 -3
- package/ios/ShortKitFeedView.swift +15 -0
- package/ios/ShortKitModule.mm +3 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +5289 -721
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +78 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +78 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/PrivacyInfo.xcprivacy +31 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +20 -9
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +5289 -721
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +78 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +78 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +5289 -721
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +78 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +78 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/PrivacyInfo.xcprivacy +31 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +28 -17
- package/ios/ShortKitWidgetNativeView.swift +7 -1
- package/package.json +1 -1
- package/src/ShortKitFeed.tsx +0 -13
- package/src/ShortKitProvider.tsx +2 -0
- package/src/ShortKitWidget.tsx +1 -0
- package/src/index.ts +0 -3
- package/src/serialization.ts +0 -1
- package/src/specs/NativeShortKitModule.ts +1 -7
- package/src/types.ts +3 -11
- package/ios/ShortKitSDK.xcframework.dev-backup/Info.plist +0 -43
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +0 -418
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Info.plist +0 -16
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +0 -32983
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +0 -915
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +0 -915
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +0 -4
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +0 -168
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +0 -418
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +0 -16
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +0 -32983
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +0 -915
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +0 -915
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +0 -4
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +0 -168
package/android/build.gradle.kts
CHANGED
|
@@ -34,7 +34,7 @@ dependencies {
|
|
|
34
34
|
// When Reco's settings.gradle has the composite build, Gradle substitutes
|
|
35
35
|
// this with the live source from android_sdk/shortkit automatically.
|
|
36
36
|
// implementation("dev.shortkit:shortkit:0.2.11")
|
|
37
|
-
implementation(files("/
|
|
37
|
+
implementation(files("libs/shortkit-release.aar"))
|
|
38
38
|
// Transitive deps needed when using local AAR (Maven artifact bundles these automatically)
|
|
39
39
|
implementation("androidx.media3:media3-exoplayer:1.5.1")
|
|
40
40
|
implementation("androidx.media3:media3-exoplayer-hls:1.5.1")
|
|
Binary file
|
|
@@ -20,13 +20,13 @@ import com.shortkit.sdk.model.ImageCarouselItem
|
|
|
20
20
|
import com.shortkit.sdk.model.VideoCarouselItem
|
|
21
21
|
import com.shortkit.sdk.model.JsonValue
|
|
22
22
|
import com.shortkit.sdk.model.PlayerState
|
|
23
|
+
import com.shortkit.sdk.config.SurveyOverlayMode
|
|
23
24
|
import com.shortkit.sdk.config.AdOverlayMode
|
|
24
25
|
import com.shortkit.sdk.config.CarouselOverlayMode
|
|
25
26
|
import com.shortkit.sdk.config.FeedConfig
|
|
26
27
|
import com.shortkit.sdk.config.FeedHeight
|
|
27
28
|
import com.shortkit.sdk.config.FeedSource
|
|
28
29
|
import com.shortkit.sdk.config.ScrollAxis
|
|
29
|
-
import com.shortkit.sdk.config.SurveyOverlayMode
|
|
30
30
|
import com.shortkit.sdk.config.VideoCarouselOverlayMode
|
|
31
31
|
import com.shortkit.sdk.feed.ShortKitRefreshState
|
|
32
32
|
import com.shortkit.sdk.config.VideoOverlayMode
|
|
@@ -57,6 +57,7 @@ class ShortKitBridge(
|
|
|
57
57
|
clientAppName: String?,
|
|
58
58
|
clientAppVersion: String?,
|
|
59
59
|
customDimensionsJSON: String?,
|
|
60
|
+
debugPanel: Boolean,
|
|
60
61
|
private val emitEvent: (String, WritableMap) -> Unit
|
|
61
62
|
) {
|
|
62
63
|
|
|
@@ -511,7 +512,8 @@ class ShortKitBridge(
|
|
|
511
512
|
adProvider = null,
|
|
512
513
|
clientAppName = clientAppName,
|
|
513
514
|
clientAppVersion = clientAppVersion,
|
|
514
|
-
customDimensions = dims
|
|
515
|
+
customDimensions = dims,
|
|
516
|
+
debugPanelEnabled = debugPanel
|
|
515
517
|
)
|
|
516
518
|
this.shortKit = sdk
|
|
517
519
|
shared = this
|
|
@@ -883,13 +885,7 @@ class ShortKitBridge(
|
|
|
883
885
|
callback(uuid)
|
|
884
886
|
return
|
|
885
887
|
}
|
|
886
|
-
val
|
|
887
|
-
it.tags?.let { tags -> put("tags", org.json.JSONArray(tags)) }
|
|
888
|
-
it.section?.let { s -> put("section", s) }
|
|
889
|
-
it.author?.let { a -> put("author", a) }
|
|
890
|
-
it.contentType?.let { ct -> put("contentType", ct) }
|
|
891
|
-
}.toString() }
|
|
892
|
-
val preload = sdk.preloadFeed(filter = filterJSON)
|
|
888
|
+
val preload = sdk.preloadFeed(filter = config.filter)
|
|
893
889
|
val uuid = java.util.UUID.randomUUID().toString()
|
|
894
890
|
preloadHandles[uuid] = preload
|
|
895
891
|
callback(uuid)
|
|
@@ -191,6 +191,14 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
|
|
|
191
191
|
|
|
192
192
|
val fragment = ShortKitFeedFragment.newInstance(sdk, feedConfig, startAtItemId)
|
|
193
193
|
|
|
194
|
+
if (sdk.debugPanelEnabled) {
|
|
195
|
+
fragment.debugPanelFactory = { active, prev, next, lifecycleOwner ->
|
|
196
|
+
com.shortkit.sdk.debug.DebugPanelView(context).also { panel ->
|
|
197
|
+
panel.subscribe(active, prev, next, lifecycleOwner)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
194
202
|
try {
|
|
195
203
|
activity.supportFragmentManager
|
|
196
204
|
.beginTransaction()
|
|
@@ -71,7 +71,8 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
|
|
|
71
71
|
hasLoadingView: Boolean,
|
|
72
72
|
clientAppName: String?,
|
|
73
73
|
clientAppVersion: String?,
|
|
74
|
-
customDimensions: String
|
|
74
|
+
customDimensions: String?,
|
|
75
|
+
debugPanel: Boolean?
|
|
75
76
|
) {
|
|
76
77
|
bridge?.teardown()
|
|
77
78
|
bridge = null
|
|
@@ -86,6 +87,7 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
|
|
|
86
87
|
clientAppName = clientAppName,
|
|
87
88
|
clientAppVersion = clientAppVersion,
|
|
88
89
|
customDimensionsJSON = customDimensions,
|
|
90
|
+
debugPanel = debugPanel ?: false,
|
|
89
91
|
emitEvent = { name, body -> sendEvent(name, body) }
|
|
90
92
|
)
|
|
91
93
|
|
|
@@ -258,7 +260,6 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
|
|
|
258
260
|
"onFormatChange" -> emitOnFormatChange(params)
|
|
259
261
|
"onPrefetchedAheadCountChanged" -> emitOnPrefetchedAheadCountChanged(params)
|
|
260
262
|
"onRemainingContentCountChanged" -> emitOnRemainingContentCountChanged(params)
|
|
261
|
-
"onSurveyResponse" -> emitOnSurveyResponse(params)
|
|
262
263
|
"onContentTapped" -> emitOnContentTapped(params)
|
|
263
264
|
"onDismiss" -> emitOnDismiss(params)
|
|
264
265
|
"onRefreshStateChanged" -> emitOnRefreshStateChanged(params)
|
|
@@ -7,6 +7,7 @@ import com.shortkit.sdk.config.VideoOverlayMode
|
|
|
7
7
|
import com.shortkit.sdk.config.WidgetConfig
|
|
8
8
|
import com.shortkit.sdk.model.ContentItem
|
|
9
9
|
import com.shortkit.sdk.widget.ShortKitWidgetView
|
|
10
|
+
import com.shortkit.sdk.model.FeedFilter
|
|
10
11
|
import org.json.JSONArray
|
|
11
12
|
import org.json.JSONObject
|
|
12
13
|
|
|
@@ -87,6 +88,9 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
|
|
|
87
88
|
else -> PlayerClickAction.FEED
|
|
88
89
|
},
|
|
89
90
|
cardOverlay = parseOverlay(obj),
|
|
91
|
+
filter = obj.optString("filter", "").let { filterStr ->
|
|
92
|
+
if (filterStr.isNotEmpty()) ShortKitBridge.parseFeedFilterToModel(filterStr) else null
|
|
93
|
+
},
|
|
90
94
|
)
|
|
91
95
|
} catch (_: Exception) {
|
|
92
96
|
WidgetConfig()
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import Combine
|
|
3
|
+
import ShortKitSDK
|
|
4
|
+
|
|
5
|
+
private enum AdjacentSlot {
|
|
6
|
+
case previous, next
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
final class DebugPanelView: UIView {
|
|
10
|
+
static let panelWidth: CGFloat = 310
|
|
11
|
+
static let panelHeight: CGFloat = 296 // +20 for collapsed header row
|
|
12
|
+
static let expandedExtraHeight: CGFloat = 80 // 4 labels × ~20pt
|
|
13
|
+
|
|
14
|
+
/// Currently selected swipe curve — FeedViewController reads SwipeCurve.selected.
|
|
15
|
+
static var selectedSwipeCurve: SwipeCurve {
|
|
16
|
+
get { SwipeCurve.selected }
|
|
17
|
+
set { SwipeCurve.selected = newValue }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private let stackView = UIStackView()
|
|
21
|
+
private let headerLabel = UILabel()
|
|
22
|
+
private let curveSegment = UISegmentedControl()
|
|
23
|
+
private let positionLabel = UILabel()
|
|
24
|
+
private let intentTtffLabel = UILabel()
|
|
25
|
+
private let transitionTtffLabel = UILabel()
|
|
26
|
+
private let prerollBufferLabel = UILabel()
|
|
27
|
+
private let videoQualityLabel = UILabel()
|
|
28
|
+
private let thumbnailLabel = UILabel()
|
|
29
|
+
private let prefetchLabel = UILabel()
|
|
30
|
+
private let bitrateLabel = UILabel()
|
|
31
|
+
private let rebufferLabel = UILabel()
|
|
32
|
+
private let loopWatchLabel = UILabel()
|
|
33
|
+
|
|
34
|
+
// Adjacent players section
|
|
35
|
+
private let adjacentHeader = UILabel()
|
|
36
|
+
private let prevHeaderLabel = UILabel()
|
|
37
|
+
private let prevDetailLabel = UILabel()
|
|
38
|
+
private let nextHeaderLabel = UILabel()
|
|
39
|
+
private let nextDetailLabel = UILabel()
|
|
40
|
+
private let separatorView = UIView()
|
|
41
|
+
private var adjacentLabels: [UIView] = []
|
|
42
|
+
private var isAdjacentExpanded = false
|
|
43
|
+
private var prevCancellable: AnyCancellable?
|
|
44
|
+
private var nextCancellable: AnyCancellable?
|
|
45
|
+
|
|
46
|
+
private var cancellable: AnyCancellable?
|
|
47
|
+
private var dragOffset: CGPoint = .zero
|
|
48
|
+
|
|
49
|
+
override init(frame: CGRect) {
|
|
50
|
+
super.init(frame: frame)
|
|
51
|
+
setupViews()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
required init?(coder: NSCoder) {
|
|
55
|
+
fatalError("init(coder:) has not been implemented")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private func setupViews() {
|
|
59
|
+
backgroundColor = UIColor.black.withAlphaComponent(0.75)
|
|
60
|
+
layer.cornerRadius = 10
|
|
61
|
+
clipsToBounds = true
|
|
62
|
+
|
|
63
|
+
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
|
64
|
+
addGestureRecognizer(pan)
|
|
65
|
+
|
|
66
|
+
let labels = [headerLabel, positionLabel, intentTtffLabel, transitionTtffLabel, prerollBufferLabel, videoQualityLabel, thumbnailLabel, prefetchLabel, bitrateLabel, rebufferLabel, loopWatchLabel]
|
|
67
|
+
for label in labels {
|
|
68
|
+
label.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .regular)
|
|
69
|
+
label.textColor = .white
|
|
70
|
+
label.numberOfLines = 1
|
|
71
|
+
}
|
|
72
|
+
headerLabel.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .bold)
|
|
73
|
+
|
|
74
|
+
stackView.axis = .vertical
|
|
75
|
+
stackView.spacing = 4
|
|
76
|
+
stackView.alignment = .leading
|
|
77
|
+
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
78
|
+
addSubview(stackView)
|
|
79
|
+
|
|
80
|
+
for label in labels {
|
|
81
|
+
stackView.addArrangedSubview(label)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Swipe curve picker
|
|
85
|
+
for (i, curve) in SwipeCurve.allCases.enumerated() {
|
|
86
|
+
curveSegment.insertSegment(withTitle: curve.label, at: i, animated: false)
|
|
87
|
+
}
|
|
88
|
+
curveSegment.selectedSegmentIndex = SwipeCurve.quarticOut.rawValue
|
|
89
|
+
curveSegment.setTitleTextAttributes([.foregroundColor: UIColor.white, .font: UIFont.monospacedSystemFont(ofSize: 10, weight: .medium)], for: .normal)
|
|
90
|
+
curveSegment.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .selected)
|
|
91
|
+
curveSegment.backgroundColor = UIColor.white.withAlphaComponent(0.15)
|
|
92
|
+
curveSegment.selectedSegmentTintColor = UIColor.white.withAlphaComponent(0.9)
|
|
93
|
+
curveSegment.addTarget(self, action: #selector(curveChanged), for: .valueChanged)
|
|
94
|
+
stackView.addArrangedSubview(curveSegment)
|
|
95
|
+
|
|
96
|
+
// Separator
|
|
97
|
+
separatorView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
|
|
98
|
+
separatorView.translatesAutoresizingMaskIntoConstraints = false
|
|
99
|
+
separatorView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
|
|
100
|
+
stackView.addArrangedSubview(separatorView)
|
|
101
|
+
|
|
102
|
+
// Adjacent header (tap target)
|
|
103
|
+
adjacentHeader.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .bold)
|
|
104
|
+
adjacentHeader.textColor = .white
|
|
105
|
+
adjacentHeader.text = "▶ Adjacent Players"
|
|
106
|
+
adjacentHeader.isUserInteractionEnabled = true
|
|
107
|
+
adjacentHeader.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(toggleAdjacent)))
|
|
108
|
+
stackView.addArrangedSubview(adjacentHeader)
|
|
109
|
+
|
|
110
|
+
// Adjacent player labels (initially hidden)
|
|
111
|
+
let adjLabels = [prevHeaderLabel, prevDetailLabel, nextHeaderLabel, nextDetailLabel]
|
|
112
|
+
for label in adjLabels {
|
|
113
|
+
label.font = UIFont.monospacedSystemFont(ofSize: 11, weight: .regular)
|
|
114
|
+
label.textColor = UIColor.white.withAlphaComponent(0.85)
|
|
115
|
+
label.numberOfLines = 1
|
|
116
|
+
label.isHidden = true
|
|
117
|
+
stackView.addArrangedSubview(label)
|
|
118
|
+
}
|
|
119
|
+
adjacentLabels = adjLabels
|
|
120
|
+
|
|
121
|
+
prevHeaderLabel.text = "▲ Prev — not assigned"
|
|
122
|
+
prevDetailLabel.text = ""
|
|
123
|
+
nextHeaderLabel.text = "▼ Next — not assigned"
|
|
124
|
+
nextDetailLabel.text = ""
|
|
125
|
+
|
|
126
|
+
NSLayoutConstraint.activate([
|
|
127
|
+
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 10),
|
|
128
|
+
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
|
|
129
|
+
stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
|
|
130
|
+
])
|
|
131
|
+
|
|
132
|
+
// Initial state
|
|
133
|
+
update(DebugMetrics())
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
|
|
137
|
+
guard let parent = superview else { return }
|
|
138
|
+
switch gesture.state {
|
|
139
|
+
case .began:
|
|
140
|
+
dragOffset = gesture.location(in: self)
|
|
141
|
+
case .changed:
|
|
142
|
+
let location = gesture.location(in: parent)
|
|
143
|
+
var newCenter = CGPoint(
|
|
144
|
+
x: location.x - dragOffset.x + bounds.width / 2,
|
|
145
|
+
y: location.y - dragOffset.y + bounds.height / 2
|
|
146
|
+
)
|
|
147
|
+
// Keep panel within parent bounds
|
|
148
|
+
let hw = bounds.width / 2, hh = bounds.height / 2
|
|
149
|
+
newCenter.x = max(hw, min(parent.bounds.width - hw, newCenter.x))
|
|
150
|
+
newCenter.y = max(hh, min(parent.bounds.height - hh, newCenter.y))
|
|
151
|
+
center = newCenter
|
|
152
|
+
default:
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@objc private func curveChanged(_ sender: UISegmentedControl) {
|
|
158
|
+
guard let curve = SwipeCurve(rawValue: sender.selectedSegmentIndex) else { return }
|
|
159
|
+
Self.selectedSwipeCurve = curve
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@objc private func toggleAdjacent() {
|
|
163
|
+
isAdjacentExpanded.toggle()
|
|
164
|
+
adjacentHeader.text = isAdjacentExpanded ? "▼ Adjacent Players" : "▶ Adjacent Players"
|
|
165
|
+
|
|
166
|
+
UIView.animate(withDuration: 0.25) {
|
|
167
|
+
for label in self.adjacentLabels {
|
|
168
|
+
label.isHidden = !self.isAdjacentExpanded
|
|
169
|
+
label.alpha = self.isAdjacentExpanded ? 1 : 0
|
|
170
|
+
}
|
|
171
|
+
// Resize panel
|
|
172
|
+
var newFrame = self.frame
|
|
173
|
+
let baseHeight = Self.panelHeight
|
|
174
|
+
newFrame.size.height = self.isAdjacentExpanded
|
|
175
|
+
? baseHeight + Self.expandedExtraHeight
|
|
176
|
+
: baseHeight
|
|
177
|
+
self.frame = newFrame
|
|
178
|
+
self.layoutIfNeeded()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func subscribe(to subject: CurrentValueSubject<DebugMetrics, Never>) {
|
|
183
|
+
cancellable = subject
|
|
184
|
+
.receive(on: DispatchQueue.main)
|
|
185
|
+
.sink { [weak self] metrics in
|
|
186
|
+
self?.update(metrics)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
func subscribeAdjacent(
|
|
191
|
+
prev: CurrentValueSubject<DebugMetrics, Never>,
|
|
192
|
+
next: CurrentValueSubject<DebugMetrics, Never>
|
|
193
|
+
) {
|
|
194
|
+
prevCancellable = prev
|
|
195
|
+
.receive(on: DispatchQueue.main)
|
|
196
|
+
.sink { [weak self] metrics in
|
|
197
|
+
self?.updateAdjacent(metrics, slot: .previous)
|
|
198
|
+
}
|
|
199
|
+
nextCancellable = next
|
|
200
|
+
.receive(on: DispatchQueue.main)
|
|
201
|
+
.sink { [weak self] metrics in
|
|
202
|
+
self?.updateAdjacent(metrics, slot: .next)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private func updateAdjacent(_ m: DebugMetrics, slot: AdjacentSlot) {
|
|
207
|
+
let headerLabel = slot == .previous ? prevHeaderLabel : nextHeaderLabel
|
|
208
|
+
let detailLabel = slot == .previous ? prevDetailLabel : nextDetailLabel
|
|
209
|
+
let arrow = slot == .previous ? "▲ Prev" : "▼ Next"
|
|
210
|
+
|
|
211
|
+
guard m.contentId != nil else {
|
|
212
|
+
headerLabel.text = "\(arrow) — not assigned"
|
|
213
|
+
detailLabel.text = ""
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let id = m.contentId.map { String($0.prefix(8)) } ?? "—"
|
|
218
|
+
let stateIcon: String
|
|
219
|
+
switch m.playerState {
|
|
220
|
+
case .playing: stateIcon = "▶"
|
|
221
|
+
case .paused: stateIcon = "⏸"
|
|
222
|
+
case .buffering: stateIcon = "⏳"
|
|
223
|
+
default: stateIcon = "○"
|
|
224
|
+
}
|
|
225
|
+
headerLabel.text = "\(arrow) #\(m.feedIndex) \(id) \(stateIcon) \(m.playerState)"
|
|
226
|
+
|
|
227
|
+
let buf = String(format: "%.1f", m.bufferAhead)
|
|
228
|
+
let preroll = m.prerollCompleted ? "✓" : "✗"
|
|
229
|
+
let mbps = m.observedBitrateBps / 1_000_000
|
|
230
|
+
var detail = " buf: \(buf)s preroll: \(preroll)"
|
|
231
|
+
if mbps > 0 {
|
|
232
|
+
detail += String(format: " %.1f Mbps", mbps)
|
|
233
|
+
}
|
|
234
|
+
if m.videoWidth > 0 {
|
|
235
|
+
let codec = m.codec ?? ""
|
|
236
|
+
detail += " \(m.videoWidth)×\(m.videoHeight) \(codec)"
|
|
237
|
+
}
|
|
238
|
+
detailLabel.text = detail
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private func update(_ m: DebugMetrics) {
|
|
242
|
+
let id = m.contentId.map { String($0.prefix(8)) } ?? "—"
|
|
243
|
+
let stateIcon: String
|
|
244
|
+
switch m.playerState {
|
|
245
|
+
case .playing: stateIcon = "▶"
|
|
246
|
+
case .paused: stateIcon = "⏸"
|
|
247
|
+
case .buffering: stateIcon = "⏳"
|
|
248
|
+
case .seeking: stateIcon = "⏩"
|
|
249
|
+
case .error: stateIcon = "⚠️"
|
|
250
|
+
default: stateIcon = "○"
|
|
251
|
+
}
|
|
252
|
+
headerLabel.text = "#\(m.feedIndex) \(id) \(stateIcon) \(m.playerState)"
|
|
253
|
+
|
|
254
|
+
let cur = String(format: "%.1f", m.currentTime)
|
|
255
|
+
let dur = String(format: "%.1f", m.duration)
|
|
256
|
+
positionLabel.text = "Position: \(cur)s / \(dur)s"
|
|
257
|
+
|
|
258
|
+
if let intent = m.intentToFrameMs {
|
|
259
|
+
intentTtffLabel.text = "Intent: \(intent)ms"
|
|
260
|
+
} else {
|
|
261
|
+
intentTtffLabel.text = "Intent: N/A"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if let transition = m.transitionToFrameMs {
|
|
265
|
+
let net = m.networkMs.map { "\($0)" } ?? "?"
|
|
266
|
+
let dec = m.decodeMs.map { "\($0)" } ?? "?"
|
|
267
|
+
transitionTtffLabel.text = "Transition: \(transition)ms (net: \(net) + dec: \(dec))"
|
|
268
|
+
} else {
|
|
269
|
+
transitionTtffLabel.text = "Transition: measuring..."
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let preroll = m.prerollCompleted ? "✓" : "✗"
|
|
273
|
+
let buffer = String(format: "%.1f", m.bufferAhead)
|
|
274
|
+
prerollBufferLabel.text = "Preroll: \(preroll) Buffer: \(buffer)s"
|
|
275
|
+
|
|
276
|
+
let codec = m.codec ?? "—"
|
|
277
|
+
if m.videoWidth > 0 {
|
|
278
|
+
videoQualityLabel.text = "Video: \(m.videoWidth)×\(m.videoHeight) \(codec)"
|
|
279
|
+
} else {
|
|
280
|
+
videoQualityLabel.text = "Video: —"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
thumbnailLabel.text = "Thumbnail: \(m.thumbnailVisible ? "visible" : "removed")"
|
|
284
|
+
prefetchLabel.text = "Prefetch: \(m.thumbsCachedAhead)/\(m.metadataAhead) thumbs \(m.metadataAhead) meta"
|
|
285
|
+
|
|
286
|
+
let obsMbps = m.observedBitrateBps / 1_000_000
|
|
287
|
+
let indMbps = m.indicatedBitrateBps / 1_000_000
|
|
288
|
+
if obsMbps > 0 {
|
|
289
|
+
bitrateLabel.text = String(format: "Bitrate: %.1f Mbps (ind: %.1f)", obsMbps, indMbps)
|
|
290
|
+
} else {
|
|
291
|
+
bitrateLabel.text = "Bitrate: —"
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if let lastMs = m.lastRebufferDurationMs {
|
|
295
|
+
rebufferLabel.text = "Rebuffers: \(m.rebufferCount) (last: \(lastMs)ms)"
|
|
296
|
+
} else {
|
|
297
|
+
rebufferLabel.text = "Rebuffers: \(m.rebufferCount)"
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
loopWatchLabel.text = "Loops: \(m.loopCount)"
|
|
301
|
+
}
|
|
302
|
+
}
|
package/ios/ShortKitBridge.swift
CHANGED
|
@@ -97,6 +97,7 @@ import ShortKitSDK
|
|
|
97
97
|
clientAppName: String?,
|
|
98
98
|
clientAppVersion: String?,
|
|
99
99
|
customDimensions customDimensionsJSON: String?,
|
|
100
|
+
debugPanel: Bool,
|
|
100
101
|
delegate: ShortKitBridgeDelegateProtocol,
|
|
101
102
|
surfacePresenter: AnyObject?
|
|
102
103
|
) {
|
|
@@ -110,7 +111,8 @@ import ShortKitSDK
|
|
|
110
111
|
apiKey: apiKey,
|
|
111
112
|
clientAppName: clientAppName,
|
|
112
113
|
clientAppVersion: clientAppVersion,
|
|
113
|
-
customDimensions: dimensions
|
|
114
|
+
customDimensions: dimensions,
|
|
115
|
+
debugPanelEnabled: debugPanel
|
|
114
116
|
)
|
|
115
117
|
self.shortKit = sdk
|
|
116
118
|
|
|
@@ -676,7 +678,7 @@ import ShortKitSDK
|
|
|
676
678
|
/// Expected JSON structure:
|
|
677
679
|
/// ```json
|
|
678
680
|
/// {"feedHeight":"{\"type\":\"fullscreen\"}","overlay":"\"none\"",
|
|
679
|
-
/// "carouselMode":"\"none\"","
|
|
681
|
+
/// "carouselMode":"\"none\"","muteOnStart":true}
|
|
680
682
|
/// ```
|
|
681
683
|
public static func parseFeedConfig(_ json: String) -> FeedConfig {
|
|
682
684
|
guard let data = json.data(using: .utf8),
|
|
@@ -842,7 +844,7 @@ import ShortKitSDK
|
|
|
842
844
|
}
|
|
843
845
|
|
|
844
846
|
/// Parse a JSON string of FeedFilter from the JS bridge.
|
|
845
|
-
|
|
847
|
+
static func parseFeedFilter(_ json: String?) -> FeedFilter? {
|
|
846
848
|
guard let json,
|
|
847
849
|
let data = json.data(using: .utf8),
|
|
848
850
|
let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
@@ -104,6 +104,21 @@ import ShortKitSDK
|
|
|
104
104
|
let feedVC = ShortKitFeedViewController(shortKit: sdk, config: feedConfig, startAtItemId: startAtItemId)
|
|
105
105
|
feedVC.setBridgeManaged()
|
|
106
106
|
|
|
107
|
+
if sdk.debugPanelEnabled {
|
|
108
|
+
feedVC.debugPanelFactory = { active, prev, next in
|
|
109
|
+
let panel = DebugPanelView(frame: CGRect(
|
|
110
|
+
x: 0, y: 0,
|
|
111
|
+
width: DebugPanelView.panelWidth,
|
|
112
|
+
height: DebugPanelView.panelHeight
|
|
113
|
+
))
|
|
114
|
+
panel.center = CGPoint(x: UIScreen.main.bounds.width / 2,
|
|
115
|
+
y: UIScreen.main.bounds.height / 2)
|
|
116
|
+
panel.subscribe(to: active)
|
|
117
|
+
panel.subscribeAdjacent(prev: prev, next: next)
|
|
118
|
+
return panel
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
107
122
|
NSLog("[ShortKit FeedView] VC created successfully (bridge-managed)")
|
|
108
123
|
|
|
109
124
|
feedVC.onDismiss = {
|
package/ios/ShortKitModule.mm
CHANGED
|
@@ -68,7 +68,6 @@ RCT_EXPORT_MODULE(ShortKitModule)
|
|
|
68
68
|
@"onFormatChange",
|
|
69
69
|
@"onPrefetchedAheadCountChanged",
|
|
70
70
|
@"onRemainingContentCountChanged",
|
|
71
|
-
@"onSurveyResponse",
|
|
72
71
|
@"onContentTapped",
|
|
73
72
|
@"onDismiss",
|
|
74
73
|
@"onFeedReady",
|
|
@@ -124,7 +123,8 @@ RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
|
|
|
124
123
|
hasLoadingView:(BOOL)hasLoadingView
|
|
125
124
|
clientAppName:(NSString *)clientAppName
|
|
126
125
|
clientAppVersion:(NSString *)clientAppVersion
|
|
127
|
-
customDimensions:(NSString *)customDimensions
|
|
126
|
+
customDimensions:(NSString *)customDimensions
|
|
127
|
+
debugPanel:(BOOL)debugPanel) {
|
|
128
128
|
// Tear down any existing instance to prevent leaks on re-initialize
|
|
129
129
|
[_shortKitBridge teardown];
|
|
130
130
|
|
|
@@ -133,6 +133,7 @@ RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
|
|
|
133
133
|
clientAppName:clientAppName
|
|
134
134
|
clientAppVersion:clientAppVersion
|
|
135
135
|
customDimensions:customDimensions
|
|
136
|
+
debugPanel:debugPanel
|
|
136
137
|
delegate:self
|
|
137
138
|
surfacePresenter:_surfacePresenter];
|
|
138
139
|
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
<key>CFBundlePackageType</key>
|
|
12
12
|
<string>FMWK</string>
|
|
13
13
|
<key>CFBundleVersion</key>
|
|
14
|
-
<string>0.2.
|
|
14
|
+
<string>0.2.22</string>
|
|
15
15
|
<key>CFBundleShortVersionString</key>
|
|
16
|
-
<string>0.2.
|
|
16
|
+
<string>0.2.22</string>
|
|
17
17
|
<key>MinimumOSVersion</key>
|
|
18
18
|
<string>16.0</string>
|
|
19
19
|
</dict>
|