@xhub-reel/feed 0.2.2 → 0.2.3
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.d.mts +130 -27
- package/dist/index.d.ts +130 -27
- package/dist/index.js +469 -162
- package/dist/index.mjs +463 -163
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -4,8 +4,10 @@ var react = require('react');
|
|
|
4
4
|
var core = require('@xhub-reel/core');
|
|
5
5
|
var gestures = require('@xhub-reel/gestures');
|
|
6
6
|
var player = require('@xhub-reel/player');
|
|
7
|
+
var playerEngine = require('@xhub-reel/player-engine');
|
|
7
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
9
|
var api = require('@xhub-reel/core/api');
|
|
10
|
+
var ui = require('@xhub-reel/ui');
|
|
9
11
|
var reactQuery = require('@tanstack/react-query');
|
|
10
12
|
|
|
11
13
|
// src/components/VideoFeed.tsx
|
|
@@ -26,8 +28,9 @@ var videoFeedItemStyles = {
|
|
|
26
28
|
placeholder: {
|
|
27
29
|
position: "absolute",
|
|
28
30
|
inset: 0,
|
|
29
|
-
backgroundSize: "
|
|
30
|
-
backgroundPosition: "center"
|
|
31
|
+
backgroundSize: "contain",
|
|
32
|
+
backgroundPosition: "center",
|
|
33
|
+
backgroundRepeat: "no-repeat"
|
|
31
34
|
},
|
|
32
35
|
// Tap area for play/pause
|
|
33
36
|
tapArea: {
|
|
@@ -612,6 +615,7 @@ function useVideoFeedItemState({
|
|
|
612
615
|
return {
|
|
613
616
|
video,
|
|
614
617
|
isActive,
|
|
618
|
+
priority,
|
|
615
619
|
shouldRenderVideo,
|
|
616
620
|
preload,
|
|
617
621
|
isPreloaded,
|
|
@@ -638,9 +642,245 @@ function useVideoFeedItemState({
|
|
|
638
642
|
handleSeekEnd
|
|
639
643
|
};
|
|
640
644
|
}
|
|
645
|
+
var VideoEnginePoolContext = react.createContext(null);
|
|
646
|
+
function VideoEnginePoolProvider({
|
|
647
|
+
children,
|
|
648
|
+
options,
|
|
649
|
+
config,
|
|
650
|
+
forceHLSJS,
|
|
651
|
+
onSlotStateChange,
|
|
652
|
+
onError,
|
|
653
|
+
onFirstFrameReady
|
|
654
|
+
}) {
|
|
655
|
+
const poolRef = react.useRef(null);
|
|
656
|
+
const [isReady, setIsReady] = react.useState(false);
|
|
657
|
+
const isClient = typeof window !== "undefined";
|
|
658
|
+
react.useEffect(() => {
|
|
659
|
+
if (!poolRef.current && isClient) {
|
|
660
|
+
poolRef.current = playerEngine.createVideoEnginePool({
|
|
661
|
+
...options,
|
|
662
|
+
config,
|
|
663
|
+
forceHLSJS,
|
|
664
|
+
onSlotStateChange,
|
|
665
|
+
onError,
|
|
666
|
+
onFirstFrameReady
|
|
667
|
+
});
|
|
668
|
+
setIsReady(true);
|
|
669
|
+
console.log("[VideoEnginePoolProvider] Pool created and ready");
|
|
670
|
+
}
|
|
671
|
+
return () => {
|
|
672
|
+
poolRef.current?.destroy();
|
|
673
|
+
poolRef.current = null;
|
|
674
|
+
setIsReady(false);
|
|
675
|
+
};
|
|
676
|
+
}, []);
|
|
677
|
+
const getPool = react.useCallback(() => {
|
|
678
|
+
return poolRef.current;
|
|
679
|
+
}, []);
|
|
680
|
+
const contextValue = react.useMemo(
|
|
681
|
+
() => ({
|
|
682
|
+
pool: poolRef.current,
|
|
683
|
+
isReady,
|
|
684
|
+
getPool
|
|
685
|
+
}),
|
|
686
|
+
[isReady, getPool]
|
|
687
|
+
);
|
|
688
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VideoEnginePoolContext.Provider, { value: contextValue, children });
|
|
689
|
+
}
|
|
690
|
+
function useVideoEnginePool() {
|
|
691
|
+
const context = react.useContext(VideoEnginePoolContext);
|
|
692
|
+
if (!context) {
|
|
693
|
+
throw new Error(
|
|
694
|
+
"[useVideoEnginePool] Must be used within a VideoEnginePoolProvider. Wrap your feed component with <VideoEnginePoolProvider>."
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
return context.pool;
|
|
698
|
+
}
|
|
699
|
+
function useVideoEnginePoolOptional() {
|
|
700
|
+
const context = react.useContext(VideoEnginePoolContext);
|
|
701
|
+
return context?.pool ?? null;
|
|
702
|
+
}
|
|
703
|
+
function useVideoEnginePoolReady() {
|
|
704
|
+
const context = react.useContext(VideoEnginePoolContext);
|
|
705
|
+
return context?.isReady ?? false;
|
|
706
|
+
}
|
|
707
|
+
function usePoolOrchestration(currentIndex, videos, enabled = true) {
|
|
708
|
+
const pool = useVideoEnginePoolOptional();
|
|
709
|
+
react.useEffect(() => {
|
|
710
|
+
if (!pool || !enabled || videos.length === 0) return;
|
|
711
|
+
const videoList = videos.map((v) => ({
|
|
712
|
+
id: v.id,
|
|
713
|
+
url: v.url
|
|
714
|
+
}));
|
|
715
|
+
pool.orchestrate(currentIndex, videoList);
|
|
716
|
+
}, [pool, currentIndex, videos, enabled]);
|
|
717
|
+
}
|
|
718
|
+
function usePooledVideo(video, isActive) {
|
|
719
|
+
const pool = useVideoEnginePoolOptional();
|
|
720
|
+
const containerRef = react.useRef(null);
|
|
721
|
+
const attachedRef = react.useRef(false);
|
|
722
|
+
const state = pool?.getState(video.id) ?? null;
|
|
723
|
+
const isReady = pool?.isReady(video.id) ?? false;
|
|
724
|
+
const element = pool?.getElement(video.id) ?? null;
|
|
725
|
+
const setContainerRef = react.useCallback((node) => {
|
|
726
|
+
containerRef.current = node;
|
|
727
|
+
if (!pool || !element) return;
|
|
728
|
+
if (node && !attachedRef.current) {
|
|
729
|
+
node.appendChild(element);
|
|
730
|
+
attachedRef.current = true;
|
|
731
|
+
} else if (!node && attachedRef.current) {
|
|
732
|
+
if (element.parentNode) {
|
|
733
|
+
element.parentNode.removeChild(element);
|
|
734
|
+
}
|
|
735
|
+
attachedRef.current = false;
|
|
736
|
+
}
|
|
737
|
+
}, [pool, element]);
|
|
738
|
+
react.useEffect(() => {
|
|
739
|
+
if (!pool) return;
|
|
740
|
+
if (isActive) {
|
|
741
|
+
pool.activate(video.id).catch((err) => {
|
|
742
|
+
console.warn("[usePooledVideo] Activation failed", video.id, err);
|
|
743
|
+
});
|
|
744
|
+
} else {
|
|
745
|
+
pool.deactivate(video.id);
|
|
746
|
+
}
|
|
747
|
+
}, [pool, video.id, isActive]);
|
|
748
|
+
react.useEffect(() => {
|
|
749
|
+
return () => {
|
|
750
|
+
if (attachedRef.current && element?.parentNode && element.parentNode === containerRef.current) {
|
|
751
|
+
element.parentNode.removeChild(element);
|
|
752
|
+
attachedRef.current = false;
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}, [element]);
|
|
756
|
+
return {
|
|
757
|
+
element,
|
|
758
|
+
isReady,
|
|
759
|
+
state,
|
|
760
|
+
containerRef: setContainerRef
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function usePoolStats() {
|
|
764
|
+
const pool = useVideoEnginePoolOptional();
|
|
765
|
+
return pool?.getStats() ?? null;
|
|
766
|
+
}
|
|
767
|
+
function usePoolMemoryControl() {
|
|
768
|
+
const pool = useVideoEnginePoolOptional();
|
|
769
|
+
const reduceMemory = react.useCallback(() => {
|
|
770
|
+
pool?.reduceMemory();
|
|
771
|
+
}, [pool]);
|
|
772
|
+
const pauseAll = react.useCallback(() => {
|
|
773
|
+
pool?.pauseAll();
|
|
774
|
+
}, [pool]);
|
|
775
|
+
return { reduceMemory, pauseAll };
|
|
776
|
+
}
|
|
641
777
|
var VideoFeedItemPlayer = react.forwardRef(
|
|
642
|
-
({ placeholder, ...props }, ref) => {
|
|
643
|
-
const {
|
|
778
|
+
({ placeholder, forceNative = false, ...props }, ref) => {
|
|
779
|
+
const {
|
|
780
|
+
video,
|
|
781
|
+
videoRef,
|
|
782
|
+
shouldRenderVideo,
|
|
783
|
+
preload,
|
|
784
|
+
isPreloaded,
|
|
785
|
+
initialMuted,
|
|
786
|
+
isActive,
|
|
787
|
+
priority
|
|
788
|
+
} = useVideoFeedItemContext();
|
|
789
|
+
const pool = useVideoEnginePoolOptional();
|
|
790
|
+
const poolReady = useVideoEnginePoolReady();
|
|
791
|
+
const containerRef = react.useRef(null);
|
|
792
|
+
const attachedElementRef = react.useRef(null);
|
|
793
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
794
|
+
const prepareStartedRef = react.useRef(false);
|
|
795
|
+
const usePool = pool && poolReady && !forceNative;
|
|
796
|
+
const attachElement = react.useCallback((element, isReady) => {
|
|
797
|
+
if (!containerRef.current) return false;
|
|
798
|
+
if (attachedElementRef.current && attachedElementRef.current.parentNode) {
|
|
799
|
+
attachedElementRef.current.parentNode.removeChild(attachedElementRef.current);
|
|
800
|
+
}
|
|
801
|
+
Object.assign(element.style, {
|
|
802
|
+
position: "absolute",
|
|
803
|
+
top: "0",
|
|
804
|
+
left: "0",
|
|
805
|
+
width: "100%",
|
|
806
|
+
height: "100%",
|
|
807
|
+
objectFit: "contain",
|
|
808
|
+
zIndex: "1",
|
|
809
|
+
opacity: isReady ? "1" : "0",
|
|
810
|
+
transition: "opacity 0.15s ease-out"
|
|
811
|
+
});
|
|
812
|
+
const handleCanPlay = () => {
|
|
813
|
+
element.style.opacity = "1";
|
|
814
|
+
setIsLoading(false);
|
|
815
|
+
};
|
|
816
|
+
if (element.readyState >= 3) {
|
|
817
|
+
handleCanPlay();
|
|
818
|
+
} else {
|
|
819
|
+
element.addEventListener("canplay", handleCanPlay, { once: true });
|
|
820
|
+
}
|
|
821
|
+
containerRef.current.appendChild(element);
|
|
822
|
+
attachedElementRef.current = element;
|
|
823
|
+
if (typeof ref === "function") {
|
|
824
|
+
ref(element);
|
|
825
|
+
} else if (ref) {
|
|
826
|
+
ref.current = element;
|
|
827
|
+
}
|
|
828
|
+
videoRef.current = element;
|
|
829
|
+
return true;
|
|
830
|
+
}, [ref, videoRef]);
|
|
831
|
+
react.useEffect(() => {
|
|
832
|
+
if (!usePool || !pool || !shouldRenderVideo) {
|
|
833
|
+
setIsLoading(false);
|
|
834
|
+
prepareStartedRef.current = false;
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const videoId = video.id;
|
|
838
|
+
const videoUrl = video.url;
|
|
839
|
+
const existingElement = pool.getElement(videoId);
|
|
840
|
+
if (existingElement) {
|
|
841
|
+
const isReady = pool.isReady(videoId);
|
|
842
|
+
const attached = attachElement(existingElement, isReady);
|
|
843
|
+
if (attached) {
|
|
844
|
+
if (!isReady) {
|
|
845
|
+
setIsLoading(true);
|
|
846
|
+
pool.prepare(videoId, videoUrl, { priority: "high" }).catch(() => {
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
console.log("[VideoFeedItemPlayer] Attached existing element:", videoId, { isReady });
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (prepareStartedRef.current) return;
|
|
854
|
+
prepareStartedRef.current = true;
|
|
855
|
+
setIsLoading(true);
|
|
856
|
+
const poolPriority = isActive ? "high" : priority === "high" ? "high" : "medium";
|
|
857
|
+
pool.prepare(videoId, videoUrl, { priority: poolPriority }).then(() => {
|
|
858
|
+
const element = pool.getElement(videoId);
|
|
859
|
+
if (element) {
|
|
860
|
+
attachElement(element, true);
|
|
861
|
+
console.log("[VideoFeedItemPlayer] Prepared and attached:", videoId);
|
|
862
|
+
}
|
|
863
|
+
}).catch((err) => {
|
|
864
|
+
console.warn("[VideoFeedItemPlayer] Prepare failed:", videoId, err);
|
|
865
|
+
setIsLoading(false);
|
|
866
|
+
});
|
|
867
|
+
return () => {
|
|
868
|
+
if (attachedElementRef.current && attachedElementRef.current.parentNode === containerRef.current) {
|
|
869
|
+
containerRef.current?.removeChild(attachedElementRef.current);
|
|
870
|
+
}
|
|
871
|
+
attachedElementRef.current = null;
|
|
872
|
+
prepareStartedRef.current = false;
|
|
873
|
+
};
|
|
874
|
+
}, [usePool, pool, video.id, video.url, shouldRenderVideo, isActive, priority, attachElement]);
|
|
875
|
+
react.useEffect(() => {
|
|
876
|
+
if (!usePool || !pool) return;
|
|
877
|
+
const videoId = video.id;
|
|
878
|
+
if (isActive) {
|
|
879
|
+
pool.activate(videoId);
|
|
880
|
+
} else {
|
|
881
|
+
pool.deactivate(videoId);
|
|
882
|
+
}
|
|
883
|
+
}, [usePool, pool, video.id, isActive]);
|
|
644
884
|
if (!shouldRenderVideo) {
|
|
645
885
|
return placeholder ?? /* @__PURE__ */ jsxRuntime.jsx(
|
|
646
886
|
"div",
|
|
@@ -654,6 +894,67 @@ var VideoFeedItemPlayer = react.forwardRef(
|
|
|
654
894
|
}
|
|
655
895
|
);
|
|
656
896
|
}
|
|
897
|
+
if (usePool) {
|
|
898
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
899
|
+
"div",
|
|
900
|
+
{
|
|
901
|
+
ref: containerRef,
|
|
902
|
+
style: {
|
|
903
|
+
...videoFeedItemStyles.video,
|
|
904
|
+
position: "relative"
|
|
905
|
+
},
|
|
906
|
+
...props,
|
|
907
|
+
children: [
|
|
908
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
909
|
+
@keyframes xhub-reel-spin {
|
|
910
|
+
to { transform: rotate(360deg); }
|
|
911
|
+
}
|
|
912
|
+
` }),
|
|
913
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
914
|
+
"div",
|
|
915
|
+
{
|
|
916
|
+
style: {
|
|
917
|
+
...videoFeedItemStyles.placeholder,
|
|
918
|
+
backgroundImage: `url(${video.thumbnail})`,
|
|
919
|
+
position: "absolute",
|
|
920
|
+
inset: 0,
|
|
921
|
+
zIndex: 0
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
),
|
|
925
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsx(
|
|
926
|
+
"div",
|
|
927
|
+
{
|
|
928
|
+
style: {
|
|
929
|
+
position: "absolute",
|
|
930
|
+
inset: 0,
|
|
931
|
+
display: "flex",
|
|
932
|
+
alignItems: "center",
|
|
933
|
+
justifyContent: "center",
|
|
934
|
+
zIndex: 2,
|
|
935
|
+
pointerEvents: "none"
|
|
936
|
+
},
|
|
937
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
938
|
+
"div",
|
|
939
|
+
{
|
|
940
|
+
style: {
|
|
941
|
+
width: 40,
|
|
942
|
+
height: 40,
|
|
943
|
+
borderWidth: 3,
|
|
944
|
+
borderStyle: "solid",
|
|
945
|
+
borderColor: "rgba(255, 255, 255, 0.2)",
|
|
946
|
+
borderTopColor: "#8B5CF6",
|
|
947
|
+
borderRadius: "50%",
|
|
948
|
+
animation: "xhub-reel-spin 0.8s linear infinite"
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
)
|
|
952
|
+
}
|
|
953
|
+
)
|
|
954
|
+
]
|
|
955
|
+
}
|
|
956
|
+
);
|
|
957
|
+
}
|
|
657
958
|
const showPoster = !isPreloaded;
|
|
658
959
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
659
960
|
"video",
|
|
@@ -703,8 +1004,16 @@ var VideoFeedItemActions = react.forwardRef(
|
|
|
703
1004
|
);
|
|
704
1005
|
VideoFeedItemActions.displayName = "VideoFeedItemActions";
|
|
705
1006
|
var VideoFeedItemTimeline = react.forwardRef(
|
|
706
|
-
({
|
|
1007
|
+
({
|
|
1008
|
+
expanded: expandedProp,
|
|
1009
|
+
showPreview = false,
|
|
1010
|
+
getThumbnailUrl,
|
|
1011
|
+
previewWidth,
|
|
1012
|
+
previewHeight,
|
|
1013
|
+
...props
|
|
1014
|
+
}, ref) => {
|
|
707
1015
|
const {
|
|
1016
|
+
video,
|
|
708
1017
|
videoRef,
|
|
709
1018
|
shouldRenderVideo,
|
|
710
1019
|
timelineExpanded,
|
|
@@ -712,6 +1021,12 @@ var VideoFeedItemTimeline = react.forwardRef(
|
|
|
712
1021
|
handleSeekStart,
|
|
713
1022
|
handleSeekEnd
|
|
714
1023
|
} = useVideoFeedItemContext();
|
|
1024
|
+
const defaultGetThumbnailUrl = react.useCallback(
|
|
1025
|
+
(_time) => {
|
|
1026
|
+
return video.thumbnail || void 0;
|
|
1027
|
+
},
|
|
1028
|
+
[video.thumbnail]
|
|
1029
|
+
);
|
|
715
1030
|
if (!shouldRenderVideo) {
|
|
716
1031
|
return null;
|
|
717
1032
|
}
|
|
@@ -722,7 +1037,11 @@ var VideoFeedItemTimeline = react.forwardRef(
|
|
|
722
1037
|
expanded: expandedProp ?? timelineExpanded,
|
|
723
1038
|
onSeekStart: handleSeekStart,
|
|
724
1039
|
onSeekEnd: handleSeekEnd,
|
|
725
|
-
onExpandedChange: setTimelineExpanded
|
|
1040
|
+
onExpandedChange: setTimelineExpanded,
|
|
1041
|
+
showPreview,
|
|
1042
|
+
getThumbnailUrl: getThumbnailUrl ?? (showPreview ? defaultGetThumbnailUrl : void 0),
|
|
1043
|
+
previewWidth,
|
|
1044
|
+
previewHeight
|
|
726
1045
|
}
|
|
727
1046
|
) });
|
|
728
1047
|
}
|
|
@@ -1555,68 +1874,42 @@ async function prefetchVideoFeed(queryClient, config, params = {}) {
|
|
|
1555
1874
|
initialPageParam: void 0
|
|
1556
1875
|
});
|
|
1557
1876
|
}
|
|
1558
|
-
var
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
backgroundColor: core.colors.accent,
|
|
1590
|
-
color: core.colors.text,
|
|
1591
|
-
border: "none",
|
|
1592
|
-
borderRadius: core.radii.md,
|
|
1593
|
-
fontSize: core.fontSizes.sm,
|
|
1594
|
-
fontWeight: core.fontWeights.semibold,
|
|
1595
|
-
cursor: "pointer"
|
|
1596
|
-
},
|
|
1597
|
-
errorIcon: {
|
|
1598
|
-
fontSize: 48,
|
|
1599
|
-
marginBottom: core.spacing[2]
|
|
1600
|
-
}
|
|
1601
|
-
};
|
|
1602
|
-
var DefaultLoading = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: stateStyles.container, children: [
|
|
1603
|
-
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
1604
|
-
@keyframes xhub-reel-spin {
|
|
1605
|
-
to { transform: rotate(360deg); }
|
|
1877
|
+
var ConnectedVideoFeedInner = react.forwardRef(
|
|
1878
|
+
({
|
|
1879
|
+
videos,
|
|
1880
|
+
isLoading,
|
|
1881
|
+
hasMore,
|
|
1882
|
+
onLoadMore,
|
|
1883
|
+
onVideoChange,
|
|
1884
|
+
initialIndex = 0,
|
|
1885
|
+
usePooling,
|
|
1886
|
+
...feedProps
|
|
1887
|
+
}, ref) => {
|
|
1888
|
+
const [currentIndex, setCurrentIndex] = react.useState(initialIndex);
|
|
1889
|
+
usePoolOrchestration(usePooling ? currentIndex : -1, usePooling ? videos : []);
|
|
1890
|
+
const handleVideoChange = react.useCallback(
|
|
1891
|
+
(video, index) => {
|
|
1892
|
+
setCurrentIndex(index);
|
|
1893
|
+
onVideoChange?.(video, index);
|
|
1894
|
+
},
|
|
1895
|
+
[onVideoChange]
|
|
1896
|
+
);
|
|
1897
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1898
|
+
VideoFeed,
|
|
1899
|
+
{
|
|
1900
|
+
ref,
|
|
1901
|
+
videos,
|
|
1902
|
+
isLoading,
|
|
1903
|
+
hasMore,
|
|
1904
|
+
onLoadMore,
|
|
1905
|
+
onVideoChange: handleVideoChange,
|
|
1906
|
+
initialIndex,
|
|
1907
|
+
...feedProps
|
|
1606
1908
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
var DefaultError = ({ error, retry }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: stateStyles.container, children: [
|
|
1612
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: stateStyles.errorIcon, children: "\u{1F615}" }),
|
|
1613
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: stateStyles.text, children: error.message || "C\xF3 l\u1ED7i x\u1EA3y ra khi t\u1EA3i video" }),
|
|
1614
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { style: stateStyles.button, onClick: retry, children: "Th\u1EED l\u1EA1i" })
|
|
1615
|
-
] });
|
|
1616
|
-
var DefaultEmpty = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: stateStyles.container, children: [
|
|
1617
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: stateStyles.errorIcon, children: "\u{1F4ED}" }),
|
|
1618
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: stateStyles.text, children: "Kh\xF4ng c\xF3 video n\xE0o \u0111\u1EC3 hi\u1EC3n th\u1ECB" })
|
|
1619
|
-
] });
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
);
|
|
1912
|
+
ConnectedVideoFeedInner.displayName = "ConnectedVideoFeedInner";
|
|
1620
1913
|
var ConnectedVideoFeed = react.forwardRef(
|
|
1621
1914
|
({
|
|
1622
1915
|
config: configProp,
|
|
@@ -1626,11 +1919,14 @@ var ConnectedVideoFeed = react.forwardRef(
|
|
|
1626
1919
|
pageSize = 10,
|
|
1627
1920
|
initialVideos,
|
|
1628
1921
|
initialMuted = true,
|
|
1922
|
+
pooling = true,
|
|
1923
|
+
poolConfig,
|
|
1924
|
+
forceHLSJS,
|
|
1629
1925
|
onFetchSuccess,
|
|
1630
1926
|
onFetchError,
|
|
1631
|
-
renderLoading
|
|
1632
|
-
renderError
|
|
1633
|
-
renderEmpty
|
|
1927
|
+
renderLoading,
|
|
1928
|
+
renderError,
|
|
1929
|
+
renderEmpty,
|
|
1634
1930
|
// Pass through VideoFeed props
|
|
1635
1931
|
onVideoChange,
|
|
1636
1932
|
onLike,
|
|
@@ -1666,22 +1962,19 @@ var ConnectedVideoFeed = react.forwardRef(
|
|
|
1666
1962
|
refetch();
|
|
1667
1963
|
}, [refetch]);
|
|
1668
1964
|
if (!config) {
|
|
1669
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1670
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: stateStyles.errorIcon, children: "\u26A0\uFE0F" }),
|
|
1671
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: stateStyles.text, children: "Ch\u01B0a c\u1EA5u h\xECnh API. Vui l\xF2ng wrap component trong XHubReelProvider v\u1EDBi config ho\u1EB7c truy\u1EC1n config prop." })
|
|
1672
|
-
] });
|
|
1965
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.FeedNoConfigState, {});
|
|
1673
1966
|
}
|
|
1674
1967
|
if (isLoading && videos.length === 0) {
|
|
1675
|
-
return renderLoading();
|
|
1968
|
+
return renderLoading ? renderLoading() : /* @__PURE__ */ jsxRuntime.jsx(ui.FeedLoadingState, {});
|
|
1676
1969
|
}
|
|
1677
1970
|
if (error && videos.length === 0) {
|
|
1678
|
-
return renderError(error, handleRetry);
|
|
1971
|
+
return renderError ? renderError(error, handleRetry) : /* @__PURE__ */ jsxRuntime.jsx(ui.FeedErrorState, { error, onRetry: handleRetry });
|
|
1679
1972
|
}
|
|
1680
1973
|
if (!isLoading && videos.length === 0) {
|
|
1681
|
-
return renderEmpty();
|
|
1974
|
+
return renderEmpty ? renderEmpty() : /* @__PURE__ */ jsxRuntime.jsx(ui.FeedEmptyState, {});
|
|
1682
1975
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1976
|
+
const feedContent = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1977
|
+
ConnectedVideoFeedInner,
|
|
1685
1978
|
{
|
|
1686
1979
|
ref,
|
|
1687
1980
|
videos,
|
|
@@ -1694,96 +1987,103 @@ var ConnectedVideoFeed = react.forwardRef(
|
|
|
1694
1987
|
onShare,
|
|
1695
1988
|
onAuthorClick,
|
|
1696
1989
|
initialMuted,
|
|
1990
|
+
usePooling: pooling,
|
|
1697
1991
|
...videoFeedProps
|
|
1698
1992
|
}
|
|
1699
1993
|
);
|
|
1994
|
+
if (pooling) {
|
|
1995
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1996
|
+
VideoEnginePoolProvider,
|
|
1997
|
+
{
|
|
1998
|
+
config: poolConfig,
|
|
1999
|
+
forceHLSJS,
|
|
2000
|
+
onFirstFrameReady: (slot) => {
|
|
2001
|
+
console.log("[ConnectedVideoFeed] First frame ready:", slot.videoId);
|
|
2002
|
+
},
|
|
2003
|
+
onError: (slot, err) => {
|
|
2004
|
+
console.warn("[ConnectedVideoFeed] Slot error:", slot.videoId, err);
|
|
2005
|
+
},
|
|
2006
|
+
children: feedContent
|
|
2007
|
+
}
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
return feedContent;
|
|
1700
2011
|
}
|
|
1701
2012
|
);
|
|
1702
2013
|
ConnectedVideoFeed.displayName = "ConnectedVideoFeed";
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
behavior: smooth ? "smooth" : "auto"
|
|
1730
|
-
});
|
|
1731
|
-
},
|
|
1732
|
-
[scrollRef, itemCount, getItemHeight]
|
|
1733
|
-
);
|
|
1734
|
-
const scrollToNext = react.useCallback(() => {
|
|
1735
|
-
scrollToIndex(currentIndex + 1);
|
|
1736
|
-
}, [currentIndex, scrollToIndex]);
|
|
1737
|
-
const scrollToPrev = react.useCallback(() => {
|
|
1738
|
-
scrollToIndex(currentIndex - 1);
|
|
1739
|
-
}, [currentIndex, scrollToIndex]);
|
|
1740
|
-
react.useEffect(() => {
|
|
1741
|
-
const element = scrollRef.current;
|
|
1742
|
-
if (!element) return;
|
|
1743
|
-
const handleScroll = () => {
|
|
1744
|
-
const now = Date.now();
|
|
1745
|
-
const scrollTop = element.scrollTop;
|
|
1746
|
-
const timeDelta = now - lastScrollTimeRef.current;
|
|
1747
|
-
const scrollDelta = Math.abs(scrollTop - lastScrollTopRef.current);
|
|
1748
|
-
if (timeDelta > 0) {
|
|
1749
|
-
const velocity = scrollDelta / timeDelta * 1e3;
|
|
1750
|
-
setScrollVelocity(velocity);
|
|
1751
|
-
onScrollChange?.(scrollTop, velocity);
|
|
1752
|
-
}
|
|
1753
|
-
const height = getItemHeight();
|
|
1754
|
-
const newIndex = Math.round(scrollTop / height);
|
|
1755
|
-
if (newIndex !== currentIndex && newIndex >= 0 && newIndex < itemCount) {
|
|
1756
|
-
setCurrentIndex(newIndex);
|
|
1757
|
-
onIndexChange?.(newIndex);
|
|
1758
|
-
}
|
|
1759
|
-
setIsScrolling(true);
|
|
1760
|
-
if (scrollTimeoutRef.current) {
|
|
1761
|
-
clearTimeout(scrollTimeoutRef.current);
|
|
2014
|
+
var PooledVideoFeedInner = react.forwardRef(
|
|
2015
|
+
({
|
|
2016
|
+
videos,
|
|
2017
|
+
showTimeline,
|
|
2018
|
+
renderVideoItem,
|
|
2019
|
+
onVideoChangeInternal,
|
|
2020
|
+
onVideoChange,
|
|
2021
|
+
...feedProps
|
|
2022
|
+
}, ref) => {
|
|
2023
|
+
const [currentIndex, setCurrentIndex] = react.useState(feedProps.initialIndex ?? 0);
|
|
2024
|
+
usePoolOrchestration(currentIndex, videos);
|
|
2025
|
+
const handleVideoChange = react.useCallback(
|
|
2026
|
+
(video, index) => {
|
|
2027
|
+
setCurrentIndex(index);
|
|
2028
|
+
onVideoChangeInternal(video, index);
|
|
2029
|
+
onVideoChange?.(video, index);
|
|
2030
|
+
},
|
|
2031
|
+
[onVideoChangeInternal, onVideoChange]
|
|
2032
|
+
);
|
|
2033
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2034
|
+
VideoFeed,
|
|
2035
|
+
{
|
|
2036
|
+
ref,
|
|
2037
|
+
videos,
|
|
2038
|
+
...feedProps,
|
|
2039
|
+
onVideoChange: handleVideoChange
|
|
1762
2040
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
);
|
|
2044
|
+
PooledVideoFeedInner.displayName = "PooledVideoFeedInner";
|
|
2045
|
+
var PooledVideoFeed = react.forwardRef(
|
|
2046
|
+
({
|
|
2047
|
+
poolConfig,
|
|
2048
|
+
forceHLSJS,
|
|
2049
|
+
showTimeline = true,
|
|
2050
|
+
renderVideoItem,
|
|
2051
|
+
onVideoChange,
|
|
2052
|
+
...feedProps
|
|
2053
|
+
}, ref) => {
|
|
2054
|
+
const handleVideoChangeInternal = react.useCallback(
|
|
2055
|
+
(video, index) => {
|
|
2056
|
+
console.log("[PooledVideoFeed] Video changed", { videoId: video.id, index });
|
|
2057
|
+
},
|
|
2058
|
+
[]
|
|
2059
|
+
);
|
|
2060
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2061
|
+
VideoEnginePoolProvider,
|
|
2062
|
+
{
|
|
2063
|
+
config: poolConfig,
|
|
2064
|
+
forceHLSJS,
|
|
2065
|
+
onFirstFrameReady: (slot) => {
|
|
2066
|
+
console.log("[PooledVideoFeed] First frame ready", slot.videoId);
|
|
2067
|
+
},
|
|
2068
|
+
onError: (slot, error) => {
|
|
2069
|
+
console.warn("[PooledVideoFeed] Slot error", slot.videoId, error);
|
|
2070
|
+
},
|
|
2071
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2072
|
+
PooledVideoFeedInner,
|
|
2073
|
+
{
|
|
2074
|
+
ref,
|
|
2075
|
+
showTimeline,
|
|
2076
|
+
renderVideoItem,
|
|
2077
|
+
onVideoChange,
|
|
2078
|
+
onVideoChangeInternal: handleVideoChangeInternal,
|
|
2079
|
+
...feedProps
|
|
2080
|
+
}
|
|
2081
|
+
)
|
|
1775
2082
|
}
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
scrollVelocity,
|
|
1781
|
-
isScrolling,
|
|
1782
|
-
scrollToIndex,
|
|
1783
|
-
scrollToNext,
|
|
1784
|
-
scrollToPrev
|
|
1785
|
-
};
|
|
1786
|
-
}
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
);
|
|
2086
|
+
PooledVideoFeed.displayName = "PooledVideoFeed";
|
|
1787
2087
|
function useInfiniteScroll({
|
|
1788
2088
|
onLoadMore,
|
|
1789
2089
|
isLoading = false,
|
|
@@ -1850,6 +2150,8 @@ Object.defineProperty(exports, "usePreload", {
|
|
|
1850
2150
|
get: function () { return player.usePreload; }
|
|
1851
2151
|
});
|
|
1852
2152
|
exports.ConnectedVideoFeed = ConnectedVideoFeed;
|
|
2153
|
+
exports.PooledVideoFeed = PooledVideoFeed;
|
|
2154
|
+
exports.VideoEnginePoolProvider = VideoEnginePoolProvider;
|
|
1853
2155
|
exports.VideoFeed = VideoFeed;
|
|
1854
2156
|
exports.VideoFeedItem = VideoFeedItem;
|
|
1855
2157
|
exports.VideoFeedItemActions = VideoFeedItemActions;
|
|
@@ -1862,12 +2164,17 @@ exports.getPreloadPriorityForFeed = getPreloadPriorityForFeed;
|
|
|
1862
2164
|
exports.mapPriorityToNumeric = mapPriorityToNumeric;
|
|
1863
2165
|
exports.memoryManager = memoryManager;
|
|
1864
2166
|
exports.prefetchVideoFeed = prefetchVideoFeed;
|
|
1865
|
-
exports.useFeedScroll = useFeedScroll;
|
|
1866
2167
|
exports.useGlobalMemoryState = useGlobalMemoryState;
|
|
1867
2168
|
exports.useInfiniteScroll = useInfiniteScroll;
|
|
1868
2169
|
exports.useMemoryManager = useMemoryManager;
|
|
2170
|
+
exports.usePoolMemoryControl = usePoolMemoryControl;
|
|
2171
|
+
exports.usePoolOrchestration = usePoolOrchestration;
|
|
2172
|
+
exports.usePoolStats = usePoolStats;
|
|
2173
|
+
exports.usePooledVideo = usePooledVideo;
|
|
1869
2174
|
exports.useSwipeAnimation = useSwipeAnimation;
|
|
1870
2175
|
exports.useVideoActivation = useVideoActivation;
|
|
2176
|
+
exports.useVideoEnginePool = useVideoEnginePool;
|
|
2177
|
+
exports.useVideoEnginePoolOptional = useVideoEnginePoolOptional;
|
|
1871
2178
|
exports.useVideoFeed = useVideoFeed;
|
|
1872
2179
|
exports.useVideoFeedItemContext = useVideoFeedItemContext;
|
|
1873
2180
|
exports.useVideoVisibility = useVideoVisibility;
|