From 3dcf5453cae0ef906830602ee09b00060fc85ff7 Mon Sep 17 00:00:00 2001 From: leon-morival Date: Tue, 30 Sep 2025 09:58:20 +0200 Subject: [PATCH] app layout --- .eslintrc.js | 10 +- src/layouts/AppLayout.js | 55 ++++----- src/layouts/Page.js | 54 ++++----- src/screens/HitParade/HitParade.js | 7 +- src/screens/Profile/Profile.js | 173 +++++++++++++++-------------- 5 files changed, 153 insertions(+), 146 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b8fbdb2..a11ee9e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,11 +1,11 @@ module.exports = { - extends: ['expo', 'prettier'], - plugins: ['prettier'], + extends: ["expo", "prettier"], + plugins: ["prettier"], rules: { - 'prettier/prettier': [ - 'error', + "prettier/prettier": [ + "error", { - singleQuote: true, + singleQuote: false, }, ], }, diff --git a/src/layouts/AppLayout.js b/src/layouts/AppLayout.js index 0ba57c1..99cea82 100644 --- a/src/layouts/AppLayout.js +++ b/src/layouts/AppLayout.js @@ -1,52 +1,51 @@ -import { View, Image } from 'react-native'; -import { useGlobal } from 'reactn'; -import { Motion } from '@legendapp/motion'; +import { Image, View } from "react-native"; +import { useGlobal } from "reactn"; -import useLayoutType from '../hooks/useLayoutType'; +import useLayoutType from "../hooks/useLayoutType"; -import Home from '../screens/Home'; - -import { Palette, Style } from '../styles'; -import { background as backgrounds } from '../assets'; +import { background as backgrounds } from "../assets"; +import { Style } from "../styles"; export default ({ currentUID = null, children }) => { const { isWeb = false } = useLayoutType(); // Web: full-screen background with a centered content area (30% width) - const [webBackgroundImg] = useGlobal('webBackgroundImg'); + const [webBackgroundImg] = useGlobal("webBackgroundImg"); if (isWeb) { return ( + alignItems: "center", + justifyContent: "center", + minHeight: "100svh", + position: "relative", + }} + > {/* Full-viewport background for web */} {/* Centered content column (phone-like width) */} + maxWidth: isWeb ? 800 : 520, + height: "100svh", + backgroundColor: "transparent", + }} + > {children} @@ -59,17 +58,19 @@ export default ({ currentUID = null, children }) => { + flexDirection: "row-reverse", + }} + > + }} + > {children} diff --git a/src/layouts/Page.js b/src/layouts/Page.js index 684a3a9..00d29e6 100644 --- a/src/layouts/Page.js +++ b/src/layouts/Page.js @@ -1,21 +1,19 @@ /* eslint-disable react/display-name */ -import React, { useState, useEffect } from 'reactn'; -import { setGlobal } from 'reactn'; -import { Image, View } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import { Motion } from '@legendapp/motion'; -import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import { Image, View } from "react-native"; +import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; +import { SafeAreaView } from "react-native-safe-area-context"; +import React, { setGlobal, useEffect, useState } from "reactn"; -import { responsiveHeight } from '../actions/responsiveSizes.js'; +import { responsiveHeight } from "../actions/responsiveSizes.js"; -import { Fonts, gutters } from '../styles'; +import { gutters } from "../styles"; -import BaseHeader from '../components/BaseHeader'; -import NavigateHeader from '../components/NavigateHeader'; +import BaseHeader from "../components/BaseHeader"; +import NavigateHeader from "../components/NavigateHeader"; -import { isDesktop, isWeb } from '../hooks/useLayoutType.js'; -import { useUserData } from '../providers/UserDataProvider.js'; -import { background } from '../assets/index.js'; +import { background } from "../assets/index.js"; +import { isDesktop, isWeb } from "../hooks/useLayoutType.js"; +import { useUserData } from "../providers/UserDataProvider.js"; export default ({ children, @@ -23,11 +21,11 @@ export default ({ topStickyContent = null, bottomStickyContent = null, - title = '', + title = "", rightComponent = null, - containerType = 'SAFE_AREA_VIEW', - headerType = 'BASE', + containerType = "SAFE_AREA_VIEW", + headerType = "BASE", containerStyle = {}, headerStyle = {}, @@ -45,7 +43,7 @@ export default ({ const [scrollPosition, setScrollPosition] = useState(0); const PageContainer = - containerType === 'SAFE_AREA_VIEW' ? SafeAreaView : View; + containerType === "SAFE_AREA_VIEW" ? SafeAreaView : View; const ContentContainer = scrollEnabled ? KeyboardAwareScrollView : View; @@ -68,24 +66,25 @@ export default ({ {!isWeb && ( )} - {headerType !== 'NONE' ? ( - headerType === 'BASE' ? ( + }} + > + {headerType !== "NONE" ? ( + headerType === "BASE" ? ( ) : ( + : {})} + > {children} diff --git a/src/screens/HitParade/HitParade.js b/src/screens/HitParade/HitParade.js index 11a46c2..ab9bd3c 100644 --- a/src/screens/HitParade/HitParade.js +++ b/src/screens/HitParade/HitParade.js @@ -23,9 +23,6 @@ const HitParade = () => { condition: true, }); const { data: topPlaybacks } = useDataFromRef({ - // Firestore n'autorise pas 2 inégalités (>, <, >=, <=, !=) sur des champs différents. - // On ne garde qu'une inégalité côté requête (views > 0) et on filtre ensuite côté client. - // On surdimensionne le limit pour s'assurer d'obtenir 10 playbacks après filtrage. ref: projectsRef.where("views", ">", 0).orderBy("views", "desc").limit(50), format: (docs) => docs.filter((d) => d?.playbackUrl != null).slice(0, 10), simpleRef: false, @@ -76,7 +73,7 @@ const HitParade = () => { style={{ ...Style.containerRow, justifyContent: "space-between", - gap: "10%", + // gap: "5%", width: "auto", }} > @@ -88,7 +85,7 @@ const HitParade = () => { onPress={() => setSelected(item)} tint={selected === item ? "light" : "dark"} containerStyle={{ - width: "50%", + width: "47%", }} titleStyle={{ fontSize: 16, diff --git a/src/screens/Profile/Profile.js b/src/screens/Profile/Profile.js index 34e52c6..14c2494 100644 --- a/src/screens/Profile/Profile.js +++ b/src/screens/Profile/Profile.js @@ -1,37 +1,37 @@ -import { useRoute } from '@react-navigation/native'; -import { BlurView } from 'expo-blur'; -import { Image as ExpoImage } from 'expo-image'; -import React, { useEffect, useMemo, useState } from 'react'; +import { useRoute } from "@react-navigation/native"; +import { BlurView } from "expo-blur"; +import { Image as ExpoImage } from "expo-image"; +import React, { useEffect, useMemo, useState } from "react"; import { FlatList, Image, - Platform, Pressable, ScrollView, StyleSheet, Text, View, -} from 'react-native'; -import { SheetManager } from 'react-native-actions-sheet'; -import { responsiveHeight } from 'react-native-responsive-dimensions'; -import { useGlobal } from 'reactn'; -import { background, icons, img } from '../../assets'; -import BorderGradient from '../../components/BorderGradient/BorderGradient'; -import MoreMenu from '../../components/MoreMenu'; -import { projectsRef, usersRef } from '../../config/firebase'; -import useDataFromRef from '../../hooks/useDataFromRef'; -import Page from '../../layouts/Page'; -import { Routes } from '../../navigation'; -import { navigate, push } from '../../navigation/NavigationService'; -import { useUser } from '../../providers/UserDataProvider'; -import { Palette } from '../../styles'; -import { FONT_FAMILY } from '../../styles/Fonts'; -import Style, { gutters, size } from '../../styles/Style'; -import MusicCard from '../Library/components/MusicCard'; - +} from "react-native"; +import { SheetManager } from "react-native-actions-sheet"; +import { responsiveHeight } from "react-native-responsive-dimensions"; +import { useGlobal } from "reactn"; +import { background, icons, img } from "../../assets"; +import BorderGradient from "../../components/BorderGradient/BorderGradient"; +import MoreMenu from "../../components/MoreMenu"; +import { projectsRef, usersRef } from "../../config/firebase"; +import useDataFromRef from "../../hooks/useDataFromRef"; +import useLayoutType from "../../hooks/useLayoutType"; +import Page from "../../layouts/Page"; +import { Routes } from "../../navigation"; +import { navigate, push } from "../../navigation/NavigationService"; +import { useUser } from "../../providers/UserDataProvider"; +import { Palette } from "../../styles"; +import { FONT_FAMILY } from "../../styles/Fonts"; +import Style, { gutters, size } from "../../styles/Style"; +import MusicCard from "../Library/components/MusicCard"; const Profile = () => { const { params } = useRoute(); - const [selected, setSelected] = useState('Chansons'); + const [selected, setSelected] = useState("Chansons"); + const { isWeb } = useLayoutType(); const { userProjects: selfProjects, userPlaybacks: selfPlaybacks, @@ -41,10 +41,10 @@ const Profile = () => { followUser, unfollowUser, } = useUser(); - const [currentUID] = useGlobal('currentUID'); + const [currentUID] = useGlobal("currentUID"); const targetUserId = params?.userId || currentUID || null; const isSelf = !!currentUID && targetUserId === currentUID; - const [, setTooltip] = useGlobal('_tooltip'); + const [, setTooltip] = useGlobal("_tooltip"); const [userData, setUserData] = useState(null); const [isFollowing, setIsFollowing] = useState(false); const [followers, setFollowers] = useState(0); @@ -82,7 +82,7 @@ const Profile = () => { const followingQueryRef = useMemo(() => { try { return targetUserId - ? usersRef.where('followedBy', 'array-contains', targetUserId) + ? usersRef.where("followedBy", "array-contains", targetUserId) : null; } catch (e) { return null; @@ -124,10 +124,10 @@ const Profile = () => { keyExtractor={(item) => item.id} renderItem={({ item }) => ( { isSelf ? [ { - label: 'Supprimer', + label: "Supprimer", onPress: () => - SheetManager.show('Delete', { + SheetManager.show("Delete", { payload: { - title: 'Supprimer le projet', + title: "Supprimer le projet", message: - 'Cette action supprimera définitivement ce projet.', + "Cette action supprimera définitivement ce projet.", onConfirm: async () => { try { if (!selectedProjectId) return; await projectsRef.doc(selectedProjectId).delete(); setTooltip({ - type: 'success', - text: 'Projet supprimé', + type: "success", + text: "Projet supprimé", }); } catch (e) { setTooltip({ - type: 'error', - text: e?.message || 'Suppression impossible', + type: "error", + text: e?.message || "Suppression impossible", }); } }, @@ -196,7 +196,8 @@ const Profile = () => { color: Palette.white, fontFamily: FONT_FAMILY.InterRegular, opacity: 0.8, - }}> + }} + > {text} @@ -207,8 +208,8 @@ const Profile = () => { try { if (!targetUserId || isSelf) return null; return projectsRef - .where('userId', '==', targetUserId) - .orderBy('updatedAt', 'desc'); + .where("userId", "==", targetUserId) + .orderBy("updatedAt", "desc"); } catch (e) { return null; } @@ -228,23 +229,24 @@ const Profile = () => { return ( isSelf ? ( - SheetManager.show('ProfileSettings')}> + SheetManager.show("ProfileSettings")}> ) : null - }> + } + > - + { // Platform.OS !== "ios" ? "dimezisBlurView" : "none" // } > - + { } : img.profile } - cachePolicy='memory-disk' - priority='high' - contentFit='cover' + cachePolicy="memory-disk" + priority="high" + contentFit="cover" transition={100} style={{ ...size({ size: 108 }), @@ -285,13 +287,14 @@ const Profile = () => { fontSize: 22, color: Palette.white, fontFamily: FONT_FAMILY.InterSemiBold, - }}> + }} + > {(isSelf ? currentUserData?.userName : userData?.userName) || - 'Profil'} + "Profil"} - + {Array.isArray(displayedProjects) ? displayedProjects.length @@ -300,13 +303,14 @@ const Profile = () => { projets push(Routes.Follows, { - selected: 'Abonnés', + selected: "Abonnés", ...(isSelf ? {} : { userId: targetUserId }), }) - }> + } + > {isSelf ? currentUserData?.followedBy?.length || 0 @@ -315,13 +319,14 @@ const Profile = () => { abonnés push(Routes.Follows, { - selected: 'Abonnements', + selected: "Abonnements", ...(isSelf ? {} : { userId: targetUserId }), }) - }> + } + > {isSelf ? followingCount : following} @@ -334,21 +339,23 @@ const Profile = () => { style={{ height: 36, borderRadius: 100, - overflow: 'hidden', - backgroundColor: '#FFFFFF22', - alignItems: 'center', - justifyContent: 'center', - width: '80%', - alignSelf: 'center', + overflow: "hidden", + backgroundColor: "#FFFFFF22", + alignItems: "center", + justifyContent: "center", + width: "80%", + alignSelf: "center", marginTop: 18, - }}> + }} + > - {isFollowing ? 'Ne plus suivre' : 'Suivre'} + }} + > + {isFollowing ? "Ne plus suivre" : "Suivre"} @@ -359,18 +366,20 @@ const Profile = () => { style={{ ...Style.containerRow, gap: 6, - }}> + }} + > {/*{["Chansons", "Playbacks", "Clips"].map((item, index) => (*/} - {['Chansons', 'Playbacks'].map((item, index) => ( + {["Chansons", "Playbacks"].map((item, index) => ( onPressMenu(item)} - style={{ flex: 1 }}> + style={{ flex: 1 }} + > { ))} - {selected === 'Playbacks' && ( + {selected === "Playbacks" && ( )} - {selected === 'Chansons' && ( + {selected === "Chansons" && ( )} - {selected === 'Clips' && ( + {selected === "Clips" && ( - + )} @@ -437,20 +446,20 @@ const styles = StyleSheet.create({ borderWidth: 1, borderRadius: 10, height: 33, - position: 'absolute', + position: "absolute", zIndex: 1, - width: '100%', + width: "100%", }, blurContainer: { height: 33, zIndex: -1, borderRadius: 10, - overflow: 'hidden', + overflow: "hidden", backgroundColor: Palette.glass, }, blurView: { - width: '100%', - height: '100%', + width: "100%", + height: "100%", paddingHorizontal: 10, ...Style.containerCenter, },