playback styles
This commit is contained in:
@@ -23,7 +23,6 @@ import {
|
||||
useWindowDimensions,
|
||||
} from "react-native";
|
||||
import Carousel from "react-native-reanimated-carousel";
|
||||
import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
import { setGlobal } from "reactn";
|
||||
import { icons, img } from "../../assets";
|
||||
import KaraokeLyrics from "../../components/KaraokeLyrics";
|
||||
@@ -67,6 +66,7 @@ const CommentsPanel = ({
|
||||
commentsCount,
|
||||
onCommentAdded,
|
||||
inputRef,
|
||||
panelHeight,
|
||||
}) => {
|
||||
const { currentUID, currentUserData } = useUser() || {};
|
||||
const [text, setText] = useState("");
|
||||
@@ -177,98 +177,113 @@ const CommentsPanel = ({
|
||||
<BlurView
|
||||
intensity={Platform.OS !== "ios" ? 10 : 20}
|
||||
tint="dark"
|
||||
style={styles.commentsWrapper}
|
||||
style={[
|
||||
styles.commentsWrapper,
|
||||
panelHeight ? { height: panelHeight } : null,
|
||||
]}
|
||||
>
|
||||
<ScrollView
|
||||
ref={scrollRef}
|
||||
contentContainerStyle={styles.commentsContent}
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
onScroll={handleScroll}
|
||||
scrollEventThrottle={120}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<Text style={styles.sectionTitle}>Description</Text>
|
||||
<View style={styles.descriptionCard}>
|
||||
<Text style={styles.descriptionText}>
|
||||
{descriptionText || "Description chanson..."}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={[styles.sectionTitle, styles.sectionSpacing]}>
|
||||
{`Commentaires${commentsCount ? ` (${commentsCount})` : ""}`}
|
||||
</Text>
|
||||
{Array.isArray(comments) && comments.length > 0 ? (
|
||||
comments.map((c, index) => (
|
||||
<View
|
||||
key={c.id}
|
||||
style={[styles.commentRow, index > 0 && styles.commentRowSpacing]}
|
||||
>
|
||||
{c?.profilePicture ? (
|
||||
<ExpoImage
|
||||
source={{ uri: c.profilePicture }}
|
||||
cachePolicy="memory-disk"
|
||||
priority="high"
|
||||
contentFit="cover"
|
||||
transition={100}
|
||||
style={styles.commentAvatar}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.commentAvatarFallback} />
|
||||
)}
|
||||
<View style={styles.commentBubble}>
|
||||
<View style={styles.commentHeader}>
|
||||
<Text style={styles.commentAuthor}>
|
||||
{c?.userId === currentUID ? "vous" : c?.userName || ""}
|
||||
</Text>
|
||||
<Text style={styles.commentMeta}>
|
||||
• {formatRelativeTime(c?.createdAt)}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={styles.commentBody}>{c?.text}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
) : (
|
||||
<Text style={styles.emptyState}>
|
||||
Aucun commentaire pour le moment
|
||||
</Text>
|
||||
)}
|
||||
</ScrollView>
|
||||
<View style={styles.commentInputContainer}>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
placeholder={
|
||||
canComment ? "Commente" : "Connecte-toi pour laisser un commentaire"
|
||||
}
|
||||
placeholderTextColor="#FFFFFF90"
|
||||
editable={canComment}
|
||||
style={[styles.commentInput, !canComment && { opacity: 0.6 }]}
|
||||
/>
|
||||
<Pressable
|
||||
onPress={onSend}
|
||||
disabled={!canComment || !text.trim()}
|
||||
style={({ pressed }) => [
|
||||
styles.sendButton,
|
||||
(!canComment || !text.trim()) && styles.sendButtonDisabled,
|
||||
pressed && canComment && styles.sendButtonPressed,
|
||||
]}
|
||||
<View style={styles.commentsInner}>
|
||||
<ScrollView
|
||||
ref={scrollRef}
|
||||
style={styles.commentsScroll}
|
||||
contentContainerStyle={styles.commentsContent}
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
onScroll={handleScroll}
|
||||
scrollEventThrottle={120}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<Image
|
||||
source={icons.send}
|
||||
style={styles.sendIcon}
|
||||
resizeMode="contain"
|
||||
<Text style={styles.sectionTitle}>Description</Text>
|
||||
<View style={styles.descriptionCard}>
|
||||
<Text style={styles.descriptionText}>
|
||||
{descriptionText || "Description chanson..."}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={[styles.sectionTitle, styles.sectionSpacing]}>
|
||||
{`Commentaires${commentsCount ? ` (${commentsCount})` : ""}`}
|
||||
</Text>
|
||||
{Array.isArray(comments) && comments.length > 0 ? (
|
||||
comments.map((c, index) => (
|
||||
<View
|
||||
key={c.id}
|
||||
style={[
|
||||
styles.commentRow,
|
||||
index > 0 && styles.commentRowSpacing,
|
||||
]}
|
||||
>
|
||||
{c?.profilePicture ? (
|
||||
<ExpoImage
|
||||
source={{ uri: c.profilePicture }}
|
||||
cachePolicy="memory-disk"
|
||||
priority="high"
|
||||
contentFit="cover"
|
||||
transition={100}
|
||||
style={styles.commentAvatar}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.commentAvatarFallback} />
|
||||
)}
|
||||
<View style={styles.commentBubble}>
|
||||
<View style={styles.commentHeader}>
|
||||
<Text style={styles.commentAuthor}>
|
||||
{c?.userId === currentUID ? "vous" : c?.userName || ""}
|
||||
</Text>
|
||||
<Text style={styles.commentMeta}>
|
||||
• {formatRelativeTime(c?.createdAt)}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={styles.commentBody}>{c?.text}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
) : (
|
||||
<Text style={styles.emptyState}>
|
||||
Aucun commentaire pour le moment
|
||||
</Text>
|
||||
)}
|
||||
</ScrollView>
|
||||
<View style={styles.commentInputContainer}>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
placeholder={
|
||||
canComment
|
||||
? "Commente"
|
||||
: "Connecte-toi pour laisser un commentaire"
|
||||
}
|
||||
placeholderTextColor="#FFFFFF90"
|
||||
editable={canComment}
|
||||
style={[styles.commentInput, !canComment && { opacity: 0.6 }]}
|
||||
/>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
onPress={onSend}
|
||||
disabled={!canComment || !text.trim()}
|
||||
style={({ pressed }) => [
|
||||
styles.sendButton,
|
||||
(!canComment || !text.trim()) && styles.sendButtonDisabled,
|
||||
pressed && canComment && styles.sendButtonPressed,
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={icons.send}
|
||||
style={styles.sendIcon}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</BlurView>
|
||||
);
|
||||
};
|
||||
|
||||
const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
const { width: viewportWidth } = useWindowDimensions();
|
||||
const [layoutWidth, setLayoutWidth] = useState(viewportWidth);
|
||||
const { width: viewportWidth, height: viewportHeight } =
|
||||
useWindowDimensions();
|
||||
const [layoutSize, setLayoutSize] = useState({
|
||||
width: viewportWidth,
|
||||
height: viewportHeight,
|
||||
});
|
||||
const commentInputRef = useRef(null);
|
||||
const { currentUID, followUser, unfollowUser } = useUser() || {};
|
||||
const videoUrl = item?.playbackUrl || null;
|
||||
@@ -439,12 +454,16 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
item?.description || item?.title || "Description chanson";
|
||||
|
||||
const handleLayout = useCallback((event) => {
|
||||
const nextWidth = event?.nativeEvent?.layout?.width;
|
||||
if (typeof nextWidth === "number" && nextWidth > 0) {
|
||||
setLayoutWidth(nextWidth);
|
||||
}
|
||||
const { width = 0, height = 0 } = event?.nativeEvent?.layout || {};
|
||||
setLayoutSize((prev) => ({
|
||||
width: width > 0 ? width : prev.width,
|
||||
height: height > 0 ? height : prev.height,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const layoutWidth = layoutSize.width || viewportWidth;
|
||||
const layoutHeight = layoutSize.height || viewportHeight;
|
||||
|
||||
const widePaddingThreshold = 900;
|
||||
const horizontalPadding = layoutWidth >= widePaddingThreshold ? 64 : 32;
|
||||
const innerWidth = Math.max(layoutWidth - horizontalPadding * 2, 0);
|
||||
@@ -454,20 +473,42 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
const minCommentsWidth = 260;
|
||||
const maxCommentsWidth = 420;
|
||||
|
||||
let sideBySide = innerWidth >= 620;
|
||||
const baseInnerWidth = innerWidth > 0 ? innerWidth : viewportWidth;
|
||||
let desiredHeight = Math.max(360, layoutHeight * 0.8);
|
||||
let desiredWidth = desiredHeight * (9 / 16);
|
||||
|
||||
if (desiredWidth > maxVideoWidth) {
|
||||
desiredWidth = maxVideoWidth;
|
||||
desiredHeight = desiredWidth * (16 / 9);
|
||||
}
|
||||
|
||||
if (desiredWidth < minVideoWidth) {
|
||||
desiredWidth = minVideoWidth;
|
||||
desiredHeight = desiredWidth * (16 / 9);
|
||||
}
|
||||
|
||||
let sideBySide =
|
||||
innerWidth >= desiredWidth + minCommentsWidth + gapBetweenColumns;
|
||||
|
||||
let videoWidth = sideBySide
|
||||
? Math.min(maxVideoWidth, Math.max(innerWidth * 0.56, minVideoWidth))
|
||||
: Math.min(innerWidth, maxVideoWidth);
|
||||
? Math.min(maxVideoWidth, Math.max(desiredWidth, minVideoWidth))
|
||||
: Math.min(Math.max(baseInnerWidth * 0.88, minVideoWidth), maxVideoWidth);
|
||||
let videoHeight = videoWidth * (16 / 9);
|
||||
|
||||
if (videoHeight > desiredHeight) {
|
||||
videoHeight = desiredHeight;
|
||||
videoWidth = videoHeight * (9 / 16);
|
||||
}
|
||||
|
||||
let commentsWidth = sideBySide
|
||||
? innerWidth - videoWidth - gapBetweenColumns
|
||||
: innerWidth;
|
||||
: baseInnerWidth;
|
||||
|
||||
if (sideBySide && commentsWidth > maxCommentsWidth) {
|
||||
videoWidth = Math.min(
|
||||
maxVideoWidth,
|
||||
videoWidth + (commentsWidth - maxCommentsWidth)
|
||||
);
|
||||
const excess = commentsWidth - maxCommentsWidth;
|
||||
commentsWidth = maxCommentsWidth;
|
||||
videoWidth = Math.min(maxVideoWidth, videoWidth + excess);
|
||||
videoHeight = videoWidth * (16 / 9);
|
||||
}
|
||||
|
||||
if (sideBySide && commentsWidth < minCommentsWidth) {
|
||||
@@ -475,33 +516,28 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
if (videoWidth - deficit >= minVideoWidth) {
|
||||
videoWidth -= deficit;
|
||||
commentsWidth = minCommentsWidth;
|
||||
videoHeight = videoWidth * (16 / 9);
|
||||
} else {
|
||||
sideBySide = false;
|
||||
videoWidth = Math.min(innerWidth, maxVideoWidth);
|
||||
commentsWidth = innerWidth;
|
||||
videoWidth = Math.min(baseInnerWidth, maxVideoWidth);
|
||||
commentsWidth = baseInnerWidth;
|
||||
videoHeight = videoWidth * (16 / 9);
|
||||
}
|
||||
}
|
||||
|
||||
const baseInnerWidth = innerWidth > 0 ? innerWidth : viewportWidth;
|
||||
const safeVideoWidth = Math.max(
|
||||
minVideoWidth,
|
||||
Math.min(
|
||||
Number.isFinite(videoWidth) ? videoWidth : baseInnerWidth,
|
||||
maxVideoWidth
|
||||
)
|
||||
);
|
||||
const videoCardWidth = sideBySide
|
||||
? safeVideoWidth
|
||||
: Math.min(Math.max(baseInnerWidth * 0.88, minVideoWidth), maxVideoWidth);
|
||||
if (!sideBySide) {
|
||||
videoHeight = Math.min(videoHeight, desiredHeight);
|
||||
videoWidth = videoHeight * (9 / 16);
|
||||
commentsWidth = videoWidth;
|
||||
}
|
||||
|
||||
const panelHeight = Math.min(videoHeight, desiredHeight);
|
||||
|
||||
const containerHeight = viewportHeight;
|
||||
const verticalPadding = Math.max((layoutHeight - panelHeight) / 2, 24);
|
||||
const safeCommentsWidth = sideBySide
|
||||
? Math.max(
|
||||
minCommentsWidth,
|
||||
Math.min(
|
||||
Number.isFinite(commentsWidth) ? commentsWidth : baseInnerWidth,
|
||||
maxCommentsWidth
|
||||
)
|
||||
)
|
||||
: 0;
|
||||
? Math.max(minCommentsWidth, Math.min(commentsWidth, maxCommentsWidth))
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -510,8 +546,9 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
styles.itemContainerBase,
|
||||
sideBySide ? styles.itemContainerRow : styles.itemContainerCompact,
|
||||
{
|
||||
height: responsiveHeight(70),
|
||||
height: "90%",
|
||||
paddingHorizontal: sideBySide ? horizontalPadding : 24,
|
||||
paddingVertical: verticalPadding,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -519,11 +556,19 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
style={[
|
||||
styles.videoColumn,
|
||||
sideBySide
|
||||
? { width: safeVideoWidth, marginRight: gapBetweenColumns }
|
||||
? { width: videoWidth, marginRight: gapBetweenColumns }
|
||||
: { width: "100%" },
|
||||
]}
|
||||
>
|
||||
<View style={[styles.videoSurface, { width: videoCardWidth }]}>
|
||||
<View
|
||||
style={[
|
||||
styles.videoSurface,
|
||||
{
|
||||
// width: videoWidth,
|
||||
height: panelHeight,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.videoContainer}>
|
||||
{!!videoUrl ? (
|
||||
<VideoView
|
||||
@@ -689,8 +734,8 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
style={[
|
||||
styles.commentsColumn,
|
||||
sideBySide
|
||||
? { width: safeCommentsWidth }
|
||||
: styles.commentsColumnCompact,
|
||||
? { width: safeCommentsWidth, height: panelHeight }
|
||||
: [styles.commentsColumnCompact, { height: panelHeight }],
|
||||
]}
|
||||
>
|
||||
<CommentsPanel
|
||||
@@ -701,6 +746,7 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
setCommentsCount((c) => Math.max(0, Number(c || 0) + 1))
|
||||
}
|
||||
inputRef={commentInputRef}
|
||||
panelHeight={panelHeight}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -726,12 +772,10 @@ const Playbacks = () => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (Platform.OS === "web") {
|
||||
setGlobal({ webLayoutMode: "playbacks-wide" });
|
||||
return () => setGlobal({ webLayoutMode: "default" });
|
||||
}
|
||||
return undefined;
|
||||
}, []);
|
||||
if (Platform.OS !== "web") return undefined;
|
||||
setGlobal({ webLayoutMode: isFocused ? "playbacks-wide" : "default" });
|
||||
return () => setGlobal({ webLayoutMode: "default" });
|
||||
}, [isFocused]);
|
||||
|
||||
const onSnap = useCallback(
|
||||
(index) => {
|
||||
@@ -790,11 +834,9 @@ export default Playbacks;
|
||||
const styles = StyleSheet.create({
|
||||
screen: {
|
||||
flex: 1,
|
||||
// backgroundColor: Palette.darkPurple,
|
||||
},
|
||||
itemContainerBase: {
|
||||
width: "100%",
|
||||
// backgroundColor: Palette.darkPurple,
|
||||
paddingVertical: 36,
|
||||
alignItems: "center",
|
||||
},
|
||||
@@ -814,8 +856,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
videoSurface: {
|
||||
alignSelf: "center",
|
||||
width: "100%",
|
||||
maxWidth: 620,
|
||||
// width: 100,
|
||||
// maxWidth: 400,
|
||||
aspectRatio: 9 / 16,
|
||||
borderRadius: 32,
|
||||
overflow: "hidden",
|
||||
@@ -915,8 +957,16 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: Palette.glass,
|
||||
minHeight: 0,
|
||||
},
|
||||
commentsInner: {
|
||||
flex: 1,
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
commentsScroll: {
|
||||
flex: 1,
|
||||
},
|
||||
commentsContent: {
|
||||
paddingBottom: 24,
|
||||
flexGrow: 1,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
|
||||
Reference in New Issue
Block a user