Skip to content

Commit 577f07f

Browse files
committed
feat(mobile): enhance player UI with floating tab bar and control improvements
- Added PlayerTabBar component to display active track in the tab bar - Replaced FloatingBar with PlayerTabBar for consistent player controls - Improved play/pause button with animated transitions - Updated progress bar text styling with tabular numbers - Refined DismissIndicator styling in player screen Signed-off-by: Innei <tukon479@gmail.com>
1 parent 48461f1 commit 577f07f

File tree

7 files changed

+73
-57
lines changed

7 files changed

+73
-57
lines changed

apps/mobile/src/components/ui/tabbar/Tabbar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"
1717

1818
import { SetBottomTabBarHeightContext } from "@/src/components/ui/tabbar/contexts/BottomTabBarHeightContext"
1919
import { quickSpringPreset, softSpringPreset } from "@/src/constants/spring"
20+
import { PlayerTabBar } from "@/src/modules/player/PlayerTabBar"
2021
import { accentColor, useColor } from "@/src/theme/colors"
2122

2223
import { ThemedBlurView } from "../../common/ThemedBlurView"
@@ -42,7 +43,7 @@ export const Tabbar: FC<BottomTabBarProps> = (props) => {
4243
return (
4344
<Animated.View
4445
accessibilityRole="tablist"
45-
className="absolute inset-x-0 bottom-0 z-10 flex-row py-[7]"
46+
className="absolute inset-x-0 bottom-0 z-10 py-[7]"
4647
style={{
4748
paddingBottom: insets.bottom,
4849
transform: [{ translateY }],
@@ -52,6 +53,7 @@ export const Tabbar: FC<BottomTabBarProps> = (props) => {
5253
}}
5354
>
5455
<TabBarBackground />
56+
<PlayerTabBar />
5557
<Grid columns={routes.length} gap={10}>
5658
{routes.map((route, index) => {
5759
const focused = index === state.index

apps/mobile/src/modules/entry-list/templates/EntryGridItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
EntryContentWebView,
1212
setWebViewEntry,
1313
} from "@/src/components/native/webview/EntryContentWebView"
14-
import { MediaCarousel } from "@/src/components/ui/carousel/Carousel"
14+
import { MediaCarousel } from "@/src/components/ui/carousel/MediaCarousel"
1515
import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime"
1616
import { FeedIcon } from "@/src/components/ui/icon/feed-icon"
1717
import { PreviewImage } from "@/src/components/ui/image/PreviewImage"

apps/mobile/src/modules/player/FloatingBar.tsx

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { cn } from "@follow/utils"
2+
import { Image } from "expo-image"
3+
import { router, usePathname } from "expo-router"
4+
import { Pressable, Text, View } from "react-native"
5+
import Animated, { SlideInDown, SlideOutDown } from "react-native-reanimated"
6+
7+
import { useActiveTrack } from "@/src/lib/player"
8+
9+
import { PlayPauseButton, SeekButton } from "./control"
10+
11+
const allowedRoutes = new Set(["/", "/subscriptions", "/player"])
12+
13+
export function PlayerTabBar({ className }: { className?: string }) {
14+
const activeTrack = useActiveTrack()
15+
const pathname = usePathname()
16+
if (!activeTrack || !allowedRoutes.has(pathname)) {
17+
return null
18+
}
19+
20+
return (
21+
<View className="mb-2 overflow-hidden">
22+
<Animated.View
23+
entering={SlideInDown}
24+
exiting={SlideOutDown}
25+
className={cn("border-opaque-separator/70 border-b px-2 shadow-md", className)}
26+
>
27+
<Pressable
28+
onPress={() => {
29+
router.push("/player")
30+
}}
31+
>
32+
<View className="flex flex-row items-center gap-4 overflow-hidden rounded-2xl p-2">
33+
<Image source={activeTrack.artwork} className="size-12 rounded-lg" />
34+
<View className="flex-1 overflow-hidden">
35+
<Text className="text-label text-lg font-semibold" numberOfLines={1}>
36+
{activeTrack.title}
37+
</Text>
38+
</View>
39+
<View className="mr-2 flex flex-row gap-4">
40+
<PlayPauseButton />
41+
<SeekButton />
42+
</View>
43+
</View>
44+
</Pressable>
45+
</Animated.View>
46+
</View>
47+
)
48+
}

apps/mobile/src/modules/player/control.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { cn } from "@follow/utils"
22
import { router } from "expo-router"
3-
import { Text, TouchableOpacity, View } from "react-native"
3+
import { StyleSheet, Text, TouchableOpacity, View } from "react-native"
44
import { Slider } from "react-native-awesome-slider"
5-
import { useDerivedValue, useSharedValue } from "react-native-reanimated"
5+
import { FadeOut, useDerivedValue, useSharedValue, ZoomIn } from "react-native-reanimated"
66
import * as DropdownMenu from "zeego/dropdown-menu"
77

8+
import { ReAnimatedPressable } from "@/src/components/common/AnimatedComponents"
89
import { Back2CuteReIcon } from "@/src/icons/back_2_cute_re"
910
import { Forward2CuteReIcon } from "@/src/icons/forward_2_cute_re"
1011
import { PauseCuteFiIcon } from "@/src/icons/pause_cute_fi"
@@ -31,7 +32,10 @@ export function PlayPauseButton({ size = 24, className, color }: ControlButtonPr
3132
const label = useColor("label")
3233
return (
3334
<View className={className}>
34-
<TouchableOpacity
35+
<ReAnimatedPressable
36+
entering={ZoomIn.springify().damping(10).stiffness(200).mass(1)}
37+
exiting={FadeOut}
38+
key={playing ? "pause" : "play"}
3539
onPress={() => {
3640
playing ? player.pause() : player.play()
3741
}}
@@ -41,7 +45,7 @@ export function PlayPauseButton({ size = 24, className, color }: ControlButtonPr
4145
) : (
4246
<PlayCuteFiIcon color={color ?? label} width={size} height={size} />
4347
)}
44-
</TouchableOpacity>
48+
</ReAnimatedPressable>
4549
</View>
4650
)
4751
}
@@ -191,21 +195,24 @@ export function ProgressBar() {
191195

192196
<View className="mt-3 flex-row justify-between">
193197
<Text
198+
style={styles.text}
194199
className={cn(
195-
"font-mono text-xs font-medium tracking-wider opacity-75",
200+
"font-mono text-xs font-medium opacity-75",
196201
isBackgroundLight ? "text-black" : "text-white",
197202
)}
198203
>
199204
{trackElapsedTime}
200205
</Text>
201206

202207
<Text
208+
style={styles.text}
203209
className={cn(
204-
"font-mono text-xs font-medium tracking-wider opacity-75",
210+
"font-mono text-xs font-medium opacity-75",
205211
isBackgroundLight ? "text-black" : "text-white",
206212
)}
207213
>
208-
{"-"} {trackRemainingTime}
214+
{"-"}
215+
{trackRemainingTime}
209216
</Text>
210217
</View>
211218
</View>
@@ -250,3 +257,9 @@ export function VolumeBar() {
250257
</View>
251258
)
252259
}
260+
261+
const styles = StyleSheet.create({
262+
text: {
263+
fontVariant: ["tabular-nums"],
264+
},
265+
})

apps/mobile/src/screens/player.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default function PlaterScreen() {
102102
function DismissIndicator() {
103103
return (
104104
<View className="absolute inset-x-0 flex items-center justify-center top-safe-offset-2">
105-
<View className="h-[5] w-[40] rounded-full bg-white/45" />
105+
<View className="bg-tertiary-label h-[5] w-[40] rounded-full" />
106106
</View>
107107
)
108108
}

0 commit comments

Comments
 (0)