Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .github/assets/Promo - UI.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import { View } from "react-native";
import { GestureDetector, usePanGesture } from "react-native-gesture-handler";
import Animated, {
LinearTransition,
SlideInLeft,
SlideInRight,
SlideOutLeft,
SlideOutRight,
SlideInDown,
SlideOutDown,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
Expand All @@ -24,8 +22,7 @@ import { getArtistsString } from "~/data/artist/utils";
import { usePlaybackStore } from "~/stores/Playback/store";
import { PlaybackControls } from "~/stores/Playback/actions";
import { usePreferenceStore } from "~/stores/Preference/store";

import { BottomActionsOffset } from "../hooks/useBottomActions";
import { BottomActionsOffset } from "./useBottomActions";

import { OnRTL } from "~/lib/react";
import { cn } from "~/lib/style";
Expand Down Expand Up @@ -103,11 +100,11 @@ export function MiniPlayer() {
<GestureDetector gesture={panGesture}>
<Animated.View
layout={LinearTransition}
entering={OnRTL.decide(SlideInRight, SlideInLeft)}
exiting={OnRTL.decide(SlideOutRight, SlideOutLeft)}
entering={SlideInDown}
exiting={SlideOutDown}
style={animatedStyles}
className={cn(
"relative z-10 shrink grow overflow-hidden rounded-full bg-surfaceContainerLowest",
"relative z-10 h-14 w-full shrink grow overflow-hidden rounded-full bg-surfaceContainerLowest",
{ "bg-surfaceContainerLow": isPressed },
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { View } from "react-native";
import Animated, {
FadeIn,
SlideInDown,
SlideInLeft,
SlideInRight,
SlideOutDown,
SlideOutLeft,
SlideOutRight,
} from "react-native-reanimated";
Expand All @@ -15,109 +16,64 @@ import { Search } from "~/resources/icons/Search";
import { Settings } from "~/resources/icons/Settings";
import { usePreferenceStore } from "~/stores/Preference/store";
import { useTabsByVisibility } from "~/stores/Preference/hooks";
import { useRenderBottomActions } from "../hooks/useBottomActions";
import { useHasNewUpdate } from "../hooks/useHasNewUpdate";

import { useHasNewUpdate } from "~/navigation/hooks/useHasNewUpdate";

import { OnRTL } from "~/lib/react";
import { cn } from "~/lib/style";
import { capitalize } from "~/utils/string";
import { createAnimatedMaterialSymbol } from "~/components/Base/AnimatedMaterialSymbol";
import { FlatList, useFlatListRef } from "~/components/Base/List";
import { Pressable } from "~/components/Base/Pressable";
import { FilledIconButton } from "~/components/Form/Button/Icon";
import { Menu } from "~/components/Menu";
import { TStyledText } from "~/components/Typography/StyledText";
import { useTheme } from "~/modules/customization/theme/hooks";
import type { Tab } from "~/stores/Preference/types";
import { MiniPlayer } from "./MiniPlayer";

//#region Bottom Actions
/** Actions stickied to the bottom of the screens. */
export function BottomActions() {
const rendered = useRenderBottomActions();
// Extra `View` is to fix positioning when button navigation is selected.
export function SearchButton() {
const { t } = useTranslation();
const navigation = useNavigation();
return (
<View>
<Animated.View
pointerEvents="box-none"
className="absolute bottom-0 left-0 h-18 w-full flex-row items-center justify-end gap-2 p-4 pt-0"
>
{rendered.miniPlayer ? <MiniPlayer /> : null}
{rendered.navBar ? <HomeActions /> : null}
</Animated.View>
</View>
<Animated.View
entering={OnRTL.decide(SlideInRight, SlideInLeft)}
exiting={OnRTL.decide(SlideOutRight, SlideOutLeft)}
>
<FilledIconButton
Icon={Search}
accessibilityLabel={t("feat.search.title")}
onPress={() => navigation.navigate("Search")}
size="lg"
className="size-14"
/>
</Animated.View>
);
}
//#endregion

//#region Home Actions
const AnimatedMenuIcon = createAnimatedMaterialSymbol(
// "Menu" SVG
"M176.15-261.08q-11.63 0-19.85-8.22-8.22-8.23-8.22-19.77 0-11.55 8.22-19.76t19.85-8.21h607.89q11.44 0 19.66 8.23 8.22 8.22 8.22 19.77 0 11.54-8.22 19.75t-19.66 8.21H176.15Zm0-191.34q-11.63 0-19.85-8.23-8.22-8.22-8.22-19.77 0-11.54 8.22-19.75t19.85-8.21h607.89q11.44 0 19.66 8.22 8.22 8.23 8.22 19.77t-8.22 19.75q-8.22 8.22-19.66 8.22H176.15Zm0-191.35q-11.63 0-19.85-8.22-8.22-8.23-8.22-19.77 0-11.55 8.22-19.76t19.85-8.21h607.89q11.44 0 19.66 8.22 8.22 8.23 8.22 19.77 0 11.55-8.22 19.76t-19.66 8.21H176.15Z",
// "Close" SVG
"M480-440.27 278.38-238.65q-8.3 8.3-19.76 8.25-11.47-.06-19.97-8.56-8.19-8.5-8.03-19.62.15-11.11 8.34-19.3L440.27-480 238.96-682.12q-7.81-7.8-8-19.11-.19-11.31 8-19.81 8.19-8.5 19.46-8.75 11.27-.25 19.96 8.25L480-519.73l201.81-201.81q8.11-8.11 19.57-8.06 11.47.06 20.16 8.56 8 8.5 7.84 19.62-.15 11.11-8.34 19.3L519.73-480l201.31 202.12q7.81 7.8 8 19.11.19 11.31-8 19.81-8.19 8.5-19.46 8.75-11.27.25-19.77-8.44L480-440.27Z",
);

function HomeActions() {
export function SettingsButton() {
const { t } = useTranslation();
const navigation = useNavigation();
const [visible, setVisible] = useState(false);
const { hasNewUpdate } = useHasNewUpdate();
const showNavbar = usePreferenceStore((s) => s.showNavbar);

return (
<Menu
<Animated.View
entering={OnRTL.decide(SlideInLeft, SlideInRight)}
exiting={OnRTL.decide(SlideOutLeft, SlideOutRight)}
visible={visible}
anchor={
<View className="relative">
<FilledIconButton
Icon={AnimatedMenuIcon}
accessibilityLabel={t("term.more")}
onPress={() => setVisible((prev) => !prev)}
alternative={visible}
size="lg"
className="size-14"
/>
{!visible && hasNewUpdate && (
<Animated.View
entering={FadeIn}
className="absolute top-3 right-3 size-2 rounded-full bg-primary"
/>
)}
</View>
}
anchorPosition="top"
menuClassName="flex-row items-center gap-2"
className="relative"
>
{showNavbar ? <Navbar /> : null}
<FilledIconButton
Icon={Search}
accessibilityLabel={t("feat.search.title")}
onPress={() => navigation.navigate("Search")}
Icon={Settings}
accessibilityLabel={t("term.settings")}
onPress={() => navigation.navigate("Settings")}
size="lg"
className="size-14"
/>
<View className="relative">
<FilledIconButton
Icon={Settings}
accessibilityLabel={t("term.settings")}
onPress={() => navigation.navigate("Settings")}
size="lg"
className="size-14"
/>
{hasNewUpdate && (
<View className="absolute top-3 right-3 size-2 rounded-full bg-primary" />
)}
</View>
</Menu>
{hasNewUpdate && (
<View className="absolute top-3 right-3 size-2 rounded-full bg-primary" />
Comment thread
cyanChill marked this conversation as resolved.
)}
</Animated.View>
);
}
//#endregion

//#region Navbar
function Navbar() {
export function Navbar() {
const navigation = useNavigation();
const { surfaceContainerLowest } = useTheme();
const homeTab = usePreferenceStore((s) => s.homeTab);
Expand Down Expand Up @@ -148,7 +104,11 @@ function Navbar() {
}, [listRef, activeIndex, mounted]);

return (
<View className="relative ml-8 shrink grow overflow-hidden rounded-full bg-surfaceContainerLowest">
<Animated.View
entering={SlideInDown}
exiting={SlideOutDown}
className="relative h-14 w-full shrink grow overflow-hidden rounded-full bg-surfaceContainerLowest"
>
<FlatList
ref={listRef}
onLayout={() => setMounted(true)}
Expand Down Expand Up @@ -190,14 +150,12 @@ function Navbar() {
style={{ [OnRTL.decide("left", "right")]: 0 }}
className="absolute h-full w-4"
/>
</View>
</Animated.View>
);
}

const ShadowProps = { start: { x: 0.0, y: 1.0 }, end: { x: 1.0, y: 1.0 } };
//#endregion

//#region Utils
function getHomeScreenRoute(tabKey: Tab) {
if (tabKey === "home") return { term: "term.home", screen: "Home" } as const;
return { term: `term.${tabKey}s`, screen: `${capitalize(tabKey)}s` } as const;
Expand Down
73 changes: 73 additions & 0 deletions mobile/src/navigation/components/BottomActions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useMemo } from "react";
import { View } from "react-native";
import Animated, { LinearTransition } from "react-native-reanimated";

import { usePreferenceStore } from "~/stores/Preference/store";
import { useRenderBottomActions } from "./useBottomActions";

import { MiniPlayer } from "./MiniPlayer";
import { Navbar, SearchButton, SettingsButton } from "./NavActions";

//#region Bottom Actions
/** Actions stickied to the bottom of the screens. */
export function BottomActions() {
const showNavbar = usePreferenceStore((s) => s.showNavbar);

const BottomActionsLayout = useMemo(
() => (showNavbar ? WithNavBar : WithOutNavBar),
[showNavbar],
);

// Extra `View` is to fix positioning when button navigation is selected.
return (
<View>
<Animated.View
layout={LinearTransition}
pointerEvents="box-none"
className="absolute bottom-0 left-0 w-full items-end gap-2 p-4 pt-0"
>
<BottomActionsLayout />
</Animated.View>
</View>
);
}
//#endregion

/** Bottom action layout when the user wants to show the navbar. */
function WithNavBar() {
const rendered = useRenderBottomActions();
return (
<>
{rendered.miniPlayer ? <MiniPlayer /> : null}
{rendered.navBar ? (
<Animated.View
layout={LinearTransition}
className="-z-1 w-full flex-row gap-2"
>
<SearchButton />
<Navbar />
<SettingsButton />
</Animated.View>
) : null}
</>
);
}

/** Bottom action layout when the user doesn't want to show the navbar. */
function WithOutNavBar() {
const rendered = useRenderBottomActions();
return (
<Animated.View
layout={LinearTransition}
className="w-full flex-row justify-between gap-2"
>
{rendered.navBar ? <SearchButton /> : null}
{rendered.miniPlayer ? (
<Animated.View layout={LinearTransition} className="w-full shrink grow">
<MiniPlayer />
</Animated.View>
) : null}
{rendered.navBar ? <SettingsButton /> : null}
</Animated.View>
Comment thread
cyanChill marked this conversation as resolved.
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function useRenderBottomActions() {
return useMemo(
() => ({
miniPlayer: canRenderMiniPlayer && (onHomeScreen || isMiniPlayerShown),
navBar: onHomeScreen,
navBar: Boolean(onHomeScreen),
}),
[canRenderMiniPlayer, isMiniPlayerShown, onHomeScreen],
);
Expand Down
15 changes: 12 additions & 3 deletions mobile/src/navigation/layouts/NScrollLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import { scheduleOnUI } from "react-native-worklets";
import { MoreHoriz } from "~/resources/icons/MoreHoriz";
import { usePreferenceStore } from "~/stores/Preference/store";

import { BottomActionsOffset } from "~/navigation/hooks/useBottomActions";
import {
BottomActionsOffset,
useBottomActionsOffset,
} from "~/navigation/components/BottomActions/useBottomActions";

import type {
AnimatedLegendListRef,
Expand Down Expand Up @@ -68,7 +71,10 @@ export function NScrollLayout(props: {
const quickScroll = usePreferenceStore((s) => s.quickScroll);
const scrollBarContext = useScrollbarContext();

const bottomOffset = BottomActionsOffset + 16;
let bottomOffset = BottomActionsOffset + 16;
const showNavbar = usePreferenceStore((s) => s.showNavbar);
const miniplayVisible = useBottomActionsOffset() !== 0;
bottomOffset += miniplayVisible && showNavbar ? BottomActionsOffset - 8 : 0;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Shy Header
const [topBarHeight, setTopBarHeight] = useState(
Expand Down Expand Up @@ -153,7 +159,10 @@ export function NScrollListLayout<TData>({
const quickScroll = usePreferenceStore((s) => s.quickScroll);
const scrollBarContext = useScrollbarContext();

const bottomOffset = BottomActionsOffset + 16;
let bottomOffset = BottomActionsOffset + 16;
const showNavbar = usePreferenceStore((s) => s.showNavbar);
const miniplayVisible = useBottomActionsOffset() !== 0;
bottomOffset += miniplayVisible && showNavbar ? BottomActionsOffset - 8 : 0;

// Shy Header
const [topBarHeight, setTopBarHeight] = useState(
Expand Down
2 changes: 1 addition & 1 deletion mobile/src/navigation/screens/RecentlyPlayedView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { RECENT_DAY_RANGE } from "~/data/recent/api";
import { useRecentlyPlayedMedia } from "~/data/recent/queries";
import { useGetColumn } from "~/hooks/useGetColumn";

import { useBottomActionsOffset } from "../hooks/useBottomActions";
import { getMediaLinkContext } from "../utils/router";
import { useBottomActionsOffset } from "../components/BottomActions/useBottomActions";
import { PagePlaceholder } from "../components/Placeholder";

import { FlatList, getListItemLayout } from "~/components/Base/List";
Expand Down
2 changes: 1 addition & 1 deletion mobile/src/navigation/screens/albums/CurrentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { View } from "react-native";
import { Favorite } from "~/resources/icons/Favorite";
import { useAlbumForScreen, useFavoriteAlbum } from "~/data/album/queries";

import { useBottomActionsOffset } from "~/navigation/hooks/useBottomActions";
import { CurrentListLayout } from "~/navigation/layouts/CurrentListLayout";
import { AlbumArtworkSheet } from "~/navigation/sheets/ArtworkSheet";
import { useBottomActionsOffset } from "~/navigation/components/BottomActions/useBottomActions";
import { CurrentListMenu } from "~/navigation/components/CurrentListMenu";
import { PagePlaceholder } from "~/navigation/components/Placeholder";

Expand Down
2 changes: 1 addition & 1 deletion mobile/src/navigation/screens/artists/CurrentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { useArtistDetails, useArtistTracks } from "~/data/artist/queries";
import type { ArtistAlbum } from "~/data/artist/types";
import { useGetColumn } from "~/hooks/useGetColumn";

import { useBottomActionsOffset } from "~/navigation/hooks/useBottomActions";
import { CurrentListLayout } from "~/navigation/layouts/CurrentListLayout";
import { ArtistArtworkSheet } from "~/navigation/sheets/ArtworkSheet";
import { SortSheet } from "~/navigation/sheets/SortSheet";
import { useBottomActionsOffset } from "~/navigation/components/BottomActions/useBottomActions";
import { CurrentListMenu } from "~/navigation/components/CurrentListMenu";
import { PagePlaceholder } from "~/navigation/components/Placeholder";

Expand Down
2 changes: 1 addition & 1 deletion mobile/src/navigation/screens/genres/CurrentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import type { StaticScreenProps } from "@react-navigation/native";

import { useGenreDetails, useGenreTracks } from "~/data/genre/queries";

import { useBottomActionsOffset } from "~/navigation/hooks/useBottomActions";
import { CurrentListLayout } from "~/navigation/layouts/CurrentListLayout";
import { GenreArtworkSheet } from "~/navigation/sheets/ArtworkSheet";
import { SortSheet } from "~/navigation/sheets/SortSheet";
import { useBottomActionsOffset } from "~/navigation/components/BottomActions/useBottomActions";
import { CurrentListMenu } from "~/navigation/components/CurrentListMenu";
import { PagePlaceholder } from "~/navigation/components/Placeholder";

Expand Down
2 changes: 1 addition & 1 deletion mobile/src/navigation/screens/playlists/CurrentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
usePlaylistForScreen,
} from "~/data/playlist/queries";

import { useBottomActionsOffset } from "~/navigation/hooks/useBottomActions";
import { CurrentListLayout } from "~/navigation/layouts/CurrentListLayout";
import { PlaylistArtworkSheet } from "~/navigation/sheets/ArtworkSheet";
import { useBottomActionsOffset } from "~/navigation/components/BottomActions/useBottomActions";
import type { MenuAction } from "~/navigation/components/CurrentListMenu";
import { CurrentListMenu } from "~/navigation/components/CurrentListMenu";
import { PagePlaceholder } from "~/navigation/components/Placeholder";
Expand Down