fix player web

This commit is contained in:
2025-10-07 12:15:17 +02:00
parent 0c9e9f5f56
commit 6734dea010
5 changed files with 145 additions and 101 deletions
+14 -15
View File
@@ -5,25 +5,24 @@
"promptToConfigurePushNotifications": true
},
"build": {
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
},
"ios": {
"simulator": true
}
},
"production": {
"android": {
"buildType": "app-bundle",
"autoIncrement": "versionCode",
"resourceClass": "large"
},
"ios": {
"buildConfiguration": "Release",
"autoIncrement": "buildNumber"
},
"android": {
"buildType": "apk",
"autoIncrement": "versionCode"
}
},
"preview": {
"distribution": "internal",
"ios": {
"simulator": true
},
"android": {
"buildType": "apk",
"gradleCommand": ":app:assembleDebug",
"applicationArchivePath": "android/app/build/outputs/apk/debug/app-debug.apk"
}
}
},
@@ -22,7 +22,7 @@ const AnimatedPaginationDot = ({
}) => {
const animatedValue = useMemo(
() => scrollValue || new Animated.Value(0),
[scrollValue],
[scrollValue]
);
const { width, height } = useWindowDimensions();
@@ -36,7 +36,10 @@ const AnimatedPaginationDot = ({
return Math.max(width, 1);
}, [height, itemDimension, orientation, width]);
const resolvedDotStyle = useMemo(() => StyleSheet.flatten(dotStyle) || {}, [dotStyle]);
const resolvedDotStyle = useMemo(
() => StyleSheet.flatten(dotStyle) || {},
[dotStyle]
);
const defaultSize = Math.max(baseDotSize, 1);
const baseWidth =
typeof resolvedDotStyle?.width === "number"
+119 -8
View File
@@ -1,11 +1,14 @@
import { BlurView } from "expo-blur";
import React, { useMemo } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Image, Pressable, StyleSheet, Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { icons, img } from "../../assets";
import { arrayRemove, arrayUnion, projectsRef } from "../../config/firebase";
import { isWeb } from "../../hooks/useLayoutType";
import useDataFromRef from "../../hooks/useDataFromRef";
import usePlayer from "../../hooks/usePlayer";
import { useUser } from "../../providers/UserDataProvider";
import { gutters, Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
@@ -21,16 +24,90 @@ const formatDuration = (ms) => {
const GlobalAudioPlayer = () => {
const { currentTrack, isPlaying, positionMs, durationMs, pause, resume } =
usePlayer();
const { currentUID } = useUser() || {};
const [pendingLike, setPendingLike] = useState(null);
const projectId = useMemo(() => {
const metadataProjectId =
typeof currentTrack?.metadata?.projectId === "string"
? currentTrack.metadata.projectId
: null;
const metadataPlaybackId =
typeof currentTrack?.metadata?.playbackId === "string"
? currentTrack.metadata.playbackId
: null;
const contextProjectId =
typeof currentTrack?.context?.projectId === "string"
? currentTrack.context.projectId
: null;
return metadataProjectId || metadataPlaybackId || contextProjectId || null;
}, [
currentTrack?.context?.projectId,
currentTrack?.metadata?.playbackId,
currentTrack?.metadata?.projectId,
]);
const { data: projectData } = useDataFromRef({
ref: projectId ? projectsRef.doc(projectId) : null,
simpleRef: true,
listener: true,
condition: !!projectId,
refreshArray: [projectId],
});
const likedByList = Array.isArray(projectData?.likedBy)
? projectData.likedBy
: [];
const likedFromDoc = useMemo(() => {
if (!projectId || !currentUID) return false;
return likedByList.includes(currentUID);
}, [likedByList, projectId, currentUID]);
const effectiveIsLiked =
pendingLike !== null ? pendingLike : likedFromDoc;
const isLikeProcessing =
pendingLike !== null && pendingLike !== likedFromDoc;
useEffect(() => {
setPendingLike(null);
}, [projectId, currentUID]);
useEffect(() => {
if (pendingLike === null) return;
if (pendingLike === likedFromDoc) {
setPendingLike(null);
}
}, [pendingLike, likedFromDoc]);
const handleToggleLike = useCallback(async () => {
if (!projectId || !currentUID || isLikeProcessing) return;
const next = !effectiveIsLiked;
setPendingLike(next);
try {
await projectsRef.doc(projectId).set(
{
likedBy: next ? arrayUnion(currentUID) : arrayRemove(currentUID),
},
{ merge: true }
);
} catch (_error) {
setPendingLike(null);
}
}, [
currentUID,
effectiveIsLiked,
isLikeProcessing,
projectId,
]);
const insets = useSafeAreaInsets();
const hasTrack = !!currentTrack?.source;
if (!hasTrack) return null;
const title = currentTrack?.title || "Lecture en cours";
const artist = currentTrack?.artist || "";
const progress =
durationMs > 0 ? Math.min(1, Math.max(0, positionMs / durationMs)) : 0;
const artworkSource = useMemo(() => {
if (typeof currentTrack?.artwork === "number") return currentTrack.artwork;
@@ -55,6 +132,8 @@ const GlobalAudioPlayer = () => {
const GAP_WITH_TAB = 8;
const bottomOffset = (isWeb ? gutters : 3) + TAB_BAR_HEIGHT + GAP_WITH_TAB;
if (!hasTrack) return null;
return (
<View
pointerEvents="box-none"
@@ -83,10 +162,26 @@ const GlobalAudioPlayer = () => {
)}
</View>
<View style={styles.actions}>
<Pressable
onPress={handleToggleLike}
hitSlop={12}
disabled={!projectId || !currentUID || isLikeProcessing}
style={[
styles.actionButton,
styles.likeButton,
(!projectId || !currentUID || isLikeProcessing) &&
styles.disabledAction,
]}
>
<Image
source={effectiveIsLiked ? icons.heart : icons.heartOutline}
style={styles.likeIcon}
/>
</Pressable>
<Pressable
onPress={handleToggle}
hitSlop={12}
style={styles.toggleButton}
style={[styles.actionButton, styles.toggleButton]}
>
<Image
source={isPlaying ? icons.pause : icons.play}
@@ -164,13 +259,29 @@ const styles = StyleSheet.create({
actions: {
flexDirection: "row",
alignItems: "center",
gap: 8,
},
actionButton: {
alignItems: "center",
justifyContent: "center",
},
disabledAction: {
opacity: 0.6,
},
likeButton: {
width: 40,
height: 40,
borderRadius: 20,
},
likeIcon: {
width: 22,
height: 22,
resizeMode: "contain",
},
toggleButton: {
width: 44,
height: 44,
borderRadius: 22,
alignItems: "center",
justifyContent: "center",
// backgroundColor: "rgba(255, 255, 255, 0.1)",
},
toggleIcon: {
+6 -53
View File
@@ -1,5 +1,4 @@
import { useIsFocused, useRoute } from "@react-navigation/native";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import { BlurView } from "expo-blur";
import { VideoView, useVideoPlayer } from "expo-video";
import React, {
@@ -22,6 +21,7 @@ import {
usersRef,
} from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider";
@@ -116,7 +116,11 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
const creatorName = item?.userName;
const audioPlayer = useSharedAudioPlayer(audioSource || undefined, {
id: item?.id ? `playback-${item.id}` : audioSource?.uri ? `playback-${audioSource.uri}` : undefined,
id: item?.id
? `playback-${item.id}`
: audioSource?.uri
? `playback-${audioSource.uri}`
: undefined,
title: typeof item?.title === "string" ? item.title : "",
artist: typeof item?.userName === "string" ? item.userName : "",
artwork: item?.coverUrl || null,
@@ -217,57 +221,6 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
backgroundColor: "black",
}}
>
{(projectTitle || creatorName) && (
<View
pointerEvents="none"
style={{
position: "absolute",
top: Platform.select({ ios: 64, default: 48 }),
left: 24,
right: 24,
zIndex: 12,
}}
>
<BlurView
tint="dark"
intensity={Platform.OS !== "ios" ? 20 : 30}
style={{
paddingHorizontal: 16,
paddingVertical: 12,
borderRadius: 18,
backgroundColor: "rgba(0,0,0,0.45)",
gap: 4,
overflow: "hidden",
}}
>
{projectTitle ? (
<Text
numberOfLines={1}
style={{
color: Palette.white,
fontSize: 18,
fontFamily: FONT_FAMILY.InterSemiBold,
}}
>
{projectTitle}
</Text>
) : null}
{creatorName ? (
<Text
numberOfLines={1}
style={{
color: Palette.white,
fontSize: 14,
fontFamily: FONT_FAMILY.InterMedium,
opacity: 0.9,
}}
>
{creatorName}
</Text>
) : null}
</BlurView>
</View>
)}
{!!videoUrl ? (
<VideoView
player={videoPlayer}
+1 -23
View File
@@ -1,12 +1,6 @@
import { useIsFocused, useRoute } from "@react-navigation/native";
import React, { useCallback, useEffect, useRef, useState } from "react";
import {
FlatList,
StyleSheet,
Text,
View,
useWindowDimensions,
} from "react-native";
import { FlatList, StyleSheet, View, useWindowDimensions } from "react-native";
import { projectsRef } from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import { useUser } from "../../providers/UserDataProvider";
@@ -221,22 +215,6 @@ const Playbacks = () => {
return (
<View style={styles.screen}>
{(activeTitle || activeCreatorName) && (
<View pointerEvents="none" style={styles.topOverlayContainer}>
<View style={styles.topOverlayContent}>
{activeTitle ? (
<Text numberOfLines={1} style={styles.topOverlayTitle}>
{activeTitle}
</Text>
) : null}
{activeCreatorName ? (
<Text numberOfLines={1} style={styles.topOverlaySubtitle}>
{activeCreatorName}
</Text>
) : null}
</View>
</View>
)}
{activeVideoUrl ? (
<View pointerEvents="none" style={styles.backgroundVideoWrapper}>
<video