@xhub-reels/sdk 0.2.6 → 0.2.12
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/dist/index.cjs +51 -13
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +51 -13
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -444,6 +444,13 @@ var FeedManager = class {
|
|
|
444
444
|
// ═══════════════════════════════════════════
|
|
445
445
|
// PUBLIC API — Prefetch
|
|
446
446
|
// ═══════════════════════════════════════════
|
|
447
|
+
setInitialItems(items) {
|
|
448
|
+
this.prefetchCache = {
|
|
449
|
+
items,
|
|
450
|
+
nextCursor: null,
|
|
451
|
+
timestamp: Date.now()
|
|
452
|
+
};
|
|
453
|
+
}
|
|
447
454
|
async prefetch(ttlMs) {
|
|
448
455
|
if (this.prefetchCache) {
|
|
449
456
|
const ttl = ttlMs ?? this.config.staleTTL;
|
|
@@ -593,16 +600,23 @@ var FeedManager = class {
|
|
|
593
600
|
applyItems(incoming, _nextCursor, append) {
|
|
594
601
|
const { itemsById, displayOrder } = this.store.getState();
|
|
595
602
|
const nextById = new Map(itemsById);
|
|
596
|
-
const existingIds = new Set(displayOrder);
|
|
597
|
-
const newIds = [];
|
|
598
603
|
for (const item of incoming) {
|
|
599
|
-
if (!existingIds.has(item.id)) {
|
|
600
|
-
newIds.push(item.id);
|
|
601
|
-
}
|
|
602
604
|
nextById.set(item.id, item);
|
|
603
605
|
this.accessOrder.set(item.id, Date.now());
|
|
604
606
|
}
|
|
605
|
-
|
|
607
|
+
let nextOrder;
|
|
608
|
+
if (append) {
|
|
609
|
+
const existingIds = new Set(displayOrder);
|
|
610
|
+
const newIds = [];
|
|
611
|
+
for (const item of incoming) {
|
|
612
|
+
if (!existingIds.has(item.id)) {
|
|
613
|
+
newIds.push(item.id);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
nextOrder = [...displayOrder, ...newIds];
|
|
617
|
+
} else {
|
|
618
|
+
nextOrder = incoming.map((item) => item.id);
|
|
619
|
+
}
|
|
606
620
|
if (nextById.size > this.config.maxCacheSize) {
|
|
607
621
|
this.evictLRU(nextById, nextOrder);
|
|
608
622
|
}
|
|
@@ -1130,7 +1144,12 @@ function useSnapAnimation(config = {}) {
|
|
|
1130
1144
|
return { animateSnap, animateBounceBack, cancelAnimation };
|
|
1131
1145
|
}
|
|
1132
1146
|
var SDKContext = react.createContext(null);
|
|
1133
|
-
function ReelsProvider({
|
|
1147
|
+
function ReelsProvider({
|
|
1148
|
+
children,
|
|
1149
|
+
adapters,
|
|
1150
|
+
initialItems,
|
|
1151
|
+
debug = false
|
|
1152
|
+
}) {
|
|
1134
1153
|
const logger = adapters.logger;
|
|
1135
1154
|
const sdkRef = react.useRef(null);
|
|
1136
1155
|
const value = react.useMemo(() => {
|
|
@@ -1141,6 +1160,9 @@ function ReelsProvider({ children, adapters, debug = false }) {
|
|
|
1141
1160
|
sdkRef.current.optimisticManager.destroy();
|
|
1142
1161
|
}
|
|
1143
1162
|
const feedManager = new FeedManager(adapters.dataSource, {}, logger);
|
|
1163
|
+
if (initialItems && initialItems.length > 0) {
|
|
1164
|
+
feedManager.setInitialItems(initialItems);
|
|
1165
|
+
}
|
|
1144
1166
|
const playerEngine = new PlayerEngine(
|
|
1145
1167
|
{},
|
|
1146
1168
|
adapters.analytics,
|
|
@@ -1329,10 +1351,11 @@ var ACTIVE_HLS_DEFAULTS = {
|
|
|
1329
1351
|
maxMaxBufferLength: 15,
|
|
1330
1352
|
capLevelToPlayerSize: true,
|
|
1331
1353
|
startLevel: 0,
|
|
1332
|
-
abrEwmaDefaultEstimate:
|
|
1354
|
+
abrEwmaDefaultEstimate: 2e6,
|
|
1333
1355
|
lowLatencyMode: false,
|
|
1334
1356
|
backBufferLength: 5,
|
|
1335
|
-
enableWorker: true
|
|
1357
|
+
enableWorker: true,
|
|
1358
|
+
startFragPrefetch: true
|
|
1336
1359
|
};
|
|
1337
1360
|
var HOT_HLS_DEFAULTS = {
|
|
1338
1361
|
maxBufferLength: 2,
|
|
@@ -1826,6 +1849,20 @@ function VideoSlotInner({
|
|
|
1826
1849
|
}
|
|
1827
1850
|
}, [mp4Src, isActive, isPrefetch, isPreloaded, isHlsSource]);
|
|
1828
1851
|
const isReady = isHlsSource ? hlsReady : mp4Ready;
|
|
1852
|
+
const [isVideoPlaying, setIsVideoPlaying] = react.useState(false);
|
|
1853
|
+
react.useEffect(() => {
|
|
1854
|
+
const video = videoRef.current;
|
|
1855
|
+
if (!video || !isActive) {
|
|
1856
|
+
setIsVideoPlaying(false);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const onPlaying = () => setIsVideoPlaying(true);
|
|
1860
|
+
video.addEventListener("playing", onPlaying);
|
|
1861
|
+
return () => {
|
|
1862
|
+
video.removeEventListener("playing", onPlaying);
|
|
1863
|
+
setIsVideoPlaying(false);
|
|
1864
|
+
};
|
|
1865
|
+
}, [isActive]);
|
|
1829
1866
|
const [hasPlayedAhead, setHasPlayedAhead] = react.useState(false);
|
|
1830
1867
|
react.useEffect(() => {
|
|
1831
1868
|
const video = videoRef.current;
|
|
@@ -1865,6 +1902,7 @@ function VideoSlotInner({
|
|
|
1865
1902
|
}, [isActive, isReady, hasPlayedAhead]);
|
|
1866
1903
|
react.useEffect(() => {
|
|
1867
1904
|
setHasPlayedAhead(false);
|
|
1905
|
+
setIsVideoPlaying(false);
|
|
1868
1906
|
}, [src]);
|
|
1869
1907
|
const wasActiveRef = react.useRef(false);
|
|
1870
1908
|
const [isManuallyPaused, setIsManuallyPaused] = react.useState(false);
|
|
@@ -1941,7 +1979,7 @@ function VideoSlotInner({
|
|
|
1941
1979
|
if (!video) return;
|
|
1942
1980
|
video.muted = isActive ? isMuted : true;
|
|
1943
1981
|
}, [isMuted, isActive]);
|
|
1944
|
-
const showPosterOverlay = !isReady && !hasPlayedAhead;
|
|
1982
|
+
const showPosterOverlay = isActive ? !isVideoPlaying : !isReady && !hasPlayedAhead;
|
|
1945
1983
|
const isPreDecoded = hasPlayedAhead;
|
|
1946
1984
|
const [showMuteIndicator, setShowMuteIndicator] = react.useState(false);
|
|
1947
1985
|
const muteIndicatorTimer = react.useRef(null);
|
|
@@ -2059,9 +2097,9 @@ function VideoSlotInner({
|
|
|
2059
2097
|
height: "100%",
|
|
2060
2098
|
objectFit: "cover",
|
|
2061
2099
|
// Hide video until ready to avoid black frame flash.
|
|
2062
|
-
// When pre-decoded, skip transition — first frame is already on canvas.
|
|
2100
|
+
// When pre-decoded or active, skip transition — first frame is already on canvas or playing.
|
|
2063
2101
|
opacity: showPosterOverlay ? 0 : 1,
|
|
2064
|
-
transition: isPreDecoded ? "none" : "opacity 0.15s ease"
|
|
2102
|
+
transition: isActive ? "none" : isPreDecoded ? "none" : "opacity 0.15s ease"
|
|
2065
2103
|
}
|
|
2066
2104
|
}
|
|
2067
2105
|
),
|
|
@@ -2075,7 +2113,7 @@ function VideoSlotInner({
|
|
|
2075
2113
|
backgroundSize: "cover",
|
|
2076
2114
|
backgroundPosition: "center",
|
|
2077
2115
|
opacity: showPosterOverlay ? 1 : 0,
|
|
2078
|
-
transition: "opacity 0.15s ease",
|
|
2116
|
+
transition: isActive ? "none" : "opacity 0.15s ease",
|
|
2079
2117
|
pointerEvents: "none"
|
|
2080
2118
|
}
|
|
2081
2119
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -503,6 +503,7 @@ declare class FeedManager {
|
|
|
503
503
|
constructor(dataSource: IDataSource, config?: FeedConfig, logger?: ILogger);
|
|
504
504
|
getDataSource(): IDataSource;
|
|
505
505
|
setDataSource(dataSource: IDataSource, reset?: boolean): void;
|
|
506
|
+
setInitialItems(items: ContentItem[]): void;
|
|
506
507
|
prefetch(ttlMs?: number): Promise<void>;
|
|
507
508
|
hasPrefetchCache(): boolean;
|
|
508
509
|
clearPrefetchCache(): void;
|
|
@@ -781,10 +782,12 @@ interface SDKContextValue {
|
|
|
781
782
|
interface ReelsProviderProps {
|
|
782
783
|
children: ReactNode;
|
|
783
784
|
adapters: SDKAdapters;
|
|
785
|
+
/** Seed initial items into feedManager */
|
|
786
|
+
initialItems?: ContentItem[];
|
|
784
787
|
/** Enable verbose logging (default: false) */
|
|
785
788
|
debug?: boolean;
|
|
786
789
|
}
|
|
787
|
-
declare function ReelsProvider({ children, adapters, debug }: ReelsProviderProps): react_jsx_runtime.JSX.Element;
|
|
790
|
+
declare function ReelsProvider({ children, adapters, initialItems, debug, }: ReelsProviderProps): react_jsx_runtime.JSX.Element;
|
|
788
791
|
declare function useSDK(): SDKContextValue;
|
|
789
792
|
|
|
790
793
|
declare function ReelsFeed({ renderOverlay, renderActions, renderPauseAction, renderLoading, renderEmpty, renderError: _renderError, showFps, loadMoreThreshold, onSlotChange, gestureConfig, snapConfig, initialMuted, onAutoplayBlocked, }: ReelsFeedProps): string | number | bigint | boolean | Iterable<react.ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<react.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -503,6 +503,7 @@ declare class FeedManager {
|
|
|
503
503
|
constructor(dataSource: IDataSource, config?: FeedConfig, logger?: ILogger);
|
|
504
504
|
getDataSource(): IDataSource;
|
|
505
505
|
setDataSource(dataSource: IDataSource, reset?: boolean): void;
|
|
506
|
+
setInitialItems(items: ContentItem[]): void;
|
|
506
507
|
prefetch(ttlMs?: number): Promise<void>;
|
|
507
508
|
hasPrefetchCache(): boolean;
|
|
508
509
|
clearPrefetchCache(): void;
|
|
@@ -781,10 +782,12 @@ interface SDKContextValue {
|
|
|
781
782
|
interface ReelsProviderProps {
|
|
782
783
|
children: ReactNode;
|
|
783
784
|
adapters: SDKAdapters;
|
|
785
|
+
/** Seed initial items into feedManager */
|
|
786
|
+
initialItems?: ContentItem[];
|
|
784
787
|
/** Enable verbose logging (default: false) */
|
|
785
788
|
debug?: boolean;
|
|
786
789
|
}
|
|
787
|
-
declare function ReelsProvider({ children, adapters, debug }: ReelsProviderProps): react_jsx_runtime.JSX.Element;
|
|
790
|
+
declare function ReelsProvider({ children, adapters, initialItems, debug, }: ReelsProviderProps): react_jsx_runtime.JSX.Element;
|
|
788
791
|
declare function useSDK(): SDKContextValue;
|
|
789
792
|
|
|
790
793
|
declare function ReelsFeed({ renderOverlay, renderActions, renderPauseAction, renderLoading, renderEmpty, renderError: _renderError, showFps, loadMoreThreshold, onSlotChange, gestureConfig, snapConfig, initialMuted, onAutoplayBlocked, }: ReelsFeedProps): string | number | bigint | boolean | Iterable<react.ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<react.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
|
package/dist/index.js
CHANGED
|
@@ -438,6 +438,13 @@ var FeedManager = class {
|
|
|
438
438
|
// ═══════════════════════════════════════════
|
|
439
439
|
// PUBLIC API — Prefetch
|
|
440
440
|
// ═══════════════════════════════════════════
|
|
441
|
+
setInitialItems(items) {
|
|
442
|
+
this.prefetchCache = {
|
|
443
|
+
items,
|
|
444
|
+
nextCursor: null,
|
|
445
|
+
timestamp: Date.now()
|
|
446
|
+
};
|
|
447
|
+
}
|
|
441
448
|
async prefetch(ttlMs) {
|
|
442
449
|
if (this.prefetchCache) {
|
|
443
450
|
const ttl = ttlMs ?? this.config.staleTTL;
|
|
@@ -587,16 +594,23 @@ var FeedManager = class {
|
|
|
587
594
|
applyItems(incoming, _nextCursor, append) {
|
|
588
595
|
const { itemsById, displayOrder } = this.store.getState();
|
|
589
596
|
const nextById = new Map(itemsById);
|
|
590
|
-
const existingIds = new Set(displayOrder);
|
|
591
|
-
const newIds = [];
|
|
592
597
|
for (const item of incoming) {
|
|
593
|
-
if (!existingIds.has(item.id)) {
|
|
594
|
-
newIds.push(item.id);
|
|
595
|
-
}
|
|
596
598
|
nextById.set(item.id, item);
|
|
597
599
|
this.accessOrder.set(item.id, Date.now());
|
|
598
600
|
}
|
|
599
|
-
|
|
601
|
+
let nextOrder;
|
|
602
|
+
if (append) {
|
|
603
|
+
const existingIds = new Set(displayOrder);
|
|
604
|
+
const newIds = [];
|
|
605
|
+
for (const item of incoming) {
|
|
606
|
+
if (!existingIds.has(item.id)) {
|
|
607
|
+
newIds.push(item.id);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
nextOrder = [...displayOrder, ...newIds];
|
|
611
|
+
} else {
|
|
612
|
+
nextOrder = incoming.map((item) => item.id);
|
|
613
|
+
}
|
|
600
614
|
if (nextById.size > this.config.maxCacheSize) {
|
|
601
615
|
this.evictLRU(nextById, nextOrder);
|
|
602
616
|
}
|
|
@@ -1124,7 +1138,12 @@ function useSnapAnimation(config = {}) {
|
|
|
1124
1138
|
return { animateSnap, animateBounceBack, cancelAnimation };
|
|
1125
1139
|
}
|
|
1126
1140
|
var SDKContext = createContext(null);
|
|
1127
|
-
function ReelsProvider({
|
|
1141
|
+
function ReelsProvider({
|
|
1142
|
+
children,
|
|
1143
|
+
adapters,
|
|
1144
|
+
initialItems,
|
|
1145
|
+
debug = false
|
|
1146
|
+
}) {
|
|
1128
1147
|
const logger = adapters.logger;
|
|
1129
1148
|
const sdkRef = useRef(null);
|
|
1130
1149
|
const value = useMemo(() => {
|
|
@@ -1135,6 +1154,9 @@ function ReelsProvider({ children, adapters, debug = false }) {
|
|
|
1135
1154
|
sdkRef.current.optimisticManager.destroy();
|
|
1136
1155
|
}
|
|
1137
1156
|
const feedManager = new FeedManager(adapters.dataSource, {}, logger);
|
|
1157
|
+
if (initialItems && initialItems.length > 0) {
|
|
1158
|
+
feedManager.setInitialItems(initialItems);
|
|
1159
|
+
}
|
|
1138
1160
|
const playerEngine = new PlayerEngine(
|
|
1139
1161
|
{},
|
|
1140
1162
|
adapters.analytics,
|
|
@@ -1323,10 +1345,11 @@ var ACTIVE_HLS_DEFAULTS = {
|
|
|
1323
1345
|
maxMaxBufferLength: 15,
|
|
1324
1346
|
capLevelToPlayerSize: true,
|
|
1325
1347
|
startLevel: 0,
|
|
1326
|
-
abrEwmaDefaultEstimate:
|
|
1348
|
+
abrEwmaDefaultEstimate: 2e6,
|
|
1327
1349
|
lowLatencyMode: false,
|
|
1328
1350
|
backBufferLength: 5,
|
|
1329
|
-
enableWorker: true
|
|
1351
|
+
enableWorker: true,
|
|
1352
|
+
startFragPrefetch: true
|
|
1330
1353
|
};
|
|
1331
1354
|
var HOT_HLS_DEFAULTS = {
|
|
1332
1355
|
maxBufferLength: 2,
|
|
@@ -1820,6 +1843,20 @@ function VideoSlotInner({
|
|
|
1820
1843
|
}
|
|
1821
1844
|
}, [mp4Src, isActive, isPrefetch, isPreloaded, isHlsSource]);
|
|
1822
1845
|
const isReady = isHlsSource ? hlsReady : mp4Ready;
|
|
1846
|
+
const [isVideoPlaying, setIsVideoPlaying] = useState(false);
|
|
1847
|
+
useEffect(() => {
|
|
1848
|
+
const video = videoRef.current;
|
|
1849
|
+
if (!video || !isActive) {
|
|
1850
|
+
setIsVideoPlaying(false);
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
const onPlaying = () => setIsVideoPlaying(true);
|
|
1854
|
+
video.addEventListener("playing", onPlaying);
|
|
1855
|
+
return () => {
|
|
1856
|
+
video.removeEventListener("playing", onPlaying);
|
|
1857
|
+
setIsVideoPlaying(false);
|
|
1858
|
+
};
|
|
1859
|
+
}, [isActive]);
|
|
1823
1860
|
const [hasPlayedAhead, setHasPlayedAhead] = useState(false);
|
|
1824
1861
|
useEffect(() => {
|
|
1825
1862
|
const video = videoRef.current;
|
|
@@ -1859,6 +1896,7 @@ function VideoSlotInner({
|
|
|
1859
1896
|
}, [isActive, isReady, hasPlayedAhead]);
|
|
1860
1897
|
useEffect(() => {
|
|
1861
1898
|
setHasPlayedAhead(false);
|
|
1899
|
+
setIsVideoPlaying(false);
|
|
1862
1900
|
}, [src]);
|
|
1863
1901
|
const wasActiveRef = useRef(false);
|
|
1864
1902
|
const [isManuallyPaused, setIsManuallyPaused] = useState(false);
|
|
@@ -1935,7 +1973,7 @@ function VideoSlotInner({
|
|
|
1935
1973
|
if (!video) return;
|
|
1936
1974
|
video.muted = isActive ? isMuted : true;
|
|
1937
1975
|
}, [isMuted, isActive]);
|
|
1938
|
-
const showPosterOverlay = !isReady && !hasPlayedAhead;
|
|
1976
|
+
const showPosterOverlay = isActive ? !isVideoPlaying : !isReady && !hasPlayedAhead;
|
|
1939
1977
|
const isPreDecoded = hasPlayedAhead;
|
|
1940
1978
|
const [showMuteIndicator, setShowMuteIndicator] = useState(false);
|
|
1941
1979
|
const muteIndicatorTimer = useRef(null);
|
|
@@ -2053,9 +2091,9 @@ function VideoSlotInner({
|
|
|
2053
2091
|
height: "100%",
|
|
2054
2092
|
objectFit: "cover",
|
|
2055
2093
|
// Hide video until ready to avoid black frame flash.
|
|
2056
|
-
// When pre-decoded, skip transition — first frame is already on canvas.
|
|
2094
|
+
// When pre-decoded or active, skip transition — first frame is already on canvas or playing.
|
|
2057
2095
|
opacity: showPosterOverlay ? 0 : 1,
|
|
2058
|
-
transition: isPreDecoded ? "none" : "opacity 0.15s ease"
|
|
2096
|
+
transition: isActive ? "none" : isPreDecoded ? "none" : "opacity 0.15s ease"
|
|
2059
2097
|
}
|
|
2060
2098
|
}
|
|
2061
2099
|
),
|
|
@@ -2069,7 +2107,7 @@ function VideoSlotInner({
|
|
|
2069
2107
|
backgroundSize: "cover",
|
|
2070
2108
|
backgroundPosition: "center",
|
|
2071
2109
|
opacity: showPosterOverlay ? 1 : 0,
|
|
2072
|
-
transition: "opacity 0.15s ease",
|
|
2110
|
+
transition: isActive ? "none" : "opacity 0.15s ease",
|
|
2073
2111
|
pointerEvents: "none"
|
|
2074
2112
|
}
|
|
2075
2113
|
}
|