From 61e7b87d1ebc925e95ce9b07f7d021778c8e3c04 Mon Sep 17 00:00:00 2001 From: Boof2015 <75185879+Boof2015@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:28:37 -0400 Subject: [PATCH] fix android notification not working, fix ui bug --- src/app/(tabs)/_layout.tsx | 6 ++++- src/app/+native-intent.ts | 15 +++++++++++ src/app/notification.click.tsx | 34 ++++++++++++++++++++++++ src/audio/notificationIntent.ts | 46 +++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/app/+native-intent.ts create mode 100644 src/app/notification.click.tsx create mode 100644 src/audio/notificationIntent.ts diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 5710491..fd1dbda 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -1,10 +1,14 @@ import { Tabs } from 'expo-router'; import { TabBar, type TabItem } from '@/components/TabBar'; +import { colors } from '@/theme'; export default function TabsLayout() { return ( { const items: TabItem[] = state.routes.map((route, index) => ({ key: route.key, diff --git a/src/app/+native-intent.ts b/src/app/+native-intent.ts new file mode 100644 index 0000000..81ab094 --- /dev/null +++ b/src/app/+native-intent.ts @@ -0,0 +1,15 @@ +import { + getNotificationClickRedirectPath, + isNotificationClickPath, +} from '@/audio/notificationIntent'; + +type RedirectSystemPathEvent = { + path: string; + initial: boolean; +}; + +export async function redirectSystemPath({ path }: RedirectSystemPathEvent): Promise { + if (!isNotificationClickPath(path)) return path; + + return getNotificationClickRedirectPath(); +} diff --git a/src/app/notification.click.tsx b/src/app/notification.click.tsx new file mode 100644 index 0000000..59a9c6d --- /dev/null +++ b/src/app/notification.click.tsx @@ -0,0 +1,34 @@ +import { useEffect } from 'react'; +import { StyleSheet, View } from 'react-native'; +import { useRouter } from 'expo-router'; +import { getNotificationClickRedirectPath } from '@/audio/notificationIntent'; +import { colors } from '@/theme'; + +export default function NotificationClickRoute() { + const router = useRouter(); + + useEffect(() => { + let cancelled = false; + + getNotificationClickRedirectPath() + .then((href) => { + if (!cancelled) router.replace(href); + }) + .catch(() => { + if (!cancelled) router.replace('/'); + }); + + return () => { + cancelled = true; + }; + }, [router]); + + return ; +} + +const styles = StyleSheet.create({ + root: { + flex: 1, + backgroundColor: colors.bgPrimary, + }, +}); diff --git a/src/audio/notificationIntent.ts b/src/audio/notificationIntent.ts new file mode 100644 index 0000000..c911844 --- /dev/null +++ b/src/audio/notificationIntent.ts @@ -0,0 +1,46 @@ +import TrackPlayer from 'react-native-track-player'; +import { usePlayerStore } from '@/stores/playerStore'; + +const NOTIFICATION_CLICK_TARGETS = new Set([ + 'trackplayer://notification.click', + 'astra://notification.click', + 'astra:///notification.click', + '/notification.click', + 'notification.click', +]); + +export type NotificationClickRedirectPath = '/' | '/now-playing'; + +export function isNotificationClickPath(path: string): boolean { + const cleanPath = path.trim(); + const pathWithoutSuffix = cleanPath.split(/[?#]/, 1)[0]; + + if ( + NOTIFICATION_CLICK_TARGETS.has(cleanPath) || + NOTIFICATION_CLICK_TARGETS.has(pathWithoutSuffix) || + pathWithoutSuffix.replace(/^\/+/, '') === 'notification.click' + ) { + return true; + } + + try { + const url = new URL(cleanPath); + return url.hostname === 'notification.click' || url.pathname === '/notification.click'; + } catch { + return false; + } +} + +export async function getNotificationClickRedirectPath(): Promise { + return (await hasLoadedTrack()) ? '/now-playing' : '/'; +} + +async function hasLoadedTrack(): Promise { + try { + if (await TrackPlayer.getActiveTrack()) return true; + } catch { + // RNTP may not be initialized if an unexpected notification click arrives. + } + + return Boolean(usePlayerStore.getState().currentTrack); +}