feat: introduce high contrast mode for ios

fixes #194
This commit is contained in:
Lei Nelissen
2024-02-12 00:01:09 +01:00
parent f95c79b254
commit 82b4223939
28 changed files with 187 additions and 109 deletions

View File

@@ -958,6 +958,10 @@ PODS:
- React-Mapbuffer (0.73.4):
- glog
- React-debug
- react-native-accessibility-settings (0.1.2):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- react-native-blur (4.4.0):
- glog
- RCT-Folly (= 2022.05.16.00)
@@ -1246,6 +1250,7 @@ DEPENDENCIES:
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- react-native-accessibility-settings (from `../node_modules/react-native-accessibility-settings`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
@@ -1364,6 +1369,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/logger"
React-Mapbuffer:
:path: "../node_modules/react-native/ReactCommon"
react-native-accessibility-settings:
:path: "../node_modules/react-native-accessibility-settings"
react-native-blur:
:path: "../node_modules/@react-native-community/blur"
react-native-netinfo:
@@ -1481,6 +1488,7 @@ SPEC CHECKSUMS:
React-jsinspector: 9ac353eccf6ab54d1e0a33862ba91221d1e88460
React-logger: 0a57b68dd2aec7ff738195f081f0520724b35dab
React-Mapbuffer: 63913773ed7f96b814a2521e13e6d010282096ad
react-native-accessibility-settings: 9e1c5c6e8268015f8447faa7d34a5834fbaf4d8c
react-native-blur: 27113acc008facbc8accae5fb3a78b8424f64cfd
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b

13
package-lock.json generated
View File

@@ -28,6 +28,7 @@
"react": "^18.2.0",
"react-airplay": "^1.2.0",
"react-native": "^0.73.4",
"react-native-accessibility-settings": "^0.1.2",
"react-native-collapsible": "^1.6.1",
"react-native-dotenv": "^3.4.9",
"react-native-fast-image": "^8.6.3",
@@ -9452,6 +9453,18 @@
"react": "18.2.0"
}
},
"node_modules/react-native-accessibility-settings": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/react-native-accessibility-settings/-/react-native-accessibility-settings-0.1.2.tgz",
"integrity": "sha512-TYk5zZ0Un10eXQgJHn+GL5g/uVD1k8lFJPQZRnOxJcuqh6afA9mluVCF+OzvYJerwsJC2tDVzOXyrGM0/QrXsA==",
"engines": {
"node": ">= 16.0.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-collapsible": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz",

View File

@@ -31,6 +31,7 @@
"react": "^18.2.0",
"react-airplay": "^1.2.0",
"react-native": "^0.73.4",
"react-native-accessibility-settings": "^0.1.2",
"react-native-collapsible": "^1.6.1",
"react-native-dotenv": "^3.4.9",
"react-native-fast-image": "^8.6.3",

View File

@@ -1,4 +1,3 @@
export const ALBUM_CACHE_AMOUNT_OF_DAYS = 7;
export const PLAYLIST_CACHE_AMOUNT_OF_DAYS = 7;
export const ALPHABET_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ';
export const THEME_COLOR = '#FF3C00';

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useState } from 'react';
import styled from 'styled-components/native';
import { ALPHABET_LETTERS, THEME_COLOR } from '@/CONSTANTS';
import { ALPHABET_LETTERS } from '@/CONSTANTS';
import { View, LayoutChangeEvent } from 'react-native';
import {
PanGestureHandler,
@@ -8,6 +8,7 @@ import {
TapGestureHandler,
TapGestureHandlerGestureEvent
} from 'react-native-gesture-handler';
import useDefaultStyles from './Colors';
// interface LetterContainerProps {
// onPress: (letter: string) => void;
@@ -29,7 +30,6 @@ const Letter = styled.Text`
text-align: center;
padding: 1px 0;
font-size: 12px;
color: ${THEME_COLOR};
`;
interface Props {
@@ -41,6 +41,7 @@ interface Props {
* screen with all letters of the Alphabet.
*/
const AlphabetScroller: React.FC<Props> = ({ onSelect }) => {
const styles = useDefaultStyles();
const [ height, setHeight ] = useState(0);
const [ index, setIndex ] = useState<number>();
@@ -69,7 +70,9 @@ const AlphabetScroller: React.FC<Props> = ({ onSelect }) => {
key={l}
onLayout={i === 0 ? handleLayout : undefined}
>
<Letter>{l}</Letter>
<Letter style={styles.themeColor}>
{l}
</Letter>
</View>
))}
</View>

View File

@@ -3,7 +3,6 @@ import { SvgProps } from 'react-native-svg';
import {
PressableProps, ViewProps, View,
} from 'react-native';
import { THEME_COLOR } from '@/CONSTANTS';
import styled, { css } from 'styled-components/native';
import useDefaultStyles from './Colors';
@@ -35,7 +34,6 @@ const BaseButton = styled.Pressable<{ size: ButtonSize }>`
`;
const ButtonText = styled.Text<{ active?: boolean, size: ButtonSize }>`
color: ${THEME_COLOR};
font-weight: 500;
font-size: 14px;
flex-shrink: 1;
@@ -72,16 +70,17 @@ const Button = React.forwardRef<View, ButtonProps>(function Button(props, ref) {
<Icon
width={14}
height={14}
fill={THEME_COLOR}
style={{
marginRight: title ? 8 : 0,
}}
fill={defaultStyles.themeColor.color}
style={[
{ marginRight: title ? 8 : 0 }
]}
/>
}
{title ? (
<ButtonText
active={isPressed}
size={size}
style={defaultStyles.themeColor}
numberOfLines={1}
>
{title}

View File

@@ -1,10 +1,10 @@
import { BlurView, BlurViewProps } from '@react-native-community/blur';
import { THEME_COLOR } from '@/CONSTANTS';
import React, { PropsWithChildren } from 'react';
import { useContext } from 'react';
import { ColorSchemeName, Platform, StyleSheet, View, useColorScheme } from 'react-native';
import { useTypedSelector } from '@/store';
import { ColorScheme } from '@/store/settings/types';
import { useAccessibilitySetting } from 'react-native-accessibility-settings';
const majorPlatformVersion = typeof Platform.Version === 'string' ? parseInt(Platform.Version, 10) : Platform.Version;
@@ -12,7 +12,7 @@ const majorPlatformVersion = typeof Platform.Version === 'string' ? parseInt(Pla
* Function for generating both the dark and light stylesheets, so that they
* don't have to be generate on every individual component render
*/
function generateStyles(scheme: ColorSchemeName) {
function generateStyles(scheme: ColorSchemeName, highContrast: boolean) {
return StyleSheet.create({
text: {
color: scheme === 'dark' ? '#fff' : '#000',
@@ -20,12 +20,15 @@ function generateStyles(scheme: ColorSchemeName) {
fontFamily: 'Inter',
},
textHalfOpacity: {
color: scheme === 'dark' ? '#ffffff88' : '#00000088',
color: highContrast
? (scheme === 'dark' ? '#ffffffbb' : '#000000bb')
: (scheme === 'dark' ? '#ffffff88' : '#00000088'),
fontSize: 14,
// fontFamily: 'Inter',
},
textQuarterOpacity: {
color: scheme === 'dark' ? '#ffffff44' : '#00000044',
color: highContrast
? (scheme === 'dark' ? '#ffffff88' : '#00000088')
: (scheme === 'dark' ? '#ffffff44' : '#00000044'),
fontSize: 14,
},
view: {
@@ -35,7 +38,9 @@ function generateStyles(scheme: ColorSchemeName) {
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
},
activeBackground: {
backgroundColor: `${THEME_COLOR}${scheme === 'dark' ? '26' : '16'}`,
backgroundColor: highContrast
? `#8b513c${scheme === 'dark' ? '26' : '10'}`
: `#FF3C00${scheme === 'dark' ? '26' : '16'}`,
},
imageBackground: {
backgroundColor: scheme === 'dark' ? '#191919' : '#eee',
@@ -49,7 +54,9 @@ function generateStyles(scheme: ColorSchemeName) {
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
},
button: {
backgroundColor: scheme === 'dark' ? '#ffffff09' : '#00000009',
backgroundColor: highContrast
? (scheme === 'dark' ? '#ffffff0f' : '#0000000f')
: (scheme === 'dark' ? '#ffffff09' : '#00000009'),
},
input: {
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
@@ -67,13 +74,35 @@ function generateStyles(scheme: ColorSchemeName) {
filter: {
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
},
themeColor: {
color: highContrast
? scheme === 'dark' ? '#FF7A1C' : '#c93400'
: '#FF3C00',
},
themeColorHalfOpacity: {
color: highContrast
? scheme === 'dark' ? '#FF7A1Cbb' : '#c93400bb'
: '#FF3C0088',
},
themeColorQuarterOpacity: {
color: highContrast
? scheme === 'dark' ? '#FF7A1C88' : '#c9340088'
: '#FF3C0044',
},
themeBackground: {
backgroundColor: highContrast
? scheme === 'dark' ? '#FF7A1C' : '#c93400'
: '#FF3C00',
}
});
}
// Prerender both stylesheets
export const themes: Record<'dark' | 'light', ReturnType<typeof generateStyles>> = {
'dark': generateStyles('dark'),
'light': generateStyles('light'),
export const themes: Record<'dark' | 'light' | 'dark-highcontrast' | 'light-highcontrast', ReturnType<typeof generateStyles>> = {
'dark': generateStyles('dark', false),
'light': generateStyles('light', false),
'dark-highcontrast': generateStyles('dark', true),
'light-highcontrast': generateStyles('light', true),
};
// Create context for supplying the theming information
@@ -84,9 +113,12 @@ export const ColorSchemeContext = React.createContext(themes.dark);
*/
export function ColorSchemeProvider({ children }: PropsWithChildren<{}>) {
const systemScheme = useColorScheme();
const highContrast = useAccessibilitySetting('darkerSystemColors');
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
const theme = themes[scheme || 'light'];
const theme = highContrast
? themes[`${scheme || 'light'}-highcontrast`]
: themes[scheme || 'light'];
return (
<ColorSchemeContext.Provider value={theme}>

View File

@@ -6,13 +6,14 @@ import CloudExclamationMarkIcon from '@/assets/icons/cloud-exclamation-mark.svg'
import InternalDriveIcon from '@/assets/icons/internal-drive.svg';
import useDefaultStyles from './Colors';
import Svg, { Circle, CircleProps } from 'react-native-svg';
import { Animated, Easing } from 'react-native';
import { Animated, Easing, ViewProps } from 'react-native';
import styled from 'styled-components/native';
interface DownloadIconProps {
trackId: string;
size?: number;
fill?: string;
style?: ViewProps['style'];
}
const DownloadContainer = styled.View`
@@ -26,7 +27,7 @@ const IconOverlay = styled.View`
transform: scale(0.5);
`;
function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
function DownloadIcon({ trackId, size = 16, fill, style }: DownloadIconProps) {
// determine styles
const defaultStyles = useDefaultStyles();
const iconFill = fill || defaultStyles.textQuarterOpacity.color;
@@ -66,19 +67,19 @@ function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
if (!entity && !isQueued) {
return (
<CloudIcon width={size} height={size} fill={iconFill} />
<CloudIcon width={size} height={size} fill={iconFill} style={style} />
);
}
if (entity?.isComplete) {
return (
<InternalDriveIcon width={size} height={size} fill={iconFill} />
<InternalDriveIcon width={size} height={size} fill={iconFill} style={style} />
);
}
if (entity?.isFailed) {
return (
<CloudExclamationMarkIcon width={size} height={size} fill={iconFill} />
<CloudExclamationMarkIcon width={size} height={size} fill={iconFill} style={style} />
);
}
@@ -100,7 +101,7 @@ function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
/>
</Svg>
<IconOverlay>
<CloudDownArrow width={size} height={size} fill={iconFill} />
<CloudDownArrow width={size} height={size} fill={iconFill} style={style} />
</IconOverlay>
</DownloadContainer>
);

View File

@@ -2,7 +2,6 @@ import React, { useCallback, useState } from 'react';
import { TouchableOpacityProps } from 'react-native';
import ChevronRight from '@/assets/icons/chevron-right.svg';
import styled from 'styled-components/native';
import { THEME_COLOR } from '@/CONSTANTS';
import useDefaultStyles from './Colors';
const BUTTON_SIZE = 14;
@@ -17,7 +16,6 @@ const Container = styled.Pressable<{ active?: boolean }>`
`;
const Label = styled.Text<{ active?: boolean }>`
color: ${THEME_COLOR};
font-size: 16px;
`;
@@ -37,8 +35,14 @@ const ListButton: React.FC<TouchableOpacityProps> = ({ children, ...props }) =>
isPressed ? defaultStyles.activeBackground : undefined
]}
>
<Label>{children}</Label>
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={THEME_COLOR} />
<Label style={defaultStyles.themeColor}>
{children}
</Label>
<ChevronRight
width={BUTTON_SIZE}
height={BUTTON_SIZE}
fill={defaultStyles.themeColor.color}
/>
</Container>
);
};

View File

@@ -1,4 +1,3 @@
import { THEME_COLOR } from '@/CONSTANTS';
import styled from 'styled-components/native';
import Animated from 'react-native-reanimated';
@@ -51,7 +50,6 @@ const ProgressTrack = styled(Animated.View)<ProgressTrackProps>`
left: 0;
right: 0;
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
background-color: ${THEME_COLOR};
opacity: ${(props) => props.opacity || 1};
border-radius: 99px;
`;

View File

@@ -15,7 +15,6 @@ import FastImage from 'react-native-fast-image';
import { useGetImage } from '@/utility/JellyfinApi';
import { ShadowWrapper } from '@/components/Shadow';
import { SafeFlatList } from '@/components/SafeNavigatorView';
import { THEME_COLOR } from '@/CONSTANTS';
import { t } from '@/localisation';
const DownloadedTrack = styled.View`
@@ -165,7 +164,9 @@ function Downloads() {
</DownloadedTrack>
{entities[item]?.error && (
<ErrorWrapper>
<Text style={{ color: THEME_COLOR }}>{entities[item]?.error}</Text>
<Text style={defaultStyles.themeColor}>
{entities[item]?.error}
</Text>
</ErrorWrapper>
)}
</>

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { StyleSheet } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { THEME_COLOR } from '@/CONSTANTS';
import { t } from '@/localisation';
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
import { StackParams } from '@/screens/types';
@@ -24,7 +23,7 @@ function MusicStack() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={{
headerTintColor: THEME_COLOR,
headerTintColor: defaultStyles.themeColor.color,
headerTitleStyle: defaultStyles.stackHeader,
cardStyle: defaultStyles.view,
headerTransparent: true,

View File

@@ -14,7 +14,6 @@ import { Text } from '@/components/Typography';
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
import { useNavigation } from '@react-navigation/native';
import { calculateProgressTranslation } from '@/components/Progresstrack';
import { THEME_COLOR } from '@/CONSTANTS';
import { NavigationProp } from '@/screens/types';
import { ShadowWrapper } from '@/components/Shadow';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
@@ -48,7 +47,6 @@ const ProgressTrack = styled(Animated.View)<{ stroke?: number; opacity?: number}
left: 0;
right: 0;
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
background-color: ${THEME_COLOR};
opacity: ${(props) => props.opacity || 1};
border-radius: 99px;
`;
@@ -191,12 +189,18 @@ function NowPlaying({ offset = 0 }: { offset?: number }) {
<SelectActionButton />
</ActionButton>
<ProgressTrack
style={{ transform: [{ translateX: bufferAnimation.current }]}}
style={[
{ transform: [{ translateX: bufferAnimation.current }]},
defaultStyles.themeBackground,
]}
opacity={0.15}
stroke={4}
/>
<ProgressTrack
style={{ transform: [{ translateX: progressAnimation.current }]}}
style={[
{ transform: [{ translateX: progressAnimation.current }]},
defaultStyles.themeBackground,
]}
stroke={4}
/>
</InnerContainer>

View File

@@ -5,7 +5,7 @@ import { useNavigation } from '@react-navigation/native';
import { differenceInDays } from 'date-fns';
import { useAppDispatch, useTypedSelector } from '@/store';
import { fetchAllAlbums } from '@/store/music/actions';
import { ALBUM_CACHE_AMOUNT_OF_DAYS, THEME_COLOR } from '@/CONSTANTS';
import { ALBUM_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
import AlbumImage from './components/AlbumImage';
import { SectionArtistItem, SectionedArtist, selectArtists } from '@/store/music/selectors';
import AlphabetScroller from '@/components/AlphabetScroller';
@@ -92,7 +92,7 @@ const GeneratedArtistItem = React.memo(function GeneratedArtistItem(props: Gener
numberOfLines={1}
style={[
defaultStyles.text,
pressed && { color: THEME_COLOR },
pressed && defaultStyles.themeColor,
{ flexShrink: 1 }
]}
@@ -201,7 +201,7 @@ const Artists: React.FC = () => {
onRefresh={retrieveData}
getItemLayout={(_, i) => {
if (!(i in itemLayouts)) {
console.log('COuLD NOT FIND LAYOUT ITEM', i, _);
// console.log('COuLD NOT FIND LAYOUT ITEM', i, _);
}
return itemLayouts[i] ?? { length: 0, offset: 0, index: i };
}}

View File

@@ -4,7 +4,6 @@ import { useGetImage } from '@/utility/JellyfinApi';
import styled, { css } from 'styled-components/native';
import { useNavigation } from '@react-navigation/native';
import { useAppDispatch, useTypedSelector } from '@/store';
import { THEME_COLOR } from '@/CONSTANTS';
import TouchableHandler from '@/components/TouchableHandler';
import useCurrentTrack from '@/utility/useCurrentTrack';
import TrackPlayer from 'react-native-track-player';
@@ -32,7 +31,6 @@ const styles = StyleSheet.create({
marginRight: 12
},
activeText: {
color: THEME_COLOR,
fontWeight: '500',
},
});
@@ -153,13 +151,17 @@ const TrackListView: React.FC<TrackListViewProps> = ({
>
<TrackContainer
isPlaying={currentTrack?.backendId === trackId || false}
style={[defaultStyles.border, currentTrack?.backendId === trackId || false ? defaultStyles.activeBackground : null ]}
style={[
defaultStyles.border,
currentTrack?.backendId === trackId ? defaultStyles.activeBackground : null
]}
>
<Text
style={[
styles.index,
{ opacity: 0.25 },
currentTrack?.backendId === trackId && styles.activeText
defaultStyles.textQuarterOpacity,
currentTrack?.backendId === trackId && styles.activeText,
currentTrack?.backendId === trackId && defaultStyles.themeColorQuarterOpacity,
]}
numberOfLines={1}
>
@@ -169,23 +171,29 @@ const TrackListView: React.FC<TrackListViewProps> = ({
</Text>
<View style={{ flexShrink: 1 }}>
<Text
style={{
...currentTrack?.backendId === trackId && styles.activeText,
flexShrink: 1,
marginRight: 4,
}}
style={[
currentTrack?.backendId === trackId && styles.activeText,
currentTrack?.backendId === trackId && defaultStyles.themeColor,
{
flexShrink: 1,
marginRight: 4,
}
]}
numberOfLines={1}
>
{tracks[trackId]?.Name}
</Text>
{itemDisplayStyle === 'playlist' && (
<Text
style={{
...currentTrack?.backendId === trackId && styles.activeText,
flexShrink: 1,
marginRight: 4,
opacity:currentTrack?.backendId === trackId ? 0.5 : 0.25,
}}
style={[
currentTrack?.backendId === trackId && styles.activeText,
currentTrack?.backendId === trackId && defaultStyles.themeColor,
{
flexShrink: 1,
marginRight: 4,
opacity: currentTrack?.backendId === trackId ? 0.5 : 0.25,
}
]}
numberOfLines={1}
>
{tracks[trackId]?.Artists.join(', ')}
@@ -195,19 +203,26 @@ const TrackListView: React.FC<TrackListViewProps> = ({
<View style={{ marginLeft: 'auto', flexDirection: 'row' }}>
<Text
style={[
{ marginRight: 12, opacity: 0.25 },
currentTrack?.backendId === trackId && styles.activeText
{ marginRight: 12 },
defaultStyles.textQuarterOpacity,
currentTrack?.backendId === trackId && styles.activeText,
currentTrack?.backendId === trackId && defaultStyles.themeColorQuarterOpacity,
]}
numberOfLines={1}
>
{ticksToDuration(tracks[trackId]?.RunTimeTicks || 0)}
</Text>
<DownloadIcon trackId={trackId} fill={currentTrack?.backendId === trackId ? `${THEME_COLOR}44` : undefined} />
<DownloadIcon
trackId={trackId}
fill={currentTrack?.backendId === trackId ? defaultStyles.themeColorQuarterOpacity.color : undefined}
/>
</View>
</TrackContainer>
</TouchableHandler>
)}
<Text style={{ paddingTop: 24, paddingBottom: 12, textAlign: 'center', opacity: 0.5 }}>{t('total-duration')}{': '}{ticksToDuration(totalDuration)}</Text>
<Text style={{ paddingTop: 24, paddingBottom: 12, textAlign: 'center', opacity: 0.5 }}>
{t('total-duration')}{': '}{ticksToDuration(totalDuration)}
</Text>
<WrappableButtonRow style={{ marginTop: 24 }}>
<WrappableButton
icon={CloudDownArrow}

View File

@@ -1,6 +1,5 @@
import React, { useState } from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { THEME_COLOR } from '@/CONSTANTS';
import { t } from '@/localisation';
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
import { StackParams } from '@/screens/types';
@@ -20,7 +19,7 @@ function SearchStack() {
<GestureHandlerRootView style={{ flex: 1 }}>
<Stack.Navigator initialRouteName="Search"
screenOptions={{
headerTintColor: THEME_COLOR,
headerTintColor: defaultStyles.themeColor.color,
headerTitleStyle: defaultStyles.stackHeader,
cardStyle: defaultStyles.view,
headerTransparent: true,

View File

@@ -1,6 +1,5 @@
import useDefaultStyles from '@/components/Colors';
import { Text } from '@/components/Typography';
import { THEME_COLOR } from '@/CONSTANTS';
import React from 'react';
import { SvgProps } from 'react-native-svg';
import styled, { css } from 'styled-components/native';
@@ -21,7 +20,6 @@ const Label = styled(Text)<{ active?: boolean }>`
${(props) => props.active && css`
opacity: 1;
font-weight: 500;
color: ${THEME_COLOR};
`}
`;
@@ -46,8 +44,10 @@ function SelectableFilter({
active={active}
onPress={onPress}
>
<Icon width={14} height={14} fill={active ? THEME_COLOR : defaultStyles.textHalfOpacity.color} />
<Label active={active}>{text}</Label>
<Icon width={14} height={14} fill={active ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color} />
<Label active={active} style={active && defaultStyles.themeColor}>
{text}
</Label>
</Container>
);
}

View File

@@ -3,7 +3,6 @@ import styled from 'styled-components/native';
import CheckmarkIcon from '@/assets/icons/checkmark.svg';
import { Text } from '@/components/Typography';
import useDefaultStyles from '@/components/Colors';
import { THEME_COLOR } from '@/CONSTANTS';
import { Gap } from '@/components/Utility';
import { View } from 'react-native';
@@ -52,9 +51,9 @@ export function RadioItem<T>({
}
]}
>
{checked ? <CheckmarkIcon fill={THEME_COLOR} height={14} width={14} /> : <Gap size={14} />}
{checked ? <CheckmarkIcon fill={defaultStyles.themeColor.color} height={14} width={14} /> : <Gap size={14} />}
<Gap size={8} />
<Text style={checked && { color: THEME_COLOR }}>{label}</Text>
<Text style={checked && defaultStyles.themeColor}>{label}</Text>
</RadioItemContainer>
</View>
);

View File

@@ -3,7 +3,6 @@ import { StyleSheet } from 'react-native';
import { t } from '@/localisation';
import { createStackNavigator } from '@react-navigation/stack';
import { useNavigation } from '@react-navigation/native';
import { THEME_COLOR } from '@/CONSTANTS';
import ListButton from '@/components/ListButton';
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
@@ -45,7 +44,7 @@ export default function Settings() {
return (
<Stack.Navigator initialRouteName="SettingList" screenOptions={{
headerTintColor: THEME_COLOR,
headerTintColor: defaultStyles.themeColor.color,
headerTitleStyle: defaultStyles.stackHeader,
headerTransparent: true,
headerBackground: () => <ColoredBlurView style={StyleSheet.absoluteFill} />,

View File

@@ -6,7 +6,6 @@ import styled, { css } from 'styled-components/native';
import { isSentryEnabled, setSentryStatus } from '@/utility/Sentry';
import Accordion from 'react-native-collapsible/Accordion';
import ChevronIcon from '@/assets/icons/chevron-right.svg';
import { THEME_COLOR } from '@/CONSTANTS';
import useDefaultStyles, { DefaultStylesProvider } from '@/components/Colors';
import { t } from '@/localisation';
import { SafeScrollView } from '@/components/SafeNavigatorView';
@@ -29,10 +28,6 @@ const HeaderContainer = styled.View<{ isActive?: boolean }>`
padding: 16px 24px;
border-radius: 8px;
overflow: hidden;
${props => props.isActive && css`
background-color: ${THEME_COLOR};
`}
`;
const HeaderText = styled(Text)`

View File

@@ -3,7 +3,6 @@ import { createBottomTabNavigator, BottomTabNavigationProp } from '@react-naviga
import { StackNavigationProp } from '@react-navigation/stack';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { CompositeNavigationProp } from '@react-navigation/native';
import { THEME_COLOR } from '@/CONSTANTS';
import SearchStack from './Search';
import Music from './Music';
@@ -23,7 +22,7 @@ import ErrorReportingAlert from '@/utility/ErrorReportingAlert';
import ErrorReportingPopup from './modals/ErrorReportingPopup';
import Player from './modals/Player';
import { StyleSheet } from 'react-native';
import { ColoredBlurView } from '@/components/Colors';
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
import { StackParams } from './types';
const Stack = createNativeStackNavigator<StackParams>();
@@ -35,6 +34,7 @@ type Screens = {
}
function Screens() {
const styles = useDefaultStyles();
const isOnboardingComplete = useTypedSelector(state => state.settings.isOnboardingComplete);
// GUARD: If onboarding has not been completed, we instead render the
@@ -62,7 +62,7 @@ function Screens() {
return null;
}
},
tabBarActiveTintColor: THEME_COLOR,
tabBarActiveTintColor: styles.themeColor.color,
tabBarInactiveTintColor: 'gray',
headerShown: false,
tabBarShowLabel: false,

View File

@@ -1,5 +1,4 @@
import { Text } from '@/components/Typography';
import { THEME_COLOR } from '@/CONSTANTS';
import React, { useCallback } from 'react';
import { showRoutePicker, useAirplayRoutes } from 'react-airplay';
import { TouchableOpacity } from 'react-native';
@@ -27,7 +26,6 @@ const Label = styled(Text)<{ active?: boolean }>`
font-size: 13px;
${(props) => props.active && css`
color: ${THEME_COLOR};
opacity: 1;
`}
`;
@@ -43,9 +41,13 @@ function Casting() {
<AirplayAudioIcon
width={20}
height={20}
fill={routes.length > 0 ? THEME_COLOR : defaultStyles.textHalfOpacity.color}
fill={routes.length > 0 ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color}
/>
<Label active={routes.length > 0} numberOfLines={1}>
<Label
active={routes.length > 0}
numberOfLines={1}
style={routes.length > 0 && defaultStyles.themeColor}
>
{routes.length > 0
? `${t('playing-on')} ${routes.map((route) => route.portName).join(', ')}`
: t('local-playback')

View File

@@ -1,6 +1,5 @@
import React from 'react';
import { useNetInfo } from '@react-native-community/netinfo';
import { THEME_COLOR } from '@/CONSTANTS';
import styled from 'styled-components/native';
import CloudSlash from '@/assets/icons/cloud-slash.svg';
import { Text } from 'react-native';
@@ -23,8 +22,8 @@ function ConnectionNotice() {
if (!isInternetReachable) {
return (
<Well style={defaultStyles.activeBackground}>
<CloudSlash width={24} height={24} fill={THEME_COLOR} />
<Text style={{ color: THEME_COLOR, marginLeft: 12 }}>
<CloudSlash width={24} height={24} fill={defaultStyles.themeColor.color} />
<Text style={[ defaultStyles.themeColor, { marginLeft: 12 }]}>
{t('you-are-offline-message')}
</Text>
</Well>

View File

@@ -8,7 +8,6 @@ import ProgressTrack, {
ProgressTrackContainer
} from '@/components/Progresstrack';
import { Gesture, GestureDetector, gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { THEME_COLOR } from '@/CONSTANTS';
import Reanimated, {
useSharedValue,
useAnimatedStyle,
@@ -19,6 +18,7 @@ import Reanimated, {
} from 'react-native-reanimated';
import ReText from '@/components/ReText';
import useCurrentTrack from '@/utility/useCurrentTrack';
import useDefaultStyles from '@/components/Colors';
const DRAG_HANDLE_SIZE = 20;
const PADDING_TOP = 12;
@@ -43,7 +43,6 @@ const DragHandle = styled(Reanimated.View)`
width: ${DRAG_HANDLE_SIZE}px;
height: ${DRAG_HANDLE_SIZE}px;
border-radius: ${DRAG_HANDLE_SIZE}px;
background-color: ${THEME_COLOR};
position: absolute;
left: -${DRAG_HANDLE_SIZE / 2}px;
top: ${PADDING_TOP - DRAG_HANDLE_SIZE / 2 + 2.5}px;
@@ -51,6 +50,7 @@ const DragHandle = styled(Reanimated.View)`
`;
function ProgressBar() {
const styles = useDefaultStyles();
const { position, buffered } = useProgress();
const { track } = useCurrentTrack();
@@ -182,16 +182,28 @@ function ProgressBar() {
<ProgressTrackContainer>
<ProgressTrack
opacity={0.15}
style={styles.themeBackground}
/>
<ProgressTrack
style={bufferStyles}
style={[
styles.themeBackground,
bufferStyles
]}
opacity={0.15}
/>
<ProgressTrack
style={progressStyles}
style={[
progressStyles,
styles.themeBackground,
]}
/>
</ProgressTrackContainer>
<DragHandle style={dragHandleStyles} />
<DragHandle
style={[
styles.themeBackground,
dragHandleStyles,
]}
/>
<NumberBar style={{ flex: 1 }}>
<Number text={timePassed} style={timePassedStyles} />
<Number text={timeRemaining} style={timeRemainingStyles} />

View File

@@ -11,7 +11,6 @@ import { Text } from '@/components/Typography';
import RepeatIcon from '@/assets/icons/repeat.svg';
import RepeatSingleIcon from '@/assets/icons/repeat.1.svg';
import Button from '@/components/Button';
import { THEME_COLOR } from '@/CONSTANTS';
import DownloadIcon from '@/components/DownloadIcon';
import Divider from '@/components/Divider';
import ticksToDuration from '@/utility/ticksToDuration';
@@ -123,7 +122,7 @@ export default function Queue({ header }: Props) {
onPress={toggleTrackLoop}
>
<RepeatSingleIcon
fill={repeatMode === RepeatMode.Track ? THEME_COLOR : defaultStyles.textHalfOpacity.color}
fill={repeatMode === RepeatMode.Track ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color}
width={ICON_SIZE}
height={ICON_SIZE}
/>
@@ -133,7 +132,7 @@ export default function Queue({ header }: Props) {
onPress={toggleQueueLoop}
>
<RepeatIcon
fill={repeatMode === RepeatMode.Queue ? THEME_COLOR : defaultStyles.textHalfOpacity.color}
fill={repeatMode === RepeatMode.Queue ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color}
width={ICON_SIZE}
height={ICON_SIZE}
/>
@@ -154,14 +153,14 @@ export default function Queue({ header }: Props) {
>
<View style={{ flex: 1, marginRight: 16 }}>
<Text
style={[currentIndex === index ? { color: THEME_COLOR, fontWeight: '500' } : styles.trackTitle, { marginBottom: 2 }]}
style={[currentIndex === index ? { color: defaultStyles.themeColor.color, fontWeight: '500' } : styles.trackTitle, { marginBottom: 2 }]}
numberOfLines={1}
>
{track.title}
</Text>
{(track.artist || track.album) && (
<TextHalfOpacity
style={currentIndex === index ? { color: THEME_COLOR, fontWeight: '400' } : undefined}
style={currentIndex === index ? { color: defaultStyles.themeColor.color, fontWeight: '400' } : undefined}
numberOfLines={1}
>
{track.artist}{track.album && ' — ' + track.album}
@@ -170,13 +169,13 @@ export default function Queue({ header }: Props) {
</View>
<View style={{ marginLeft: 'auto', marginRight: 8 }}>
<TextHalfOpacity
style={currentIndex === index ? { color: THEME_COLOR, fontWeight: '400' } : undefined}
style={currentIndex === index ? { color: defaultStyles.themeColor.color, fontWeight: '400' } : undefined}
>
{ticksToDuration(track.duration || 0)}
</TextHalfOpacity>
</View>
<View>
<DownloadIcon trackId={track.backendId} fill={currentIndex === index ? THEME_COLOR + '80' : undefined} />
<DownloadIcon trackId={track.backendId} fill={currentIndex === index ? defaultStyles.themeColor.color + '80' : undefined} />
</View>
</QueueItem>
</TouchableHandler>

View File

@@ -1,7 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import styled from 'styled-components/native';
import { THEME_COLOR } from '@/CONSTANTS';
import { useDispatch } from 'react-redux';
import { useTypedSelector } from '@/store';
import TimerIcon from '@/assets/icons/timer.svg';
@@ -109,7 +108,7 @@ export default function Timer() {
<View>
<TimerIcon
fill={showPicker || date
? THEME_COLOR
? defaultStyles.themeColor.color
: defaultStyles.textHalfOpacity.color
}
width={16}
@@ -117,7 +116,7 @@ export default function Timer() {
/>
<Label
style={{ color: showPicker || date
? THEME_COLOR
? defaultStyles.themeColor.color
: defaultStyles.textHalfOpacity.color
}}
>

View File

@@ -5,7 +5,6 @@ import Input from '@/components/Input';
import { setJellyfinCredentials } from '@/store/settings/actions';
import { useNavigation, StackActions } from '@react-navigation/native';
import CredentialGenerator from './components/CredentialGenerator';
import { THEME_COLOR } from '@/CONSTANTS';
import { t } from '@/localisation';
import useDefaultStyles from '@/components/Colors';
import { Text } from '@/components/Typography';
@@ -55,7 +54,7 @@ export default function SetJellyfinServer() {
title={t('set-jellyfin-server')}
onPress={() => setIsLogginIn(true)}
disabled={!serverUrl?.length}
color={THEME_COLOR}
color={defaultStyles.themeColor.color}
/>
</View>
)}

View File

@@ -45,7 +45,6 @@ const baseTrackOptions: Record<string, string> = {
export function generateTrack(track: AlbumTrack, credentials: Credentials): Track {
// Also construct the URL for the stream
const url = generateTrackUrl(track.Id, credentials);
console.log(url);
return {
url,