From 08e02a752ad7b0c31bd6e7dcf5a3e8de1940a5a9 Mon Sep 17 00:00:00 2001 From: leon-morival Date: Wed, 1 Oct 2025 11:16:03 +0200 Subject: [PATCH] more menu --- src/components/MoreMenu.js | 35 ++++++++++++++---- src/screens/Home/HomeSave.js | 12 ++++-- src/screens/Library/AllMyList.js | 12 ++++-- src/screens/Library/AllMyPlaylist.web.js | 12 ++++-- src/screens/Library/PlaylistDetails.js | 12 ++++-- src/screens/Library/Research.js | 11 ++++-- src/screens/Library/components/MusicCard.js | 41 +++++++++++++++++---- src/screens/Library/components/MyMusic.js | 12 ++++-- src/screens/Profile/Profile.js | 12 ++++-- 9 files changed, 117 insertions(+), 42 deletions(-) diff --git a/src/components/MoreMenu.js b/src/components/MoreMenu.js index dd605d5..be2d4e0 100644 --- a/src/components/MoreMenu.js +++ b/src/components/MoreMenu.js @@ -12,6 +12,7 @@ import { Portal } from "@gorhom/portal"; const MoreMenu = ({ visible = false, top = 0, + position = null, onClose = () => {}, inPlaylist = false, playlistId = null, @@ -21,6 +22,17 @@ const MoreMenu = ({ const { setTooltip } = useMinuit(); if (!visible) return null; + const resolvedTop = + typeof position?.top === "number" + ? position.top + : typeof top === "number" + ? top + : 0; + const resolvedRight = + typeof position?.right === "number" ? Math.max(position.right, 0) : null; + const resolvedLeft = + typeof position?.left === "number" ? Math.max(position.left, 0) : null; + const removeFromPlaylist = async () => { try { if (inPlaylist && playlistId && projectId) { @@ -49,6 +61,21 @@ const MoreMenu = ({ onClose?.(); }; + const anchorStyle = { + position: "absolute", + top: resolvedTop, + zIndex: 2, + elevation: 2, + }; + + if (resolvedRight != null) { + anchorStyle.right = resolvedRight; + } else if (resolvedLeft != null) { + anchorStyle.left = resolvedLeft; + } else { + anchorStyle.right = 0; + } + return ( {inPlaylist && ( diff --git a/src/screens/Home/HomeSave.js b/src/screens/Home/HomeSave.js index 2fe0c31..9eb602f 100644 --- a/src/screens/Home/HomeSave.js +++ b/src/screens/Home/HomeSave.js @@ -23,7 +23,7 @@ const HomeSave = () => { [userProjects], ); const [, setTooltip] = useGlobal("_tooltip"); - const [menuTop, setMenuTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [menuProjectId, setMenuProjectId] = useState(null); @@ -83,15 +83,19 @@ const HomeSave = () => { }} onPressMore={(posTop) => { setMenuProjectId(item.id); - setMenuTop(posTop); - setShowMenu((prev) => !prev || posTop !== menuTop); + setMenuPosition(posTop); + setShowMenu( + (prev) => + !prev || posTop?.top !== (menuPosition?.top ?? null), + ); }} /> )} /> setShowMenu(false)} inPlaylist={false} projectId={menuProjectId} diff --git a/src/screens/Library/AllMyList.js b/src/screens/Library/AllMyList.js index 839a94e..8a2ac93 100644 --- a/src/screens/Library/AllMyList.js +++ b/src/screens/Library/AllMyList.js @@ -37,7 +37,7 @@ export default function AllMyList() { : []; })(); - const [top, setTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); @@ -70,14 +70,18 @@ export default function AllMyList() { onPress={() => onPressItem(item)} onPressMore={(posTop) => { setSelectedProjectId(item.id); - setTop(posTop); - setShowMenu((prev) => !prev || posTop !== top); + setMenuPosition(posTop); + setShowMenu( + (prev) => + !prev || posTop?.top !== (menuPosition?.top ?? null), + ); }} /> ))} setShowMenu(false)} inPlaylist={false} projectId={selectedProjectId} diff --git a/src/screens/Library/AllMyPlaylist.web.js b/src/screens/Library/AllMyPlaylist.web.js index f24fd8c..1895ff5 100644 --- a/src/screens/Library/AllMyPlaylist.web.js +++ b/src/screens/Library/AllMyPlaylist.web.js @@ -37,7 +37,7 @@ const AllMyPlaylist = ({ route }) => { condition: Array.isArray(playlist?.musics) && playlist.musics.length > 0, refreshArray: [selectedPlaylistId, playlist?.musics?.length || 0], }); - const [menuTop, setMenuTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); console.log("musics are : ", musics); @@ -174,15 +174,19 @@ const AllMyPlaylist = ({ route }) => { } onPressMore={(posTop) => { setSelectedProjectId(p.id); - setMenuTop(posTop); - setShowMenu((prev) => !prev || posTop !== menuTop); + setMenuPosition(posTop); + setShowMenu( + (prev) => + !prev || posTop?.top !== (menuPosition?.top ?? null), + ); }} /> ))} setShowMenu(false)} inPlaylist playlistId={selectedPlaylistId} diff --git a/src/screens/Library/PlaylistDetails.js b/src/screens/Library/PlaylistDetails.js index 9a731b5..b4a176c 100644 --- a/src/screens/Library/PlaylistDetails.js +++ b/src/screens/Library/PlaylistDetails.js @@ -36,7 +36,7 @@ const PlaylistDetails = () => { refreshArray: [playlistId, playlist?.musics?.length || 0], }); - const [menuTop, setMenuTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); @@ -90,15 +90,19 @@ const PlaylistDetails = () => { onPress={() => navigate(Routes.MusicDetails, { projectId: p.id })} onPressMore={(posTop) => { setSelectedProjectId(p.id); - setMenuTop(posTop); - setShowMenu((prev) => !prev || posTop !== menuTop); + setMenuPosition(posTop); + setShowMenu( + (prev) => + !prev || posTop?.top !== (menuPosition?.top ?? null), + ); }} /> ))} setShowMenu(false)} inPlaylist playlistId={playlistId} diff --git a/src/screens/Library/Research.js b/src/screens/Library/Research.js index 75120e3..d33a88c 100644 --- a/src/screens/Library/Research.js +++ b/src/screens/Library/Research.js @@ -194,7 +194,7 @@ const Research = () => { } } }, [filteredUsers.length, usersLoading, usersQueryRef, autoLoads]); - const [menuTop, setMenuTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); @@ -264,9 +264,11 @@ const Research = () => { } onPressMore={(posTop) => { setSelectedProjectId(p.id); - setMenuTop(posTop); + setMenuPosition(posTop); setShowMenu( - (prev) => !prev || posTop !== menuTop, + (prev) => + !prev || + posTop?.top !== (menuPosition?.top ?? null), ); }} /> @@ -391,7 +393,8 @@ const Research = () => { )} setShowMenu(false)} inPlaylist={false} projectId={selectedProjectId} diff --git a/src/screens/Library/components/MusicCard.js b/src/screens/Library/components/MusicCard.js index 6527fb4..f4fecbc 100644 --- a/src/screens/Library/components/MusicCard.js +++ b/src/screens/Library/components/MusicCard.js @@ -6,6 +6,7 @@ import { Text, View, Image as RNImage, + Dimensions, } from "react-native"; import { Image as ExpoImage } from "expo-image"; import { useGlobal } from "reactn"; @@ -28,13 +29,20 @@ const MusicCard = ({ const [currentUID] = useGlobal("currentUID"); const [selected, setSelected] = useState(false); const [layout, setLayout] = useState(null); - const [menuPos, setMenuPos] = useState(0); + const [menuPos, setMenuPos] = useState({ + top: 0, + right: 0, + left: 0, + width: 0, + height: 0, + }); const cardRef = useRef(null); + const moreButtonRef = useRef(null); useEffect(() => { if (layout) { - const top = layout?.y + 45; - setMenuPos(top); + const top = (layout?.y || 0) + 45; + setMenuPos((prev) => ({ ...prev, top })); } }, [layout]); @@ -59,13 +67,32 @@ const MusicCard = ({ }; const onPressMenu = () => { + const computeAnchor = (x = 0, y = 0, width = 0, height = 0) => { + const windowWidth = Dimensions.get("window").width || 0; + const top = (y || 0) + (height || 0) + 8; + const right = Math.max(0, windowWidth - ((x || 0) + (width || 0))); + const left = Math.max(0, x || 0); + const anchor = { top, right, left, width, height }; + setMenuPos(anchor); + onPressMore?.(anchor); + }; + + if (moreButtonRef?.current?.measureInWindow) { + try { + moreButtonRef.current.measureInWindow((x, y, width, height) => { + computeAnchor(x, y, width, height); + }); + return; + } catch (e) { + // fallback to card measurement below + } + } + // Prefer absolute screen position when available (Portal rendering) if (cardRef?.current?.measureInWindow) { try { cardRef.current.measureInWindow((x, y, width, height) => { - const top = (y || 0) + 45; - setMenuPos(top); - onPressMore?.(top); + computeAnchor(x, y, width, height); }); return; } catch (e) { @@ -132,7 +159,7 @@ const MusicCard = ({ resizeMode="contain" /> - + diff --git a/src/screens/Library/components/MyMusic.js b/src/screens/Library/components/MyMusic.js index b5f6237..91cf9b4 100644 --- a/src/screens/Library/components/MyMusic.js +++ b/src/screens/Library/components/MyMusic.js @@ -9,7 +9,7 @@ import MoreMenu from "../../../components/MoreMenu"; const MyMusic = () => { const { userProjects = [] } = useUser(); - const [menuTop, setMenuTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); const projects = Array.isArray(userProjects) ? userProjects.slice(0, 3) : []; @@ -37,14 +37,18 @@ const MyMusic = () => { onPress={() => navigate(Routes.MusicDetails, { projectId: p.id })} onPressMore={(posTop) => { setSelectedProjectId(p.id); - setMenuTop(posTop); - setShowMenu((prev) => !prev || posTop !== menuTop); + setMenuPosition(posTop); + setShowMenu( + (prev) => + !prev || posTop?.top !== (menuPosition?.top ?? null), + ); }} /> ))} setShowMenu(false)} inPlaylist={false} projectId={selectedProjectId} diff --git a/src/screens/Profile/Profile.js b/src/screens/Profile/Profile.js index 3b6454f..21611e5 100644 --- a/src/screens/Profile/Profile.js +++ b/src/screens/Profile/Profile.js @@ -49,7 +49,7 @@ const Profile = () => { const [isFollowing, setIsFollowing] = useState(false); const [followers, setFollowers] = useState(0); const [following, setFollowing] = useState(0); - const [menuTop, setMenuTop] = useState(0); + const [menuPosition, setMenuPosition] = useState(null); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); const onPressMenu = (item) => { @@ -137,8 +137,11 @@ const Profile = () => { } onPressMore={(posTop) => { setSelectedProjectId(item.id); - setMenuTop(posTop); - setShowMenu((prev) => !prev || posTop !== menuTop); + setMenuPosition(posTop); + setShowMenu( + (prev) => + !prev || posTop?.top !== (menuPosition?.top ?? null), + ); }} /> )} @@ -148,7 +151,8 @@ const Profile = () => { )} setShowMenu(false)} inPlaylist={false} projectId={selectedProjectId}