playback styles
This commit is contained in:
@@ -1,22 +1,37 @@
|
|||||||
import * as Haptics from "expo-haptics";
|
import * as Haptics from "expo-haptics";
|
||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
import { Text, TouchableOpacity, View } from "react-native";
|
import { Text, TouchableOpacity, View } from "react-native";
|
||||||
|
|
||||||
|
import { useGlobal } from "reactn";
|
||||||
import ItemContainer from "../components/ItemContainer/ItemContainer";
|
import ItemContainer from "../components/ItemContainer/ItemContainer";
|
||||||
import useLayoutType from "../hooks/useLayoutType";
|
import useLayoutType from "../hooks/useLayoutType";
|
||||||
import { gutters, Palette, Style } from "../styles";
|
import { gutters, Palette, Style } from "../styles";
|
||||||
import { FONT_FAMILY } from "../styles/Fonts";
|
import { FONT_FAMILY } from "../styles/Fonts";
|
||||||
|
|
||||||
const TabBar = ({ state = {}, descriptors = {}, navigation = {} }) => {
|
const TabBar = ({ state = {}, descriptors = {}, navigation = {} }) => {
|
||||||
const { isDesktop, windowWidth, mediumDesktopBreakpoint, isWeb } =
|
const { windowWidth, isWeb } = useLayoutType();
|
||||||
useLayoutType();
|
const [webLayoutMode] = useGlobal("webLayoutMode");
|
||||||
|
|
||||||
|
const containerWidth = useMemo(() => {
|
||||||
|
if (!isWeb) return "90%";
|
||||||
|
|
||||||
|
const containerFraction = webLayoutMode === "playbacks-wide" ? 0.9 : 0.6;
|
||||||
|
const containerMax = webLayoutMode === "playbacks-wide" ? 1440 : 800;
|
||||||
|
const computedContainerWidth = Math.min(
|
||||||
|
windowWidth * containerFraction,
|
||||||
|
containerMax
|
||||||
|
);
|
||||||
|
const barWidth = Math.max(computedContainerWidth * 0.9, 360);
|
||||||
|
return barWidth;
|
||||||
|
}, [isWeb, webLayoutMode, windowWidth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: gutters * 2,
|
bottom: gutters * 2,
|
||||||
width: "90%",
|
width: containerWidth,
|
||||||
|
maxWidth: 700,
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import {
|
|||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import Carousel from "react-native-reanimated-carousel";
|
import Carousel from "react-native-reanimated-carousel";
|
||||||
import { responsiveHeight } from "react-native-responsive-dimensions";
|
|
||||||
import { setGlobal } from "reactn";
|
import { setGlobal } from "reactn";
|
||||||
import { icons, img } from "../../assets";
|
import { icons, img } from "../../assets";
|
||||||
import KaraokeLyrics from "../../components/KaraokeLyrics";
|
import KaraokeLyrics from "../../components/KaraokeLyrics";
|
||||||
@@ -67,6 +66,7 @@ const CommentsPanel = ({
|
|||||||
commentsCount,
|
commentsCount,
|
||||||
onCommentAdded,
|
onCommentAdded,
|
||||||
inputRef,
|
inputRef,
|
||||||
|
panelHeight,
|
||||||
}) => {
|
}) => {
|
||||||
const { currentUID, currentUserData } = useUser() || {};
|
const { currentUID, currentUserData } = useUser() || {};
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
@@ -177,10 +177,15 @@ const CommentsPanel = ({
|
|||||||
<BlurView
|
<BlurView
|
||||||
intensity={Platform.OS !== "ios" ? 10 : 20}
|
intensity={Platform.OS !== "ios" ? 10 : 20}
|
||||||
tint="dark"
|
tint="dark"
|
||||||
style={styles.commentsWrapper}
|
style={[
|
||||||
|
styles.commentsWrapper,
|
||||||
|
panelHeight ? { height: panelHeight } : null,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
|
<View style={styles.commentsInner}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
|
style={styles.commentsScroll}
|
||||||
contentContainerStyle={styles.commentsContent}
|
contentContainerStyle={styles.commentsContent}
|
||||||
keyboardDismissMode="interactive"
|
keyboardDismissMode="interactive"
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
@@ -201,7 +206,10 @@ const CommentsPanel = ({
|
|||||||
comments.map((c, index) => (
|
comments.map((c, index) => (
|
||||||
<View
|
<View
|
||||||
key={c.id}
|
key={c.id}
|
||||||
style={[styles.commentRow, index > 0 && styles.commentRowSpacing]}
|
style={[
|
||||||
|
styles.commentRow,
|
||||||
|
index > 0 && styles.commentRowSpacing,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{c?.profilePicture ? (
|
{c?.profilePicture ? (
|
||||||
<ExpoImage
|
<ExpoImage
|
||||||
@@ -240,7 +248,9 @@ const CommentsPanel = ({
|
|||||||
value={text}
|
value={text}
|
||||||
onChangeText={setText}
|
onChangeText={setText}
|
||||||
placeholder={
|
placeholder={
|
||||||
canComment ? "Commente" : "Connecte-toi pour laisser un commentaire"
|
canComment
|
||||||
|
? "Commente"
|
||||||
|
: "Connecte-toi pour laisser un commentaire"
|
||||||
}
|
}
|
||||||
placeholderTextColor="#FFFFFF90"
|
placeholderTextColor="#FFFFFF90"
|
||||||
editable={canComment}
|
editable={canComment}
|
||||||
@@ -262,13 +272,18 @@ const CommentsPanel = ({
|
|||||||
/>
|
/>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
</BlurView>
|
</BlurView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||||
const { width: viewportWidth } = useWindowDimensions();
|
const { width: viewportWidth, height: viewportHeight } =
|
||||||
const [layoutWidth, setLayoutWidth] = useState(viewportWidth);
|
useWindowDimensions();
|
||||||
|
const [layoutSize, setLayoutSize] = useState({
|
||||||
|
width: viewportWidth,
|
||||||
|
height: viewportHeight,
|
||||||
|
});
|
||||||
const commentInputRef = useRef(null);
|
const commentInputRef = useRef(null);
|
||||||
const { currentUID, followUser, unfollowUser } = useUser() || {};
|
const { currentUID, followUser, unfollowUser } = useUser() || {};
|
||||||
const videoUrl = item?.playbackUrl || null;
|
const videoUrl = item?.playbackUrl || null;
|
||||||
@@ -439,12 +454,16 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
item?.description || item?.title || "Description chanson";
|
item?.description || item?.title || "Description chanson";
|
||||||
|
|
||||||
const handleLayout = useCallback((event) => {
|
const handleLayout = useCallback((event) => {
|
||||||
const nextWidth = event?.nativeEvent?.layout?.width;
|
const { width = 0, height = 0 } = event?.nativeEvent?.layout || {};
|
||||||
if (typeof nextWidth === "number" && nextWidth > 0) {
|
setLayoutSize((prev) => ({
|
||||||
setLayoutWidth(nextWidth);
|
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 widePaddingThreshold = 900;
|
||||||
const horizontalPadding = layoutWidth >= widePaddingThreshold ? 64 : 32;
|
const horizontalPadding = layoutWidth >= widePaddingThreshold ? 64 : 32;
|
||||||
const innerWidth = Math.max(layoutWidth - horizontalPadding * 2, 0);
|
const innerWidth = Math.max(layoutWidth - horizontalPadding * 2, 0);
|
||||||
@@ -454,20 +473,42 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
const minCommentsWidth = 260;
|
const minCommentsWidth = 260;
|
||||||
const maxCommentsWidth = 420;
|
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
|
let videoWidth = sideBySide
|
||||||
? Math.min(maxVideoWidth, Math.max(innerWidth * 0.56, minVideoWidth))
|
? Math.min(maxVideoWidth, Math.max(desiredWidth, minVideoWidth))
|
||||||
: Math.min(innerWidth, maxVideoWidth);
|
: 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
|
let commentsWidth = sideBySide
|
||||||
? innerWidth - videoWidth - gapBetweenColumns
|
? innerWidth - videoWidth - gapBetweenColumns
|
||||||
: innerWidth;
|
: baseInnerWidth;
|
||||||
|
|
||||||
if (sideBySide && commentsWidth > maxCommentsWidth) {
|
if (sideBySide && commentsWidth > maxCommentsWidth) {
|
||||||
videoWidth = Math.min(
|
const excess = commentsWidth - maxCommentsWidth;
|
||||||
maxVideoWidth,
|
|
||||||
videoWidth + (commentsWidth - maxCommentsWidth)
|
|
||||||
);
|
|
||||||
commentsWidth = maxCommentsWidth;
|
commentsWidth = maxCommentsWidth;
|
||||||
|
videoWidth = Math.min(maxVideoWidth, videoWidth + excess);
|
||||||
|
videoHeight = videoWidth * (16 / 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sideBySide && commentsWidth < minCommentsWidth) {
|
if (sideBySide && commentsWidth < minCommentsWidth) {
|
||||||
@@ -475,33 +516,28 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
if (videoWidth - deficit >= minVideoWidth) {
|
if (videoWidth - deficit >= minVideoWidth) {
|
||||||
videoWidth -= deficit;
|
videoWidth -= deficit;
|
||||||
commentsWidth = minCommentsWidth;
|
commentsWidth = minCommentsWidth;
|
||||||
|
videoHeight = videoWidth * (16 / 9);
|
||||||
} else {
|
} else {
|
||||||
sideBySide = false;
|
sideBySide = false;
|
||||||
videoWidth = Math.min(innerWidth, maxVideoWidth);
|
videoWidth = Math.min(baseInnerWidth, maxVideoWidth);
|
||||||
commentsWidth = innerWidth;
|
commentsWidth = baseInnerWidth;
|
||||||
|
videoHeight = videoWidth * (16 / 9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseInnerWidth = innerWidth > 0 ? innerWidth : viewportWidth;
|
if (!sideBySide) {
|
||||||
const safeVideoWidth = Math.max(
|
videoHeight = Math.min(videoHeight, desiredHeight);
|
||||||
minVideoWidth,
|
videoWidth = videoHeight * (9 / 16);
|
||||||
Math.min(
|
commentsWidth = videoWidth;
|
||||||
Number.isFinite(videoWidth) ? videoWidth : baseInnerWidth,
|
}
|
||||||
maxVideoWidth
|
|
||||||
)
|
const panelHeight = Math.min(videoHeight, desiredHeight);
|
||||||
);
|
|
||||||
const videoCardWidth = sideBySide
|
const containerHeight = viewportHeight;
|
||||||
? safeVideoWidth
|
const verticalPadding = Math.max((layoutHeight - panelHeight) / 2, 24);
|
||||||
: Math.min(Math.max(baseInnerWidth * 0.88, minVideoWidth), maxVideoWidth);
|
|
||||||
const safeCommentsWidth = sideBySide
|
const safeCommentsWidth = sideBySide
|
||||||
? Math.max(
|
? Math.max(minCommentsWidth, Math.min(commentsWidth, maxCommentsWidth))
|
||||||
minCommentsWidth,
|
: undefined;
|
||||||
Math.min(
|
|
||||||
Number.isFinite(commentsWidth) ? commentsWidth : baseInnerWidth,
|
|
||||||
maxCommentsWidth
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -510,8 +546,9 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
styles.itemContainerBase,
|
styles.itemContainerBase,
|
||||||
sideBySide ? styles.itemContainerRow : styles.itemContainerCompact,
|
sideBySide ? styles.itemContainerRow : styles.itemContainerCompact,
|
||||||
{
|
{
|
||||||
height: responsiveHeight(70),
|
height: "90%",
|
||||||
paddingHorizontal: sideBySide ? horizontalPadding : 24,
|
paddingHorizontal: sideBySide ? horizontalPadding : 24,
|
||||||
|
paddingVertical: verticalPadding,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -519,11 +556,19 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
style={[
|
style={[
|
||||||
styles.videoColumn,
|
styles.videoColumn,
|
||||||
sideBySide
|
sideBySide
|
||||||
? { width: safeVideoWidth, marginRight: gapBetweenColumns }
|
? { width: videoWidth, marginRight: gapBetweenColumns }
|
||||||
: { width: "100%" },
|
: { width: "100%" },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<View style={[styles.videoSurface, { width: videoCardWidth }]}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.videoSurface,
|
||||||
|
{
|
||||||
|
// width: videoWidth,
|
||||||
|
height: panelHeight,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
<View style={styles.videoContainer}>
|
<View style={styles.videoContainer}>
|
||||||
{!!videoUrl ? (
|
{!!videoUrl ? (
|
||||||
<VideoView
|
<VideoView
|
||||||
@@ -689,8 +734,8 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
style={[
|
style={[
|
||||||
styles.commentsColumn,
|
styles.commentsColumn,
|
||||||
sideBySide
|
sideBySide
|
||||||
? { width: safeCommentsWidth }
|
? { width: safeCommentsWidth, height: panelHeight }
|
||||||
: styles.commentsColumnCompact,
|
: [styles.commentsColumnCompact, { height: panelHeight }],
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<CommentsPanel
|
<CommentsPanel
|
||||||
@@ -701,6 +746,7 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
|||||||
setCommentsCount((c) => Math.max(0, Number(c || 0) + 1))
|
setCommentsCount((c) => Math.max(0, Number(c || 0) + 1))
|
||||||
}
|
}
|
||||||
inputRef={commentInputRef}
|
inputRef={commentInputRef}
|
||||||
|
panelHeight={panelHeight}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -726,12 +772,10 @@ const Playbacks = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.OS === "web") {
|
if (Platform.OS !== "web") return undefined;
|
||||||
setGlobal({ webLayoutMode: "playbacks-wide" });
|
setGlobal({ webLayoutMode: isFocused ? "playbacks-wide" : "default" });
|
||||||
return () => setGlobal({ webLayoutMode: "default" });
|
return () => setGlobal({ webLayoutMode: "default" });
|
||||||
}
|
}, [isFocused]);
|
||||||
return undefined;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onSnap = useCallback(
|
const onSnap = useCallback(
|
||||||
(index) => {
|
(index) => {
|
||||||
@@ -790,11 +834,9 @@ export default Playbacks;
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
screen: {
|
screen: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
// backgroundColor: Palette.darkPurple,
|
|
||||||
},
|
},
|
||||||
itemContainerBase: {
|
itemContainerBase: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
// backgroundColor: Palette.darkPurple,
|
|
||||||
paddingVertical: 36,
|
paddingVertical: 36,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
@@ -814,8 +856,8 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
videoSurface: {
|
videoSurface: {
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
width: "100%",
|
// width: 100,
|
||||||
maxWidth: 620,
|
// maxWidth: 400,
|
||||||
aspectRatio: 9 / 16,
|
aspectRatio: 9 / 16,
|
||||||
borderRadius: 32,
|
borderRadius: 32,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
@@ -915,8 +957,16 @@ const styles = StyleSheet.create({
|
|||||||
backgroundColor: Palette.glass,
|
backgroundColor: Palette.glass,
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
},
|
},
|
||||||
|
commentsInner: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "space-between",
|
||||||
|
},
|
||||||
|
commentsScroll: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
commentsContent: {
|
commentsContent: {
|
||||||
paddingBottom: 24,
|
paddingBottom: 24,
|
||||||
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
|
|||||||
Reference in New Issue
Block a user