Fixed Android Safe View Areas (#294)
* Fixed Android safe view areas * fix: xmark positioning * fix: redundant safeareaprovider * fix: roll back redundant changes * fix: linter --------- Co-authored-by: Lei Nelissen <lei@codified.nl>
This commit is contained in:
@@ -49,7 +49,7 @@
|
|||||||
"download-track": "Download Track",
|
"download-track": "Download Track",
|
||||||
"download-album": "Download Album",
|
"download-album": "Download Album",
|
||||||
"download-playlist": "Download Playlist",
|
"download-playlist": "Download Playlist",
|
||||||
"no-downloads": "You have not yet downloaded any tracks",
|
"no-downloads": "You have not downloaded any tracks yet",
|
||||||
"delete-track": "Delete Track",
|
"delete-track": "Delete Track",
|
||||||
"delete-all-tracks": "Delete All Tracks",
|
"delete-all-tracks": "Delete All Tracks",
|
||||||
"confirm-delete-all-tracks": "Are you sure you want to delete all currently downloaded tracks?",
|
"confirm-delete-all-tracks": "Are you sure you want to delete all currently downloaded tracks?",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StatusBar, StyleSheet } from 'react-native';
|
||||||
import { createStackNavigator } from '@react-navigation/stack';
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView, useUserOrSystemScheme } from '@/components/Colors';
|
||||||
import { StackParams } from '@/screens/types';
|
import { StackParams } from '@/screens/types';
|
||||||
import NowPlaying from './overlays/NowPlaying';
|
import NowPlaying from './overlays/NowPlaying';
|
||||||
|
|
||||||
@@ -14,14 +14,18 @@ import Playlists from './stacks/Playlists';
|
|||||||
import Playlist from './stacks/Playlist';
|
import Playlist from './stacks/Playlist';
|
||||||
import Artists from './stacks/Artists';
|
import Artists from './stacks/Artists';
|
||||||
import Artist from './stacks/Artist';
|
import Artist from './stacks/Artist';
|
||||||
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
const Stack = createStackNavigator<StackParams>();
|
const Stack = createStackNavigator<StackParams>();
|
||||||
|
|
||||||
function MusicStack() {
|
function MusicStack() {
|
||||||
const defaultStyles = useDefaultStyles();
|
const defaultStyles = useDefaultStyles();
|
||||||
|
const scheme = useUserOrSystemScheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SafeAreaProvider>
|
||||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
|
<StatusBar backgroundColor="transparent" barStyle={scheme === 'dark' ? 'light-content' : 'dark-content'} />
|
||||||
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={{
|
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={{
|
||||||
headerTintColor: defaultStyles.themeColor.color,
|
headerTintColor: defaultStyles.themeColor.color,
|
||||||
headerTitleStyle: defaultStyles.stackHeader,
|
headerTitleStyle: defaultStyles.stackHeader,
|
||||||
@@ -39,6 +43,7 @@ function MusicStack() {
|
|||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
<NowPlaying />
|
<NowPlaying />
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
|
</SafeAreaProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useGetImage } from '@/utility/JellyfinApi/lib';
|
import { useGetImage } from '@/utility/JellyfinApi/lib';
|
||||||
import { Text, SafeAreaView, StyleSheet } from 'react-native';
|
import { Text, StyleSheet, View } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { useAppDispatch, useTypedSelector } from '@/store';
|
import { useAppDispatch, useTypedSelector } from '@/store';
|
||||||
import { fetchRecentAlbums } from '@/store/music/actions';
|
import { fetchRecentAlbums } from '@/store/music/actions';
|
||||||
@@ -18,6 +18,7 @@ import styled from 'styled-components/native';
|
|||||||
import { ShadowWrapper } from '@/components/Shadow';
|
import { ShadowWrapper } from '@/components/Shadow';
|
||||||
import { NavigationProp } from '@/screens/types';
|
import { NavigationProp } from '@/screens/types';
|
||||||
import { SafeFlatList } from '@/components/SafeNavigatorView';
|
import { SafeFlatList } from '@/components/SafeNavigatorView';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
columnWrapper: {
|
columnWrapper: {
|
||||||
@@ -78,8 +79,14 @@ const RecentAlbums: React.FC = () => {
|
|||||||
// Retrieve data on mount
|
// Retrieve data on mount
|
||||||
useEffect(() => { retrieveData(); }, [retrieveData]);
|
useEffect(() => { retrieveData(); }, [retrieveData]);
|
||||||
|
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: insets.top,
|
||||||
|
paddingBottom: 1 * insets.bottom,
|
||||||
|
}}>
|
||||||
<SafeFlatList
|
<SafeFlatList
|
||||||
data={recentAlbums as string[]}
|
data={recentAlbums as string[]}
|
||||||
refreshing={isLoading}
|
refreshing={isLoading}
|
||||||
@@ -100,7 +107,7 @@ const RecentAlbums: React.FC = () => {
|
|||||||
</TouchableHandler>
|
</TouchableHandler>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Album from '@/screens/Music/stacks/Album';
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import NowPlaying from '@/screens/Music/overlays/NowPlaying';
|
import NowPlaying from '@/screens/Music/overlays/NowPlaying';
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
const Stack = createStackNavigator<StackParams>();
|
const Stack = createStackNavigator<StackParams>();
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ function SearchStack() {
|
|||||||
const [isInitialRoute, setIsInitialRoute] = useState(true);
|
const [isInitialRoute, setIsInitialRoute] = useState(true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SafeAreaProvider>
|
||||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
<Stack.Navigator initialRouteName="Search"
|
<Stack.Navigator initialRouteName="Search"
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
@@ -37,6 +39,7 @@ function SearchStack() {
|
|||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
<NowPlaying offset={isInitialRoute ? 64 : 0} />
|
<NowPlaying offset={isInitialRoute ? 64 : 0} />
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
|
</SafeAreaProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||||
import Input from '@/components/Input';
|
import Input from '@/components/Input';
|
||||||
import { ActivityIndicator, Animated, KeyboardAvoidingView, Platform, SafeAreaView, View } from 'react-native';
|
import { ActivityIndicator, Animated, KeyboardAvoidingView, Platform, View } from 'react-native';
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import { useAppDispatch, useTypedSelector } from '@/store';
|
import { useAppDispatch, useTypedSelector } from '@/store';
|
||||||
import Fuse, { IFuseOptions } from 'fuse.js';
|
import Fuse, { IFuseOptions } from 'fuse.js';
|
||||||
@@ -21,6 +21,7 @@ import { ShadowWrapper } from '@/components/Shadow';
|
|||||||
import { NavigationProp } from '@/screens/types';
|
import { NavigationProp } from '@/screens/types';
|
||||||
import { useNavigationOffsets } from '@/components/SafeNavigatorView';
|
import { useNavigationOffsets } from '@/components/SafeNavigatorView';
|
||||||
import BaseAlbumImage from '@/screens/Music/stacks/components/AlbumImage';
|
import BaseAlbumImage from '@/screens/Music/stacks/components/AlbumImage';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
// import MicrophoneIcon from '@/assets/icons/microphone.svg';
|
// import MicrophoneIcon from '@/assets/icons/microphone.svg';
|
||||||
// import AlbumIcon from '@/assets/icons/collection.svg';
|
// import AlbumIcon from '@/assets/icons/collection.svg';
|
||||||
// import TrackIcon from '@/assets/icons/note.svg';
|
// import TrackIcon from '@/assets/icons/note.svg';
|
||||||
@@ -31,7 +32,8 @@ import BaseAlbumImage from '@/screens/Music/stacks/components/AlbumImage';
|
|||||||
|
|
||||||
const KEYBOARD_OFFSET = Platform.select({
|
const KEYBOARD_OFFSET = Platform.select({
|
||||||
ios: 0,
|
ios: 0,
|
||||||
android: 72,
|
// Android 15+ has edge-to-edge support, changing the keyboard offset to 0
|
||||||
|
android: Number.parseInt(Platform.Version as string) >= 35 ? 0 : 72,
|
||||||
});
|
});
|
||||||
const SEARCH_INPUT_HEIGHT = 62;
|
const SEARCH_INPUT_HEIGHT = 62;
|
||||||
|
|
||||||
@@ -266,8 +268,10 @@ export default function Search() {
|
|||||||
...jellyfinResults,
|
...jellyfinResults,
|
||||||
]), [fuseResults, jellyfinResults]);
|
]), [fuseResults, jellyfinResults]);
|
||||||
|
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1, marginBottom: offsets.bottom }}>
|
<View style={{ flex: 1, paddingTop: insets.top, marginBottom: offsets.bottom }}>
|
||||||
<KeyboardAvoidingView behavior="height" style={{ flex: 1 }} keyboardVerticalOffset={KEYBOARD_OFFSET}>
|
<KeyboardAvoidingView behavior="height" style={{ flex: 1 }} keyboardVerticalOffset={KEYBOARD_OFFSET}>
|
||||||
<FlatList
|
<FlatList
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
@@ -323,6 +327,6 @@ export default function Search() {
|
|||||||
) : null}
|
) : null}
|
||||||
{SearchInput}
|
{SearchInput}
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
</SafeAreaView>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ import { Paragraph } from '@/components/Typography';
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Switch } from 'react-native-gesture-handler';
|
import { Switch } from 'react-native-gesture-handler';
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import { SafeScrollView } from '@/components/SafeNavigatorView';
|
|
||||||
import { useAppDispatch, useTypedSelector } from '@/store';
|
import { useAppDispatch, useTypedSelector } from '@/store';
|
||||||
import { setEnablePlaybackReporting } from '@/store/settings/actions';
|
import { setEnablePlaybackReporting } from '@/store/settings/actions';
|
||||||
import Container from '../components/Container';
|
import Container from '../components/Container';
|
||||||
@@ -17,7 +16,6 @@ export default function PlaybackReporting() {
|
|||||||
}, [isEnabled, dispatch]);
|
}, [isEnabled, dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeScrollView>
|
|
||||||
<Container>
|
<Container>
|
||||||
<Paragraph>{t('playback-reporting-description')}</Paragraph>
|
<Paragraph>{t('playback-reporting-description')}</Paragraph>
|
||||||
<SwitchContainer>
|
<SwitchContainer>
|
||||||
@@ -25,6 +23,5 @@ export default function PlaybackReporting() {
|
|||||||
<Switch value={isEnabled} onValueChange={toggleSwitch} />
|
<Switch value={isEnabled} onValueChange={toggleSwitch} />
|
||||||
</SwitchContainer>
|
</SwitchContainer>
|
||||||
</Container>
|
</Container>
|
||||||
</SafeScrollView>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ export default function Routes() {
|
|||||||
}} id="MAIN">
|
}} id="MAIN">
|
||||||
<Stack.Screen name="Screens" component={Screens} />
|
<Stack.Screen name="Screens" component={Screens} />
|
||||||
<Stack.Screen name="SetJellyfinServer" component={SetJellyfinServer} />
|
<Stack.Screen name="SetJellyfinServer" component={SetJellyfinServer} />
|
||||||
<Stack.Screen name="TrackPopupMenu" component={TrackPopupMenu} options={{ presentation: 'formSheet' }} />
|
<Stack.Screen name="TrackPopupMenu" component={TrackPopupMenu} options={{ presentation: 'formSheet', sheetCornerRadius: 10, sheetAllowedDetents: [0.85, 1.0]}} />
|
||||||
<Stack.Screen name="ErrorReporting" component={ErrorReportingPopup} />
|
<Stack.Screen name="ErrorReporting" component={ErrorReportingPopup} />
|
||||||
<Stack.Screen name="Player" component={Player} />
|
<Stack.Screen name="Player" component={Player} />
|
||||||
<Stack.Screen name="Lyrics" component={Lyrics} />
|
<Stack.Screen name="Lyrics" component={Lyrics} />
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import XmarkIcon from '@/assets/icons/xmark.svg';
|
import XmarkIcon from '@/assets/icons/xmark.svg';
|
||||||
import { TouchableOpacity } from 'react-native';
|
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
|
|
||||||
const Container = styled.View`
|
const Container = styled.TouchableOpacity`
|
||||||
padding: 6px 12px;
|
padding: 12px 0px;
|
||||||
|
z-index: 2;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function BackButton() {
|
function BackButton() {
|
||||||
@@ -16,10 +16,8 @@ function BackButton() {
|
|||||||
}, [navigation]);
|
}, [navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container onPress={handlePress}>
|
||||||
<TouchableOpacity onPress={handlePress}>
|
|
||||||
<XmarkIcon />
|
<XmarkIcon />
|
||||||
</TouchableOpacity>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ export default function Player() {
|
|||||||
return (
|
return (
|
||||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
<ColoredBlurView>
|
<ColoredBlurView>
|
||||||
{Platform.OS === 'android' && (<BackButton />)}
|
|
||||||
<Queue header={(
|
<Queue header={(
|
||||||
<>
|
<>
|
||||||
|
{Platform.OS === 'android' && (
|
||||||
|
<BackButton />
|
||||||
|
)}
|
||||||
<NowPlaying />
|
<NowPlaying />
|
||||||
<ConnectionNotice />
|
<ConnectionNotice />
|
||||||
<StreamStatus />
|
<StreamStatus />
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ function TrackPopupMenu() {
|
|||||||
}, [trackId, dispatch, closeModal]);
|
}, [trackId, dispatch, closeModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColoredBlurView>
|
<ColoredBlurView style={{flex: 1}}>
|
||||||
<Container>
|
<Container>
|
||||||
<Artwork src={getImage(track)} />
|
<Artwork src={getImage(track)} />
|
||||||
<Header>{track?.Name}</Header>
|
<Header>{track?.Name}</Header>
|
||||||
|
|||||||
Reference in New Issue
Block a user