scroll indactor playbacks web

This commit is contained in:
2025-10-23 15:15:32 +02:00
parent d1be65cb3f
commit bd26a2fb29
2 changed files with 160 additions and 4 deletions
+159 -3
View File
@@ -1,6 +1,19 @@
import { useIsFocused, useRoute } from "@react-navigation/native";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { FlatList, StyleSheet, View, useWindowDimensions } from "react-native";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import {
FlatList,
Image,
StyleSheet,
View,
useWindowDimensions,
} from "react-native";
import { icons } from "../../assets";
import { projectsRef } from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import { useUser } from "../../providers/UserDataProvider";
@@ -17,7 +30,12 @@ const Playbacks = () => {
const userCache = useRef(new Map());
const { getUserByUid } = useUser() || {};
const isFocused = useIsFocused();
const { data: playbacks = [], loadMore } = useDataFromRef({
const {
data: playbacks = [],
loadMore,
hasMore,
loading,
} = useDataFromRef({
ref: projectsRef.where("playbackUrl", "!=", null),
simpleRef: false,
listener: false,
@@ -213,6 +231,47 @@ const Playbacks = () => {
[windowHeight]
);
const total = playbacks.length;
const indicatorItems = useMemo(() => {
if (total <= 0) return [];
const maxVisible = 7;
let start = 0;
let end = total - 1;
if (total > maxVisible) {
const half = Math.floor(maxVisible / 2);
start = activeIndex - half;
end = start + maxVisible - 1;
if (start < 0) {
end += -start;
start = 0;
}
if (end >= total) {
const overshoot = end - (total - 1);
start = Math.max(0, start - overshoot);
end = total - 1;
}
}
const items = [];
for (let i = start; i <= end; i += 1) {
items.push({
key: `indicator-${i}`,
isActive: i === activeIndex,
isPast: i < activeIndex,
});
}
return items;
}, [total, activeIndex]);
const canScrollUp = total > 0 && activeIndex > 0;
const atLastLoaded = total > 0 && activeIndex >= total - 1;
const canScrollDown = total > 0 && (!atLastLoaded || hasMore);
const showIndicators = total > 0;
const indicatorLabel = useMemo(() => {
if (total <= 0) return null;
if (hasMore || loading) return `${activeIndex + 1}+`;
return `${activeIndex + 1}/${total}`;
}, [total, activeIndex, hasMore, loading]);
return (
<View style={styles.screen}>
{activeVideoUrl ? (
@@ -264,6 +323,50 @@ const Playbacks = () => {
onScroll={onScroll}
scrollEventThrottle={16}
/>
{showIndicators && (
<View style={styles.indicatorContainer} pointerEvents="none">
<Image
source={icons.chevronDown}
resizeMode="contain"
style={[
styles.indicatorArrow,
styles.indicatorArrowUp,
!canScrollUp && styles.indicatorArrowDisabled,
]}
/>
<View style={styles.indicatorDotsWrapper}>
{indicatorItems.map((item, index) => (
<View
key={item.key}
style={[
styles.indicatorDot,
item.isActive && styles.indicatorDotActive,
item.isPast && styles.indicatorDotPast,
index < indicatorItems.length - 1
? styles.indicatorDotSpacing
: null,
]}
/>
))}
{canScrollDown && hasMore && (
<View
style={[styles.indicatorMoreDot, styles.indicatorDotSpacing]}
/>
)}
</View>
{/* {indicatorLabel ? (
<Text style={styles.indicatorLabel}>{indicatorLabel}</Text>
) : null} */}
<Image
source={icons.chevronDown}
resizeMode="contain"
style={[
styles.indicatorArrow,
!canScrollDown && styles.indicatorArrowDisabled,
]}
/>
</View>
)}
</View>
// </Page>
);
@@ -319,4 +422,57 @@ const styles = StyleSheet.create({
fontFamily: FONT_FAMILY.InterMedium,
opacity: 0.9,
},
indicatorContainer: {
position: "absolute",
left: 20,
top: 0,
bottom: 0,
justifyContent: "center",
alignItems: "center",
gap: 16,
},
indicatorArrow: {
width: 20,
height: 20,
tintColor: Palette.white,
opacity: 0.8,
},
indicatorArrowUp: {
transform: [{ rotate: "180deg" }],
},
indicatorArrowDisabled: {
opacity: 0.25,
},
indicatorDotsWrapper: {
alignItems: "center",
},
indicatorDot: {
width: 6,
height: 12,
borderRadius: 3,
backgroundColor: "rgba(255,255,255,0.3)",
},
indicatorDotPast: {
backgroundColor: "rgba(255,255,255,0.55)",
},
indicatorDotActive: {
backgroundColor: Palette.white,
height: 20,
},
indicatorDotSpacing: {
marginVertical: 5,
},
indicatorMoreDot: {
width: 4,
height: 4,
borderRadius: 2,
backgroundColor: Palette.white,
opacity: 0.7,
},
indicatorLabel: {
color: Palette.white,
fontSize: 12,
fontFamily: FONT_FAMILY.InterMedium,
textAlign: "center",
},
});
+1 -1
View File
@@ -43,7 +43,7 @@ const uploadSourceRecording = async ({ uri, uid, projectId }) => {
const extension = guessExtension(uri);
const sourcePath = `users/${uid}/projects/${projectId}/recordings/source-${Date.now()}.${extension}`;
console.log("source path : ", sourcePath);
console.log("[DownloadSongs] uploadSourceRecording source path", {
extension,
sourcePath,