@shortkitsdk/react-native 0.2.23 → 0.2.24

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.
Files changed (29) hide show
  1. package/android/libs/shortkit-release.aar +0 -0
  2. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +10 -7
  3. package/ios/ShortKitBridge.swift +97 -2
  4. package/ios/ShortKitModule.mm +23 -0
  5. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
  6. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1257 -176
  7. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +24 -1
  8. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  9. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +24 -1
  10. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  11. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
  12. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
  13. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1257 -176
  14. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +24 -1
  15. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  16. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +24 -1
  17. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1257 -176
  18. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +24 -1
  19. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  20. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +24 -1
  21. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  22. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
  23. package/package.json +1 -1
  24. package/src/ShortKitCommands.ts +3 -0
  25. package/src/ShortKitContext.ts +6 -0
  26. package/src/ShortKitProvider.tsx +45 -1
  27. package/src/index.ts +2 -0
  28. package/src/specs/NativeShortKitModule.ts +29 -0
  29. package/src/types.ts +44 -1
@@ -521,7 +521,7 @@ extension ShortKitSDK.ContentItem {
521
521
  public enum FeedInput : Swift.Sendable {
522
522
  case video(playbackId: Swift.String, fallbackUrl: Swift.String? = nil)
523
523
  case imageCarousel(ShortKitSDK.ImageCarouselItem)
524
- case videoCarousel(ShortKitSDK.VideoCarouselItem)
524
+ case videoCarousel(ShortKitSDK.VideoCarouselInput)
525
525
  }
526
526
  public enum FeedItem : Swift.Sendable {
527
527
  case content(ShortKitSDK.ContentItem)
@@ -665,6 +665,19 @@ public struct VTTCue : Swift.Equatable, Swift.Sendable {
665
665
  public init(startTime: Swift.Double, endTime: Swift.Double, text: Swift.String)
666
666
  public static func == (a: ShortKitSDK.VTTCue, b: ShortKitSDK.VTTCue) -> Swift.Bool
667
667
  }
668
+ public struct VideoCarouselInput : Swift.Codable, Swift.Equatable, Swift.Sendable {
669
+ public let id: Swift.String
670
+ public let videos: [ShortKitSDK.VideoCarouselVideoInput]
671
+ public let title: Swift.String?
672
+ public let description: Swift.String?
673
+ public let author: Swift.String?
674
+ public let section: Swift.String?
675
+ public let articleUrl: Swift.String?
676
+ public init(id: Swift.String, videos: [ShortKitSDK.VideoCarouselVideoInput], title: Swift.String? = nil, description: Swift.String? = nil, author: Swift.String? = nil, section: Swift.String? = nil, articleUrl: Swift.String? = nil)
677
+ public static func == (a: ShortKitSDK.VideoCarouselInput, b: ShortKitSDK.VideoCarouselInput) -> Swift.Bool
678
+ public func encode(to encoder: any Swift.Encoder) throws
679
+ public init(from decoder: any Swift.Decoder) throws
680
+ }
668
681
  public struct VideoCarouselItem : Swift.Codable, Swift.Equatable, Swift.Sendable {
669
682
  public let id: Swift.String
670
683
  public let videos: [ShortKitSDK.ContentItem]
@@ -678,6 +691,14 @@ public struct VideoCarouselItem : Swift.Codable, Swift.Equatable, Swift.Sendable
678
691
  public func encode(to encoder: any Swift.Encoder) throws
679
692
  public init(from decoder: any Swift.Decoder) throws
680
693
  }
694
+ public struct VideoCarouselVideoInput : Swift.Codable, Swift.Equatable, Swift.Sendable {
695
+ public let playbackId: Swift.String
696
+ public let fallbackUrl: Swift.String?
697
+ public init(playbackId: Swift.String, fallbackUrl: Swift.String? = nil)
698
+ public static func == (a: ShortKitSDK.VideoCarouselVideoInput, b: ShortKitSDK.VideoCarouselVideoInput) -> Swift.Bool
699
+ public func encode(to encoder: any Swift.Encoder) throws
700
+ public init(from decoder: any Swift.Decoder) throws
701
+ }
681
702
  public protocol AdOverlay : AnyObject {
682
703
  func configure(with content: ShortKitSDK.NativeAdContent)
683
704
  func resetState()
@@ -689,6 +710,7 @@ public protocol CarouselOverlay : AnyObject {
689
710
  var wantsNativeImagePrefetch: Swift.Bool { get }
690
711
  func activatePlayback()
691
712
  func updateActiveImage(index: Swift.Int)
713
+ func resetToFirstImage()
692
714
  }
693
715
  extension ShortKitSDK.CarouselOverlay {
694
716
  public var cachedImage: ((Swift.String) -> UIKit.UIImage?)? {
@@ -700,6 +722,7 @@ extension ShortKitSDK.CarouselOverlay {
700
722
  }
701
723
  public func activatePlayback()
702
724
  public func updateActiveImage(index: Swift.Int)
725
+ public func resetToFirstImage()
703
726
  }
704
727
  final public class CellContent : Combine.ObservableObject {
705
728
  @Combine.Published<ShortKitSDK.ContentItem?> @_projectedValueProperty($item) final public var item: ShortKitSDK.ContentItem? {
@@ -521,7 +521,7 @@ extension ShortKitSDK.ContentItem {
521
521
  public enum FeedInput : Swift.Sendable {
522
522
  case video(playbackId: Swift.String, fallbackUrl: Swift.String? = nil)
523
523
  case imageCarousel(ShortKitSDK.ImageCarouselItem)
524
- case videoCarousel(ShortKitSDK.VideoCarouselItem)
524
+ case videoCarousel(ShortKitSDK.VideoCarouselInput)
525
525
  }
526
526
  public enum FeedItem : Swift.Sendable {
527
527
  case content(ShortKitSDK.ContentItem)
@@ -665,6 +665,19 @@ public struct VTTCue : Swift.Equatable, Swift.Sendable {
665
665
  public init(startTime: Swift.Double, endTime: Swift.Double, text: Swift.String)
666
666
  public static func == (a: ShortKitSDK.VTTCue, b: ShortKitSDK.VTTCue) -> Swift.Bool
667
667
  }
668
+ public struct VideoCarouselInput : Swift.Codable, Swift.Equatable, Swift.Sendable {
669
+ public let id: Swift.String
670
+ public let videos: [ShortKitSDK.VideoCarouselVideoInput]
671
+ public let title: Swift.String?
672
+ public let description: Swift.String?
673
+ public let author: Swift.String?
674
+ public let section: Swift.String?
675
+ public let articleUrl: Swift.String?
676
+ public init(id: Swift.String, videos: [ShortKitSDK.VideoCarouselVideoInput], title: Swift.String? = nil, description: Swift.String? = nil, author: Swift.String? = nil, section: Swift.String? = nil, articleUrl: Swift.String? = nil)
677
+ public static func == (a: ShortKitSDK.VideoCarouselInput, b: ShortKitSDK.VideoCarouselInput) -> Swift.Bool
678
+ public func encode(to encoder: any Swift.Encoder) throws
679
+ public init(from decoder: any Swift.Decoder) throws
680
+ }
668
681
  public struct VideoCarouselItem : Swift.Codable, Swift.Equatable, Swift.Sendable {
669
682
  public let id: Swift.String
670
683
  public let videos: [ShortKitSDK.ContentItem]
@@ -678,6 +691,14 @@ public struct VideoCarouselItem : Swift.Codable, Swift.Equatable, Swift.Sendable
678
691
  public func encode(to encoder: any Swift.Encoder) throws
679
692
  public init(from decoder: any Swift.Decoder) throws
680
693
  }
694
+ public struct VideoCarouselVideoInput : Swift.Codable, Swift.Equatable, Swift.Sendable {
695
+ public let playbackId: Swift.String
696
+ public let fallbackUrl: Swift.String?
697
+ public init(playbackId: Swift.String, fallbackUrl: Swift.String? = nil)
698
+ public static func == (a: ShortKitSDK.VideoCarouselVideoInput, b: ShortKitSDK.VideoCarouselVideoInput) -> Swift.Bool
699
+ public func encode(to encoder: any Swift.Encoder) throws
700
+ public init(from decoder: any Swift.Decoder) throws
701
+ }
681
702
  public protocol AdOverlay : AnyObject {
682
703
  func configure(with content: ShortKitSDK.NativeAdContent)
683
704
  func resetState()
@@ -689,6 +710,7 @@ public protocol CarouselOverlay : AnyObject {
689
710
  var wantsNativeImagePrefetch: Swift.Bool { get }
690
711
  func activatePlayback()
691
712
  func updateActiveImage(index: Swift.Int)
713
+ func resetToFirstImage()
692
714
  }
693
715
  extension ShortKitSDK.CarouselOverlay {
694
716
  public var cachedImage: ((Swift.String) -> UIKit.UIImage?)? {
@@ -700,6 +722,7 @@ extension ShortKitSDK.CarouselOverlay {
700
722
  }
701
723
  public func activatePlayback()
702
724
  public func updateActiveImage(index: Swift.Int)
725
+ public func resetToFirstImage()
703
726
  }
704
727
  final public class CellContent : Combine.ObservableObject {
705
728
  @Combine.Published<ShortKitSDK.ContentItem?> @_projectedValueProperty($item) final public var item: ShortKitSDK.ContentItem? {
@@ -10,39 +10,39 @@
10
10
  </data>
11
11
  <key>Info.plist</key>
12
12
  <data>
13
- slY81X2CJlE1Pc6TjenuCp90CSs=
13
+ aVOP/OHkEUzNyBoUHTT2iiGJbu4=
14
14
  </data>
15
15
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json</key>
16
16
  <data>
17
- 0TmEtkdyeDtGF9z1GonyKRX1OIk=
17
+ mxcDambiFIzUbFTPfCTaKwjGz/8=
18
18
  </data>
19
19
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
20
20
  <data>
21
- EyeJYhkTLApLeAoODyn7P3qdVZY=
21
+ wayE2tftrhjvaEAekKB20ZGO36g=
22
22
  </data>
23
23
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
24
24
  <data>
25
- HE+XPfFpv4kMMRInspsxHhhXkw4=
25
+ OXtQVstuyP3hqDsSczrvdpR/BZY=
26
26
  </data>
27
27
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
28
28
  <data>
29
- EyeJYhkTLApLeAoODyn7P3qdVZY=
29
+ wayE2tftrhjvaEAekKB20ZGO36g=
30
30
  </data>
31
31
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
32
32
  <data>
33
- 0TmEtkdyeDtGF9z1GonyKRX1OIk=
33
+ mxcDambiFIzUbFTPfCTaKwjGz/8=
34
34
  </data>
35
35
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
36
36
  <data>
37
- 9KZgxBHMYkTCwRzoy4Ka8H+EAgY=
37
+ c5HZCcsGjJPhu5ppN3Wq4SsCx2A=
38
38
  </data>
39
39
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
40
40
  <data>
41
- pGKYJUkUKWstUmgNirkMz9UZfXw=
41
+ IjquwfiAxSFG2n0IR/+e9HvnZCM=
42
42
  </data>
43
43
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
44
44
  <data>
45
- 9KZgxBHMYkTCwRzoy4Ka8H+EAgY=
45
+ c5HZCcsGjJPhu5ppN3Wq4SsCx2A=
46
46
  </data>
47
47
  <key>Modules/module.modulemap</key>
48
48
  <data>
@@ -66,56 +66,56 @@
66
66
  <dict>
67
67
  <key>hash2</key>
68
68
  <data>
69
- 8YuVfF0QAXEqQa3D9XBHjhehjxHFdKOWvdGWcWcpL1I=
69
+ s3LSfH8aLgl3mic08rsi958gHLv6ImNC8MsZtFoy8u0=
70
70
  </data>
71
71
  </dict>
72
72
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
73
73
  <dict>
74
74
  <key>hash2</key>
75
75
  <data>
76
- YocaRcQ6ILnEzQPEMCG/TpddeUKnFr5CCG6d7lwMs6c=
76
+ cYm595F0j0GSGb/GBTR7QHYSuFeUw9ZTPc8F87PKzvA=
77
77
  </data>
78
78
  </dict>
79
79
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
80
80
  <dict>
81
81
  <key>hash2</key>
82
82
  <data>
83
- 8D3JzqWKNEUX1+kjgT8Fm/WQRi9dkyAGOowk2wYL2eM=
83
+ 9FsW2XMD0E0AiZ7mHDBgLg/OeCcSwB2BmWG3PuPqPwM=
84
84
  </data>
85
85
  </dict>
86
86
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
87
87
  <dict>
88
88
  <key>hash2</key>
89
89
  <data>
90
- YocaRcQ6ILnEzQPEMCG/TpddeUKnFr5CCG6d7lwMs6c=
90
+ cYm595F0j0GSGb/GBTR7QHYSuFeUw9ZTPc8F87PKzvA=
91
91
  </data>
92
92
  </dict>
93
93
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
94
94
  <dict>
95
95
  <key>hash2</key>
96
96
  <data>
97
- 8YuVfF0QAXEqQa3D9XBHjhehjxHFdKOWvdGWcWcpL1I=
97
+ s3LSfH8aLgl3mic08rsi958gHLv6ImNC8MsZtFoy8u0=
98
98
  </data>
99
99
  </dict>
100
100
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
101
101
  <dict>
102
102
  <key>hash2</key>
103
103
  <data>
104
- une5ImatYPq9T1HlZHjrO8WrjG1AM7s7ft783RqMgSU=
104
+ QRoqxRRNskUOSMbf+RlD6fMBj0mmhgmOres7jwvebiQ=
105
105
  </data>
106
106
  </dict>
107
107
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
108
108
  <dict>
109
109
  <key>hash2</key>
110
110
  <data>
111
- Jv93EDTJvoM0SmW3gXGty+JtS4YoRc2PxBLpd8aPvYw=
111
+ R1p/WNYI3bnLOrWsIezOXSJRZEwJ/t8FuoDZCBZQhHg=
112
112
  </data>
113
113
  </dict>
114
114
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
115
115
  <dict>
116
116
  <key>hash2</key>
117
117
  <data>
118
- une5ImatYPq9T1HlZHjrO8WrjG1AM7s7ft783RqMgSU=
118
+ QRoqxRRNskUOSMbf+RlD6fMBj0mmhgmOres7jwvebiQ=
119
119
  </data>
120
120
  </dict>
121
121
  <key>Modules/module.modulemap</key>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shortkitsdk/react-native",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "description": "ShortKit React Native SDK — short-form video feed",
5
5
  "react-native": "src/index",
6
6
  "source": "src/index",
@@ -28,4 +28,7 @@ export const ShortKitCommands = {
28
28
  NativeShortKitModule?.prefetchStoryboard(playbackId),
29
29
  getStoryboardData: (playbackId: string): Promise<string | null> =>
30
30
  NativeShortKitModule?.getStoryboardData(playbackId) ?? Promise.resolve(null),
31
+ downloadVideo: (itemId: string, mode: 'nonInterruptive' | 'interruptive' = 'nonInterruptive'): Promise<string> =>
32
+ NativeShortKitModule?.downloadVideo(itemId, mode) ?? Promise.reject(new Error('ShortKit not initialized')),
33
+ cancelDownload: () => NativeShortKitModule?.cancelDownload(),
31
34
  } as const;
@@ -1,6 +1,7 @@
1
1
  import { createContext } from 'react';
2
2
  import type {
3
3
  ContentItem,
4
+ DownloadState,
4
5
  FeedConfig,
5
6
  FeedFilter,
6
7
  FeedInput,
@@ -51,6 +52,11 @@ export interface ShortKitContextValue {
51
52
  // Storyboard / seek thumbnails
52
53
  prefetchStoryboard: (playbackId: string) => void;
53
54
  getStoryboardData: (playbackId: string) => Promise<StoryboardData | null>;
55
+
56
+ // Download
57
+ downloadState: DownloadState;
58
+ downloadVideo: (itemId: string, mode?: 'nonInterruptive' | 'interruptive') => Promise<string>;
59
+ cancelDownload: () => void;
54
60
  }
55
61
 
56
62
  export const ShortKitContext = createContext<ShortKitContextValue | null>(null);
@@ -6,6 +6,7 @@ import type { ShortKitContextValue } from './ShortKitContext';
6
6
  import type {
7
7
  ShortKitProviderProps,
8
8
  ContentItem,
9
+ DownloadState,
9
10
  FeedConfig,
10
11
  FeedFilter,
11
12
  FeedInput,
@@ -23,6 +24,7 @@ import {
23
24
  deserializePlayerTime,
24
25
  } from './serialization';
25
26
  import NativeShortKitModule from './specs/NativeShortKitModule';
27
+ import { ShortKitCommands } from './ShortKitCommands';
26
28
  import { registerLoadingComponent } from './ShortKitLoadingSurface';
27
29
 
28
30
  // ---------------------------------------------------------------------------
@@ -41,6 +43,7 @@ interface State {
41
43
  prefetchedAheadCount: number;
42
44
  isActive: boolean;
43
45
  feedScrollPhase: FeedScrollPhase | null;
46
+ downloadState: DownloadState;
44
47
  }
45
48
 
46
49
  const initialState: State = {
@@ -55,6 +58,7 @@ const initialState: State = {
55
58
  prefetchedAheadCount: 0,
56
59
  isActive: false,
57
60
  feedScrollPhase: null,
61
+ downloadState: { status: 'idle', progress: 0 },
58
62
  };
59
63
 
60
64
  type Action =
@@ -67,7 +71,11 @@ type Action =
67
71
  | { type: 'CAPTION_TRACK'; payload: CaptionTrack | null }
68
72
  | { type: 'CUE'; payload: { text: string; startTime: number; endTime: number } | null }
69
73
  | { type: 'PREFETCH_COUNT'; payload: number }
70
- | { type: 'FEED_SCROLL_PHASE'; payload: FeedScrollPhase };
74
+ | { type: 'FEED_SCROLL_PHASE'; payload: FeedScrollPhase }
75
+ | { type: 'DOWNLOAD_STARTED'; itemId: string }
76
+ | { type: 'DOWNLOAD_PROGRESS'; itemId: string; progress: number }
77
+ | { type: 'DOWNLOAD_COMPLETED'; itemId: string; fileUrl: string }
78
+ | { type: 'DOWNLOAD_FAILED'; itemId: string; error: string };
71
79
 
72
80
  function reducer(state: State, action: Action): State {
73
81
  switch (action.type) {
@@ -101,6 +109,14 @@ function reducer(state: State, action: Action): State {
101
109
  return { ...state, prefetchedAheadCount: action.payload };
102
110
  case 'FEED_SCROLL_PHASE':
103
111
  return { ...state, feedScrollPhase: action.payload };
112
+ case 'DOWNLOAD_STARTED':
113
+ return { ...state, downloadState: { status: 'downloading', itemId: action.itemId, progress: 0 } };
114
+ case 'DOWNLOAD_PROGRESS':
115
+ return { ...state, downloadState: { ...state.downloadState, progress: action.progress } };
116
+ case 'DOWNLOAD_COMPLETED':
117
+ return { ...state, downloadState: { status: 'completed', itemId: action.itemId, progress: 1, fileUrl: action.fileUrl } };
118
+ case 'DOWNLOAD_FAILED':
119
+ return { ...state, downloadState: { status: 'failed', itemId: action.itemId, progress: 0, error: action.error } };
104
120
  default:
105
121
  return state;
106
122
  }
@@ -327,6 +343,26 @@ export function ShortKitProvider({
327
343
  }),
328
344
  );
329
345
 
346
+ // Download started
347
+ const downloadStartedSub = NativeShortKitModule.onDownloadStarted((event) => {
348
+ dispatch({ type: 'DOWNLOAD_STARTED', itemId: event.itemId });
349
+ });
350
+
351
+ // Download progress
352
+ const downloadProgressSub = NativeShortKitModule.onDownloadProgress((event) => {
353
+ dispatch({ type: 'DOWNLOAD_PROGRESS', itemId: event.itemId, progress: event.progress });
354
+ });
355
+
356
+ // Download completed
357
+ const downloadCompletedSub = NativeShortKitModule.onDownloadCompleted((event) => {
358
+ dispatch({ type: 'DOWNLOAD_COMPLETED', itemId: event.itemId, fileUrl: event.fileUrl });
359
+ });
360
+
361
+ // Download failed
362
+ const downloadFailedSub = NativeShortKitModule.onDownloadFailed((event) => {
363
+ dispatch({ type: 'DOWNLOAD_FAILED', itemId: event.itemId, error: event.error });
364
+ });
365
+
330
366
  // Note: Feed-level callback events (onDidLoop, onFeedTransition,
331
367
  // onDismiss, etc.) are subscribed by the ShortKitFeed component
332
368
  // not here. The provider only manages state-driving events.
@@ -335,6 +371,10 @@ export function ShortKitProvider({
335
371
  for (const sub of subscriptions) {
336
372
  sub.remove();
337
373
  }
374
+ downloadStartedSub.remove();
375
+ downloadProgressSub.remove();
376
+ downloadCompletedSub.remove();
377
+ downloadFailedSub.remove();
338
378
  };
339
379
  }, []);
340
380
 
@@ -447,6 +487,7 @@ export function ShortKitProvider({
447
487
  prefetchedAheadCount: state.prefetchedAheadCount,
448
488
  isActive: state.isActive,
449
489
  feedScrollPhase: state.feedScrollPhase,
490
+ downloadState: state.downloadState,
450
491
 
451
492
  // Commands
452
493
  play,
@@ -467,6 +508,8 @@ export function ShortKitProvider({
467
508
  preloadFeed: preloadFeedCmd,
468
509
  prefetchStoryboard: prefetchStoryboardCmd,
469
510
  getStoryboardData: getStoryboardDataCmd,
511
+ downloadVideo: ShortKitCommands.downloadVideo,
512
+ cancelDownload: ShortKitCommands.cancelDownload,
470
513
  }),
471
514
  [
472
515
  state.playerState,
@@ -480,6 +523,7 @@ export function ShortKitProvider({
480
523
  state.prefetchedAheadCount,
481
524
  state.isActive,
482
525
  state.feedScrollPhase,
526
+ state.downloadState,
483
527
  play,
484
528
  pause,
485
529
  seek,
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ export type {
20
20
  CarouselImage,
21
21
  ImageCarouselItem,
22
22
  VideoCarouselItem,
23
+ VideoCarouselVideoInput,
24
+ VideoCarouselInput,
23
25
  FeedInput,
24
26
  JSONValue,
25
27
  CaptionTrack,
@@ -109,6 +109,25 @@ type FeedReadyEvent = Readonly<{
109
109
  feedId: string;
110
110
  }>;
111
111
 
112
+ type DownloadStartedEvent = Readonly<{
113
+ itemId: string;
114
+ }>;
115
+
116
+ type DownloadProgressEvent = Readonly<{
117
+ itemId: string;
118
+ progress: number;
119
+ }>;
120
+
121
+ type DownloadCompletedEvent = Readonly<{
122
+ itemId: string;
123
+ fileUrl: string;
124
+ }>;
125
+
126
+ type DownloadFailedEvent = Readonly<{
127
+ itemId: string;
128
+ error: string;
129
+ }>;
130
+
112
131
  // --- Overlay per-surface event payload types ---
113
132
 
114
133
  type OverlayActiveEvent = Readonly<{
@@ -216,6 +235,10 @@ export interface Spec extends TurboModule {
216
235
  prefetchStoryboard(playbackId: string): void;
217
236
  getStoryboardData(playbackId: string): Promise<string>;
218
237
 
238
+ // --- Download ---
239
+ downloadVideo(itemId: string, mode: string): Promise<string>;
240
+ cancelDownload(): void;
241
+
219
242
  // --- Event emitters ---
220
243
  readonly onPlayerStateChanged: EventEmitter<PlayerStateEvent>;
221
244
  readonly onCurrentItemChanged: EventEmitter<CurrentItemEvent>;
@@ -249,6 +272,12 @@ export interface Spec extends TurboModule {
249
272
  readonly onOverlayFullState: EventEmitter<OverlayFullStateEvent>;
250
273
  readonly onCarouselActiveImageChanged: EventEmitter<CarouselActiveImageEvent>;
251
274
  readonly onVideoCarouselActiveVideoChanged: EventEmitter<VideoCarouselActiveVideoEvent>;
275
+
276
+ // --- Download events ---
277
+ readonly onDownloadStarted: EventEmitter<DownloadStartedEvent>;
278
+ readonly onDownloadProgress: EventEmitter<DownloadProgressEvent>;
279
+ readonly onDownloadCompleted: EventEmitter<DownloadCompletedEvent>;
280
+ readonly onDownloadFailed: EventEmitter<DownloadFailedEvent>;
252
281
  }
253
282
 
254
283
  export default TurboModuleRegistry.getEnforcing<Spec>('ShortKitModule');
package/src/types.ts CHANGED
@@ -68,6 +68,21 @@ export interface ContentItem {
68
68
  articleUrl?: string;
69
69
  commentCount?: number;
70
70
  fallbackUrl?: string;
71
+ downloadUrl?: string;
72
+ }
73
+
74
+ export type DownloadStatus = 'idle' | 'downloading' | 'completed' | 'failed';
75
+
76
+ export interface DownloadState {
77
+ status: DownloadStatus;
78
+ /** ID of the content item being downloaded, if any. */
79
+ itemId?: string;
80
+ /** 0.0 to 1.0 progress fraction. */
81
+ progress: number;
82
+ /** Local file URL after successful download. */
83
+ fileUrl?: string;
84
+ /** Error message if download failed. */
85
+ error?: string;
71
86
  }
72
87
 
73
88
  // --- Custom Feed Types ---
@@ -98,10 +113,38 @@ export interface VideoCarouselItem {
98
113
  articleUrl?: string;
99
114
  }
100
115
 
116
+ /**
117
+ * Per-slide input for a VideoCarouselInput. Mirrors the `{ type: 'video' }`
118
+ * FeedInput variant — host apps pass a playback ID and the SDK constructs
119
+ * the streaming and thumbnail URLs internally.
120
+ */
121
+ export interface VideoCarouselVideoInput {
122
+ playbackId: string;
123
+ fallbackUrl?: string;
124
+ }
125
+
126
+ /**
127
+ * Compact input type for video carousels passed to
128
+ * `{ type: 'videoCarousel', item: VideoCarouselInput }`.
129
+ *
130
+ * The SDK hydrates each playback ID into a full ContentItem internally
131
+ * before rendering. Overlays continue to receive the fully-hydrated
132
+ * VideoCarouselItem shape via VideoCarouselOverlayProps.carouselItem.
133
+ */
134
+ export interface VideoCarouselInput {
135
+ id: string;
136
+ videos: VideoCarouselVideoInput[];
137
+ title?: string;
138
+ description?: string;
139
+ author?: string;
140
+ section?: string;
141
+ articleUrl?: string;
142
+ }
143
+
101
144
  export type FeedInput =
102
145
  | { type: 'video'; playbackId: string; fallbackUrl?: string }
103
146
  | { type: 'imageCarousel'; item: ImageCarouselItem }
104
- | { type: 'videoCarousel'; item: VideoCarouselItem };
147
+ | { type: 'videoCarousel'; item: VideoCarouselInput };
105
148
 
106
149
  export type JSONValue =
107
150
  | string