@@ -958,6 +958,10 @@ PODS:
|
|||||||
- React-Mapbuffer (0.73.4):
|
- React-Mapbuffer (0.73.4):
|
||||||
- glog
|
- glog
|
||||||
- React-debug
|
- React-debug
|
||||||
|
- react-native-accessibility-settings (0.1.2):
|
||||||
|
- glog
|
||||||
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
|
- React-Core
|
||||||
- react-native-blur (4.4.0):
|
- react-native-blur (4.4.0):
|
||||||
- glog
|
- glog
|
||||||
- RCT-Folly (= 2022.05.16.00)
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
@@ -1246,6 +1250,7 @@ DEPENDENCIES:
|
|||||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
|
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
|
||||||
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
||||||
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
|
- 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-blur (from `../node_modules/@react-native-community/blur`)"
|
||||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
- 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"
|
:path: "../node_modules/react-native/ReactCommon/logger"
|
||||||
React-Mapbuffer:
|
React-Mapbuffer:
|
||||||
:path: "../node_modules/react-native/ReactCommon"
|
:path: "../node_modules/react-native/ReactCommon"
|
||||||
|
react-native-accessibility-settings:
|
||||||
|
:path: "../node_modules/react-native-accessibility-settings"
|
||||||
react-native-blur:
|
react-native-blur:
|
||||||
:path: "../node_modules/@react-native-community/blur"
|
:path: "../node_modules/@react-native-community/blur"
|
||||||
react-native-netinfo:
|
react-native-netinfo:
|
||||||
@@ -1481,6 +1488,7 @@ SPEC CHECKSUMS:
|
|||||||
React-jsinspector: 9ac353eccf6ab54d1e0a33862ba91221d1e88460
|
React-jsinspector: 9ac353eccf6ab54d1e0a33862ba91221d1e88460
|
||||||
React-logger: 0a57b68dd2aec7ff738195f081f0520724b35dab
|
React-logger: 0a57b68dd2aec7ff738195f081f0520724b35dab
|
||||||
React-Mapbuffer: 63913773ed7f96b814a2521e13e6d010282096ad
|
React-Mapbuffer: 63913773ed7f96b814a2521e13e6d010282096ad
|
||||||
|
react-native-accessibility-settings: 9e1c5c6e8268015f8447faa7d34a5834fbaf4d8c
|
||||||
react-native-blur: 27113acc008facbc8accae5fb3a78b8424f64cfd
|
react-native-blur: 27113acc008facbc8accae5fb3a78b8424f64cfd
|
||||||
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
|
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
|
||||||
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
|
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
|
||||||
|
|||||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -28,6 +28,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-airplay": "^1.2.0",
|
"react-airplay": "^1.2.0",
|
||||||
"react-native": "^0.73.4",
|
"react-native": "^0.73.4",
|
||||||
|
"react-native-accessibility-settings": "^0.1.2",
|
||||||
"react-native-collapsible": "^1.6.1",
|
"react-native-collapsible": "^1.6.1",
|
||||||
"react-native-dotenv": "^3.4.9",
|
"react-native-dotenv": "^3.4.9",
|
||||||
"react-native-fast-image": "^8.6.3",
|
"react-native-fast-image": "^8.6.3",
|
||||||
@@ -9452,6 +9453,18 @@
|
|||||||
"react": "18.2.0"
|
"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": {
|
"node_modules/react-native-collapsible": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-airplay": "^1.2.0",
|
"react-airplay": "^1.2.0",
|
||||||
"react-native": "^0.73.4",
|
"react-native": "^0.73.4",
|
||||||
|
"react-native-accessibility-settings": "^0.1.2",
|
||||||
"react-native-collapsible": "^1.6.1",
|
"react-native-collapsible": "^1.6.1",
|
||||||
"react-native-dotenv": "^3.4.9",
|
"react-native-dotenv": "^3.4.9",
|
||||||
"react-native-fast-image": "^8.6.3",
|
"react-native-fast-image": "^8.6.3",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export const ALBUM_CACHE_AMOUNT_OF_DAYS = 7;
|
export const ALBUM_CACHE_AMOUNT_OF_DAYS = 7;
|
||||||
export const PLAYLIST_CACHE_AMOUNT_OF_DAYS = 7;
|
export const PLAYLIST_CACHE_AMOUNT_OF_DAYS = 7;
|
||||||
export const ALPHABET_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ';
|
export const ALPHABET_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ';
|
||||||
export const THEME_COLOR = '#FF3C00';
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import styled from 'styled-components/native';
|
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 { View, LayoutChangeEvent } from 'react-native';
|
||||||
import {
|
import {
|
||||||
PanGestureHandler,
|
PanGestureHandler,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
TapGestureHandler,
|
TapGestureHandler,
|
||||||
TapGestureHandlerGestureEvent
|
TapGestureHandlerGestureEvent
|
||||||
} from 'react-native-gesture-handler';
|
} from 'react-native-gesture-handler';
|
||||||
|
import useDefaultStyles from './Colors';
|
||||||
|
|
||||||
// interface LetterContainerProps {
|
// interface LetterContainerProps {
|
||||||
// onPress: (letter: string) => void;
|
// onPress: (letter: string) => void;
|
||||||
@@ -29,7 +30,6 @@ const Letter = styled.Text`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1px 0;
|
padding: 1px 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: ${THEME_COLOR};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -41,6 +41,7 @@ interface Props {
|
|||||||
* screen with all letters of the Alphabet.
|
* screen with all letters of the Alphabet.
|
||||||
*/
|
*/
|
||||||
const AlphabetScroller: React.FC<Props> = ({ onSelect }) => {
|
const AlphabetScroller: React.FC<Props> = ({ onSelect }) => {
|
||||||
|
const styles = useDefaultStyles();
|
||||||
const [ height, setHeight ] = useState(0);
|
const [ height, setHeight ] = useState(0);
|
||||||
const [ index, setIndex ] = useState<number>();
|
const [ index, setIndex ] = useState<number>();
|
||||||
|
|
||||||
@@ -69,7 +70,9 @@ const AlphabetScroller: React.FC<Props> = ({ onSelect }) => {
|
|||||||
key={l}
|
key={l}
|
||||||
onLayout={i === 0 ? handleLayout : undefined}
|
onLayout={i === 0 ? handleLayout : undefined}
|
||||||
>
|
>
|
||||||
<Letter>{l}</Letter>
|
<Letter style={styles.themeColor}>
|
||||||
|
{l}
|
||||||
|
</Letter>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { SvgProps } from 'react-native-svg';
|
|||||||
import {
|
import {
|
||||||
PressableProps, ViewProps, View,
|
PressableProps, ViewProps, View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import styled, { css } from 'styled-components/native';
|
import styled, { css } from 'styled-components/native';
|
||||||
import useDefaultStyles from './Colors';
|
import useDefaultStyles from './Colors';
|
||||||
|
|
||||||
@@ -35,7 +34,6 @@ const BaseButton = styled.Pressable<{ size: ButtonSize }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ButtonText = styled.Text<{ active?: boolean, size: ButtonSize }>`
|
const ButtonText = styled.Text<{ active?: boolean, size: ButtonSize }>`
|
||||||
color: ${THEME_COLOR};
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
@@ -72,16 +70,17 @@ const Button = React.forwardRef<View, ButtonProps>(function Button(props, ref) {
|
|||||||
<Icon
|
<Icon
|
||||||
width={14}
|
width={14}
|
||||||
height={14}
|
height={14}
|
||||||
fill={THEME_COLOR}
|
fill={defaultStyles.themeColor.color}
|
||||||
style={{
|
style={[
|
||||||
marginRight: title ? 8 : 0,
|
{ marginRight: title ? 8 : 0 }
|
||||||
}}
|
]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{title ? (
|
{title ? (
|
||||||
<ButtonText
|
<ButtonText
|
||||||
active={isPressed}
|
active={isPressed}
|
||||||
size={size}
|
size={size}
|
||||||
|
style={defaultStyles.themeColor}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { BlurView, BlurViewProps } from '@react-native-community/blur';
|
import { BlurView, BlurViewProps } from '@react-native-community/blur';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import React, { PropsWithChildren } from 'react';
|
import React, { PropsWithChildren } from 'react';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { ColorSchemeName, Platform, StyleSheet, View, useColorScheme } from 'react-native';
|
import { ColorSchemeName, Platform, StyleSheet, View, useColorScheme } from 'react-native';
|
||||||
import { useTypedSelector } from '@/store';
|
import { useTypedSelector } from '@/store';
|
||||||
import { ColorScheme } from '@/store/settings/types';
|
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;
|
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
|
* Function for generating both the dark and light stylesheets, so that they
|
||||||
* don't have to be generate on every individual component render
|
* don't have to be generate on every individual component render
|
||||||
*/
|
*/
|
||||||
function generateStyles(scheme: ColorSchemeName) {
|
function generateStyles(scheme: ColorSchemeName, highContrast: boolean) {
|
||||||
return StyleSheet.create({
|
return StyleSheet.create({
|
||||||
text: {
|
text: {
|
||||||
color: scheme === 'dark' ? '#fff' : '#000',
|
color: scheme === 'dark' ? '#fff' : '#000',
|
||||||
@@ -20,12 +20,15 @@ function generateStyles(scheme: ColorSchemeName) {
|
|||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
},
|
},
|
||||||
textHalfOpacity: {
|
textHalfOpacity: {
|
||||||
color: scheme === 'dark' ? '#ffffff88' : '#00000088',
|
color: highContrast
|
||||||
|
? (scheme === 'dark' ? '#ffffffbb' : '#000000bb')
|
||||||
|
: (scheme === 'dark' ? '#ffffff88' : '#00000088'),
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
// fontFamily: 'Inter',
|
|
||||||
},
|
},
|
||||||
textQuarterOpacity: {
|
textQuarterOpacity: {
|
||||||
color: scheme === 'dark' ? '#ffffff44' : '#00000044',
|
color: highContrast
|
||||||
|
? (scheme === 'dark' ? '#ffffff88' : '#00000088')
|
||||||
|
: (scheme === 'dark' ? '#ffffff44' : '#00000044'),
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
@@ -35,7 +38,9 @@ function generateStyles(scheme: ColorSchemeName) {
|
|||||||
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
|
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
|
||||||
},
|
},
|
||||||
activeBackground: {
|
activeBackground: {
|
||||||
backgroundColor: `${THEME_COLOR}${scheme === 'dark' ? '26' : '16'}`,
|
backgroundColor: highContrast
|
||||||
|
? `#8b513c${scheme === 'dark' ? '26' : '10'}`
|
||||||
|
: `#FF3C00${scheme === 'dark' ? '26' : '16'}`,
|
||||||
},
|
},
|
||||||
imageBackground: {
|
imageBackground: {
|
||||||
backgroundColor: scheme === 'dark' ? '#191919' : '#eee',
|
backgroundColor: scheme === 'dark' ? '#191919' : '#eee',
|
||||||
@@ -49,7 +54,9 @@ function generateStyles(scheme: ColorSchemeName) {
|
|||||||
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
|
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
backgroundColor: scheme === 'dark' ? '#ffffff09' : '#00000009',
|
backgroundColor: highContrast
|
||||||
|
? (scheme === 'dark' ? '#ffffff0f' : '#0000000f')
|
||||||
|
: (scheme === 'dark' ? '#ffffff09' : '#00000009'),
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
|
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
|
||||||
@@ -67,13 +74,35 @@ function generateStyles(scheme: ColorSchemeName) {
|
|||||||
filter: {
|
filter: {
|
||||||
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
|
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
|
// Prerender both stylesheets
|
||||||
export const themes: Record<'dark' | 'light', ReturnType<typeof generateStyles>> = {
|
export const themes: Record<'dark' | 'light' | 'dark-highcontrast' | 'light-highcontrast', ReturnType<typeof generateStyles>> = {
|
||||||
'dark': generateStyles('dark'),
|
'dark': generateStyles('dark', false),
|
||||||
'light': generateStyles('light'),
|
'light': generateStyles('light', false),
|
||||||
|
'dark-highcontrast': generateStyles('dark', true),
|
||||||
|
'light-highcontrast': generateStyles('light', true),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create context for supplying the theming information
|
// Create context for supplying the theming information
|
||||||
@@ -84,9 +113,12 @@ export const ColorSchemeContext = React.createContext(themes.dark);
|
|||||||
*/
|
*/
|
||||||
export function ColorSchemeProvider({ children }: PropsWithChildren<{}>) {
|
export function ColorSchemeProvider({ children }: PropsWithChildren<{}>) {
|
||||||
const systemScheme = useColorScheme();
|
const systemScheme = useColorScheme();
|
||||||
|
const highContrast = useAccessibilitySetting('darkerSystemColors');
|
||||||
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
|
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
|
||||||
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
|
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
|
||||||
const theme = themes[scheme || 'light'];
|
const theme = highContrast
|
||||||
|
? themes[`${scheme || 'light'}-highcontrast`]
|
||||||
|
: themes[scheme || 'light'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColorSchemeContext.Provider value={theme}>
|
<ColorSchemeContext.Provider value={theme}>
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ import CloudExclamationMarkIcon from '@/assets/icons/cloud-exclamation-mark.svg'
|
|||||||
import InternalDriveIcon from '@/assets/icons/internal-drive.svg';
|
import InternalDriveIcon from '@/assets/icons/internal-drive.svg';
|
||||||
import useDefaultStyles from './Colors';
|
import useDefaultStyles from './Colors';
|
||||||
import Svg, { Circle, CircleProps } from 'react-native-svg';
|
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';
|
import styled from 'styled-components/native';
|
||||||
|
|
||||||
interface DownloadIconProps {
|
interface DownloadIconProps {
|
||||||
trackId: string;
|
trackId: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
fill?: string;
|
fill?: string;
|
||||||
|
style?: ViewProps['style'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const DownloadContainer = styled.View`
|
const DownloadContainer = styled.View`
|
||||||
@@ -26,7 +27,7 @@ const IconOverlay = styled.View`
|
|||||||
transform: scale(0.5);
|
transform: scale(0.5);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
|
function DownloadIcon({ trackId, size = 16, fill, style }: DownloadIconProps) {
|
||||||
// determine styles
|
// determine styles
|
||||||
const defaultStyles = useDefaultStyles();
|
const defaultStyles = useDefaultStyles();
|
||||||
const iconFill = fill || defaultStyles.textQuarterOpacity.color;
|
const iconFill = fill || defaultStyles.textQuarterOpacity.color;
|
||||||
@@ -66,19 +67,19 @@ function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
|
|||||||
|
|
||||||
if (!entity && !isQueued) {
|
if (!entity && !isQueued) {
|
||||||
return (
|
return (
|
||||||
<CloudIcon width={size} height={size} fill={iconFill} />
|
<CloudIcon width={size} height={size} fill={iconFill} style={style} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity?.isComplete) {
|
if (entity?.isComplete) {
|
||||||
return (
|
return (
|
||||||
<InternalDriveIcon width={size} height={size} fill={iconFill} />
|
<InternalDriveIcon width={size} height={size} fill={iconFill} style={style} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity?.isFailed) {
|
if (entity?.isFailed) {
|
||||||
return (
|
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>
|
</Svg>
|
||||||
<IconOverlay>
|
<IconOverlay>
|
||||||
<CloudDownArrow width={size} height={size} fill={iconFill} />
|
<CloudDownArrow width={size} height={size} fill={iconFill} style={style} />
|
||||||
</IconOverlay>
|
</IconOverlay>
|
||||||
</DownloadContainer>
|
</DownloadContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { useCallback, useState } from 'react';
|
|||||||
import { TouchableOpacityProps } from 'react-native';
|
import { TouchableOpacityProps } from 'react-native';
|
||||||
import ChevronRight from '@/assets/icons/chevron-right.svg';
|
import ChevronRight from '@/assets/icons/chevron-right.svg';
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import useDefaultStyles from './Colors';
|
import useDefaultStyles from './Colors';
|
||||||
|
|
||||||
const BUTTON_SIZE = 14;
|
const BUTTON_SIZE = 14;
|
||||||
@@ -17,7 +16,6 @@ const Container = styled.Pressable<{ active?: boolean }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const Label = styled.Text<{ active?: boolean }>`
|
const Label = styled.Text<{ active?: boolean }>`
|
||||||
color: ${THEME_COLOR};
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -37,8 +35,14 @@ const ListButton: React.FC<TouchableOpacityProps> = ({ children, ...props }) =>
|
|||||||
isPressed ? defaultStyles.activeBackground : undefined
|
isPressed ? defaultStyles.activeBackground : undefined
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Label>{children}</Label>
|
<Label style={defaultStyles.themeColor}>
|
||||||
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={THEME_COLOR} />
|
{children}
|
||||||
|
</Label>
|
||||||
|
<ChevronRight
|
||||||
|
width={BUTTON_SIZE}
|
||||||
|
height={BUTTON_SIZE}
|
||||||
|
fill={defaultStyles.themeColor.color}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import Animated from 'react-native-reanimated';
|
import Animated from 'react-native-reanimated';
|
||||||
|
|
||||||
@@ -51,7 +50,6 @@ const ProgressTrack = styled(Animated.View)<ProgressTrackProps>`
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
|
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
|
||||||
background-color: ${THEME_COLOR};
|
|
||||||
opacity: ${(props) => props.opacity || 1};
|
opacity: ${(props) => props.opacity || 1};
|
||||||
border-radius: 99px;
|
border-radius: 99px;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import FastImage from 'react-native-fast-image';
|
|||||||
import { useGetImage } from '@/utility/JellyfinApi';
|
import { useGetImage } from '@/utility/JellyfinApi';
|
||||||
import { ShadowWrapper } from '@/components/Shadow';
|
import { ShadowWrapper } from '@/components/Shadow';
|
||||||
import { SafeFlatList } from '@/components/SafeNavigatorView';
|
import { SafeFlatList } from '@/components/SafeNavigatorView';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
|
|
||||||
const DownloadedTrack = styled.View`
|
const DownloadedTrack = styled.View`
|
||||||
@@ -165,7 +164,9 @@ function Downloads() {
|
|||||||
</DownloadedTrack>
|
</DownloadedTrack>
|
||||||
{entities[item]?.error && (
|
{entities[item]?.error && (
|
||||||
<ErrorWrapper>
|
<ErrorWrapper>
|
||||||
<Text style={{ color: THEME_COLOR }}>{entities[item]?.error}</Text>
|
<Text style={defaultStyles.themeColor}>
|
||||||
|
{entities[item]?.error}
|
||||||
|
</Text>
|
||||||
</ErrorWrapper>
|
</ErrorWrapper>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import { StyleSheet } from 'react-native';
|
import { 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 { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
||||||
import { StackParams } from '@/screens/types';
|
import { StackParams } from '@/screens/types';
|
||||||
@@ -24,7 +23,7 @@ function MusicStack() {
|
|||||||
return (
|
return (
|
||||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={{
|
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={{
|
||||||
headerTintColor: THEME_COLOR,
|
headerTintColor: defaultStyles.themeColor.color,
|
||||||
headerTitleStyle: defaultStyles.stackHeader,
|
headerTitleStyle: defaultStyles.stackHeader,
|
||||||
cardStyle: defaultStyles.view,
|
cardStyle: defaultStyles.view,
|
||||||
headerTransparent: true,
|
headerTransparent: true,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { Text } from '@/components/Typography';
|
|||||||
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { calculateProgressTranslation } from '@/components/Progresstrack';
|
import { calculateProgressTranslation } from '@/components/Progresstrack';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { NavigationProp } from '@/screens/types';
|
import { NavigationProp } from '@/screens/types';
|
||||||
import { ShadowWrapper } from '@/components/Shadow';
|
import { ShadowWrapper } from '@/components/Shadow';
|
||||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
||||||
@@ -48,7 +47,6 @@ const ProgressTrack = styled(Animated.View)<{ stroke?: number; opacity?: number}
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
|
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
|
||||||
background-color: ${THEME_COLOR};
|
|
||||||
opacity: ${(props) => props.opacity || 1};
|
opacity: ${(props) => props.opacity || 1};
|
||||||
border-radius: 99px;
|
border-radius: 99px;
|
||||||
`;
|
`;
|
||||||
@@ -191,12 +189,18 @@ function NowPlaying({ offset = 0 }: { offset?: number }) {
|
|||||||
<SelectActionButton />
|
<SelectActionButton />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ProgressTrack
|
<ProgressTrack
|
||||||
style={{ transform: [{ translateX: bufferAnimation.current }]}}
|
style={[
|
||||||
|
{ transform: [{ translateX: bufferAnimation.current }]},
|
||||||
|
defaultStyles.themeBackground,
|
||||||
|
]}
|
||||||
opacity={0.15}
|
opacity={0.15}
|
||||||
stroke={4}
|
stroke={4}
|
||||||
/>
|
/>
|
||||||
<ProgressTrack
|
<ProgressTrack
|
||||||
style={{ transform: [{ translateX: progressAnimation.current }]}}
|
style={[
|
||||||
|
{ transform: [{ translateX: progressAnimation.current }]},
|
||||||
|
defaultStyles.themeBackground,
|
||||||
|
]}
|
||||||
stroke={4}
|
stroke={4}
|
||||||
/>
|
/>
|
||||||
</InnerContainer>
|
</InnerContainer>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useNavigation } from '@react-navigation/native';
|
|||||||
import { differenceInDays } from 'date-fns';
|
import { differenceInDays } from 'date-fns';
|
||||||
import { useAppDispatch, useTypedSelector } from '@/store';
|
import { useAppDispatch, useTypedSelector } from '@/store';
|
||||||
import { fetchAllAlbums } from '@/store/music/actions';
|
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 AlbumImage from './components/AlbumImage';
|
||||||
import { SectionArtistItem, SectionedArtist, selectArtists } from '@/store/music/selectors';
|
import { SectionArtistItem, SectionedArtist, selectArtists } from '@/store/music/selectors';
|
||||||
import AlphabetScroller from '@/components/AlphabetScroller';
|
import AlphabetScroller from '@/components/AlphabetScroller';
|
||||||
@@ -92,7 +92,7 @@ const GeneratedArtistItem = React.memo(function GeneratedArtistItem(props: Gener
|
|||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[
|
style={[
|
||||||
defaultStyles.text,
|
defaultStyles.text,
|
||||||
pressed && { color: THEME_COLOR },
|
pressed && defaultStyles.themeColor,
|
||||||
{ flexShrink: 1 }
|
{ flexShrink: 1 }
|
||||||
]}
|
]}
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ const Artists: React.FC = () => {
|
|||||||
onRefresh={retrieveData}
|
onRefresh={retrieveData}
|
||||||
getItemLayout={(_, i) => {
|
getItemLayout={(_, i) => {
|
||||||
if (!(i in itemLayouts)) {
|
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 };
|
return itemLayouts[i] ?? { length: 0, offset: 0, index: i };
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useGetImage } from '@/utility/JellyfinApi';
|
|||||||
import styled, { css } from 'styled-components/native';
|
import styled, { css } from 'styled-components/native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { useAppDispatch, useTypedSelector } from '@/store';
|
import { useAppDispatch, useTypedSelector } from '@/store';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import TouchableHandler from '@/components/TouchableHandler';
|
import TouchableHandler from '@/components/TouchableHandler';
|
||||||
import useCurrentTrack from '@/utility/useCurrentTrack';
|
import useCurrentTrack from '@/utility/useCurrentTrack';
|
||||||
import TrackPlayer from 'react-native-track-player';
|
import TrackPlayer from 'react-native-track-player';
|
||||||
@@ -32,7 +31,6 @@ const styles = StyleSheet.create({
|
|||||||
marginRight: 12
|
marginRight: 12
|
||||||
},
|
},
|
||||||
activeText: {
|
activeText: {
|
||||||
color: THEME_COLOR,
|
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -153,13 +151,17 @@ const TrackListView: React.FC<TrackListViewProps> = ({
|
|||||||
>
|
>
|
||||||
<TrackContainer
|
<TrackContainer
|
||||||
isPlaying={currentTrack?.backendId === trackId || false}
|
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
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.index,
|
styles.index,
|
||||||
{ opacity: 0.25 },
|
defaultStyles.textQuarterOpacity,
|
||||||
currentTrack?.backendId === trackId && styles.activeText
|
currentTrack?.backendId === trackId && styles.activeText,
|
||||||
|
currentTrack?.backendId === trackId && defaultStyles.themeColorQuarterOpacity,
|
||||||
]}
|
]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
@@ -169,23 +171,29 @@ const TrackListView: React.FC<TrackListViewProps> = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<View style={{ flexShrink: 1 }}>
|
<View style={{ flexShrink: 1 }}>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={[
|
||||||
...currentTrack?.backendId === trackId && styles.activeText,
|
currentTrack?.backendId === trackId && styles.activeText,
|
||||||
flexShrink: 1,
|
currentTrack?.backendId === trackId && defaultStyles.themeColor,
|
||||||
marginRight: 4,
|
{
|
||||||
}}
|
flexShrink: 1,
|
||||||
|
marginRight: 4,
|
||||||
|
}
|
||||||
|
]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{tracks[trackId]?.Name}
|
{tracks[trackId]?.Name}
|
||||||
</Text>
|
</Text>
|
||||||
{itemDisplayStyle === 'playlist' && (
|
{itemDisplayStyle === 'playlist' && (
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={[
|
||||||
...currentTrack?.backendId === trackId && styles.activeText,
|
currentTrack?.backendId === trackId && styles.activeText,
|
||||||
flexShrink: 1,
|
currentTrack?.backendId === trackId && defaultStyles.themeColor,
|
||||||
marginRight: 4,
|
{
|
||||||
opacity:currentTrack?.backendId === trackId ? 0.5 : 0.25,
|
flexShrink: 1,
|
||||||
}}
|
marginRight: 4,
|
||||||
|
opacity: currentTrack?.backendId === trackId ? 0.5 : 0.25,
|
||||||
|
}
|
||||||
|
]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{tracks[trackId]?.Artists.join(', ')}
|
{tracks[trackId]?.Artists.join(', ')}
|
||||||
@@ -195,19 +203,26 @@ const TrackListView: React.FC<TrackListViewProps> = ({
|
|||||||
<View style={{ marginLeft: 'auto', flexDirection: 'row' }}>
|
<View style={{ marginLeft: 'auto', flexDirection: 'row' }}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
{ marginRight: 12, opacity: 0.25 },
|
{ marginRight: 12 },
|
||||||
currentTrack?.backendId === trackId && styles.activeText
|
defaultStyles.textQuarterOpacity,
|
||||||
|
currentTrack?.backendId === trackId && styles.activeText,
|
||||||
|
currentTrack?.backendId === trackId && defaultStyles.themeColorQuarterOpacity,
|
||||||
]}
|
]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{ticksToDuration(tracks[trackId]?.RunTimeTicks || 0)}
|
{ticksToDuration(tracks[trackId]?.RunTimeTicks || 0)}
|
||||||
</Text>
|
</Text>
|
||||||
<DownloadIcon trackId={trackId} fill={currentTrack?.backendId === trackId ? `${THEME_COLOR}44` : undefined} />
|
<DownloadIcon
|
||||||
|
trackId={trackId}
|
||||||
|
fill={currentTrack?.backendId === trackId ? defaultStyles.themeColorQuarterOpacity.color : undefined}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</TrackContainer>
|
</TrackContainer>
|
||||||
</TouchableHandler>
|
</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 }}>
|
<WrappableButtonRow style={{ marginTop: 24 }}>
|
||||||
<WrappableButton
|
<WrappableButton
|
||||||
icon={CloudDownArrow}
|
icon={CloudDownArrow}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { createStackNavigator } from '@react-navigation/stack';
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
||||||
import { StackParams } from '@/screens/types';
|
import { StackParams } from '@/screens/types';
|
||||||
@@ -20,7 +19,7 @@ function SearchStack() {
|
|||||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
<Stack.Navigator initialRouteName="Search"
|
<Stack.Navigator initialRouteName="Search"
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
headerTintColor: THEME_COLOR,
|
headerTintColor: defaultStyles.themeColor.color,
|
||||||
headerTitleStyle: defaultStyles.stackHeader,
|
headerTitleStyle: defaultStyles.stackHeader,
|
||||||
cardStyle: defaultStyles.view,
|
cardStyle: defaultStyles.view,
|
||||||
headerTransparent: true,
|
headerTransparent: true,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import useDefaultStyles from '@/components/Colors';
|
import useDefaultStyles from '@/components/Colors';
|
||||||
import { Text } from '@/components/Typography';
|
import { Text } from '@/components/Typography';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SvgProps } from 'react-native-svg';
|
import { SvgProps } from 'react-native-svg';
|
||||||
import styled, { css } from 'styled-components/native';
|
import styled, { css } from 'styled-components/native';
|
||||||
@@ -21,7 +20,6 @@ const Label = styled(Text)<{ active?: boolean }>`
|
|||||||
${(props) => props.active && css`
|
${(props) => props.active && css`
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: ${THEME_COLOR};
|
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -46,8 +44,10 @@ function SelectableFilter({
|
|||||||
active={active}
|
active={active}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
<Icon width={14} height={14} fill={active ? THEME_COLOR : defaultStyles.textHalfOpacity.color} />
|
<Icon width={14} height={14} fill={active ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color} />
|
||||||
<Label active={active}>{text}</Label>
|
<Label active={active} style={active && defaultStyles.themeColor}>
|
||||||
|
{text}
|
||||||
|
</Label>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import styled from 'styled-components/native';
|
|||||||
import CheckmarkIcon from '@/assets/icons/checkmark.svg';
|
import CheckmarkIcon from '@/assets/icons/checkmark.svg';
|
||||||
import { Text } from '@/components/Typography';
|
import { Text } from '@/components/Typography';
|
||||||
import useDefaultStyles from '@/components/Colors';
|
import useDefaultStyles from '@/components/Colors';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { Gap } from '@/components/Utility';
|
import { Gap } from '@/components/Utility';
|
||||||
import { View } from 'react-native';
|
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} />
|
<Gap size={8} />
|
||||||
<Text style={checked && { color: THEME_COLOR }}>{label}</Text>
|
<Text style={checked && defaultStyles.themeColor}>{label}</Text>
|
||||||
</RadioItemContainer>
|
</RadioItemContainer>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { StyleSheet } from 'react-native';
|
|||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import { createStackNavigator } from '@react-navigation/stack';
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import ListButton from '@/components/ListButton';
|
import ListButton from '@/components/ListButton';
|
||||||
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
||||||
|
|
||||||
@@ -45,7 +44,7 @@ export default function Settings() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack.Navigator initialRouteName="SettingList" screenOptions={{
|
<Stack.Navigator initialRouteName="SettingList" screenOptions={{
|
||||||
headerTintColor: THEME_COLOR,
|
headerTintColor: defaultStyles.themeColor.color,
|
||||||
headerTitleStyle: defaultStyles.stackHeader,
|
headerTitleStyle: defaultStyles.stackHeader,
|
||||||
headerTransparent: true,
|
headerTransparent: true,
|
||||||
headerBackground: () => <ColoredBlurView style={StyleSheet.absoluteFill} />,
|
headerBackground: () => <ColoredBlurView style={StyleSheet.absoluteFill} />,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import styled, { css } from 'styled-components/native';
|
|||||||
import { isSentryEnabled, setSentryStatus } from '@/utility/Sentry';
|
import { isSentryEnabled, setSentryStatus } from '@/utility/Sentry';
|
||||||
import Accordion from 'react-native-collapsible/Accordion';
|
import Accordion from 'react-native-collapsible/Accordion';
|
||||||
import ChevronIcon from '@/assets/icons/chevron-right.svg';
|
import ChevronIcon from '@/assets/icons/chevron-right.svg';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import useDefaultStyles, { DefaultStylesProvider } from '@/components/Colors';
|
import useDefaultStyles, { DefaultStylesProvider } from '@/components/Colors';
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import { SafeScrollView } from '@/components/SafeNavigatorView';
|
import { SafeScrollView } from '@/components/SafeNavigatorView';
|
||||||
@@ -29,10 +28,6 @@ const HeaderContainer = styled.View<{ isActive?: boolean }>`
|
|||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
${props => props.isActive && css`
|
|
||||||
background-color: ${THEME_COLOR};
|
|
||||||
`}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const HeaderText = styled(Text)`
|
const HeaderText = styled(Text)`
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { createBottomTabNavigator, BottomTabNavigationProp } from '@react-naviga
|
|||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||||
import { CompositeNavigationProp } from '@react-navigation/native';
|
import { CompositeNavigationProp } from '@react-navigation/native';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
|
|
||||||
import SearchStack from './Search';
|
import SearchStack from './Search';
|
||||||
import Music from './Music';
|
import Music from './Music';
|
||||||
@@ -23,7 +22,7 @@ import ErrorReportingAlert from '@/utility/ErrorReportingAlert';
|
|||||||
import ErrorReportingPopup from './modals/ErrorReportingPopup';
|
import ErrorReportingPopup from './modals/ErrorReportingPopup';
|
||||||
import Player from './modals/Player';
|
import Player from './modals/Player';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
||||||
import { StackParams } from './types';
|
import { StackParams } from './types';
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<StackParams>();
|
const Stack = createNativeStackNavigator<StackParams>();
|
||||||
@@ -35,6 +34,7 @@ type Screens = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Screens() {
|
function Screens() {
|
||||||
|
const styles = useDefaultStyles();
|
||||||
const isOnboardingComplete = useTypedSelector(state => state.settings.isOnboardingComplete);
|
const isOnboardingComplete = useTypedSelector(state => state.settings.isOnboardingComplete);
|
||||||
|
|
||||||
// GUARD: If onboarding has not been completed, we instead render the
|
// GUARD: If onboarding has not been completed, we instead render the
|
||||||
@@ -62,7 +62,7 @@ function Screens() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tabBarActiveTintColor: THEME_COLOR,
|
tabBarActiveTintColor: styles.themeColor.color,
|
||||||
tabBarInactiveTintColor: 'gray',
|
tabBarInactiveTintColor: 'gray',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarShowLabel: false,
|
tabBarShowLabel: false,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Text } from '@/components/Typography';
|
import { Text } from '@/components/Typography';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { showRoutePicker, useAirplayRoutes } from 'react-airplay';
|
import { showRoutePicker, useAirplayRoutes } from 'react-airplay';
|
||||||
import { TouchableOpacity } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
@@ -27,7 +26,6 @@ const Label = styled(Text)<{ active?: boolean }>`
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
${(props) => props.active && css`
|
${(props) => props.active && css`
|
||||||
color: ${THEME_COLOR};
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@@ -43,9 +41,13 @@ function Casting() {
|
|||||||
<AirplayAudioIcon
|
<AirplayAudioIcon
|
||||||
width={20}
|
width={20}
|
||||||
height={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
|
{routes.length > 0
|
||||||
? `${t('playing-on')} ${routes.map((route) => route.portName).join(', ')}`
|
? `${t('playing-on')} ${routes.map((route) => route.portName).join(', ')}`
|
||||||
: t('local-playback')
|
: t('local-playback')
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useNetInfo } from '@react-native-community/netinfo';
|
import { useNetInfo } from '@react-native-community/netinfo';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import CloudSlash from '@/assets/icons/cloud-slash.svg';
|
import CloudSlash from '@/assets/icons/cloud-slash.svg';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
@@ -23,8 +22,8 @@ function ConnectionNotice() {
|
|||||||
if (!isInternetReachable) {
|
if (!isInternetReachable) {
|
||||||
return (
|
return (
|
||||||
<Well style={defaultStyles.activeBackground}>
|
<Well style={defaultStyles.activeBackground}>
|
||||||
<CloudSlash width={24} height={24} fill={THEME_COLOR} />
|
<CloudSlash width={24} height={24} fill={defaultStyles.themeColor.color} />
|
||||||
<Text style={{ color: THEME_COLOR, marginLeft: 12 }}>
|
<Text style={[ defaultStyles.themeColor, { marginLeft: 12 }]}>
|
||||||
{t('you-are-offline-message')}
|
{t('you-are-offline-message')}
|
||||||
</Text>
|
</Text>
|
||||||
</Well>
|
</Well>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ProgressTrack, {
|
|||||||
ProgressTrackContainer
|
ProgressTrackContainer
|
||||||
} from '@/components/Progresstrack';
|
} from '@/components/Progresstrack';
|
||||||
import { Gesture, GestureDetector, gestureHandlerRootHOC } from 'react-native-gesture-handler';
|
import { Gesture, GestureDetector, gestureHandlerRootHOC } from 'react-native-gesture-handler';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import Reanimated, {
|
import Reanimated, {
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
@@ -19,6 +18,7 @@ import Reanimated, {
|
|||||||
} from 'react-native-reanimated';
|
} from 'react-native-reanimated';
|
||||||
import ReText from '@/components/ReText';
|
import ReText from '@/components/ReText';
|
||||||
import useCurrentTrack from '@/utility/useCurrentTrack';
|
import useCurrentTrack from '@/utility/useCurrentTrack';
|
||||||
|
import useDefaultStyles from '@/components/Colors';
|
||||||
|
|
||||||
const DRAG_HANDLE_SIZE = 20;
|
const DRAG_HANDLE_SIZE = 20;
|
||||||
const PADDING_TOP = 12;
|
const PADDING_TOP = 12;
|
||||||
@@ -43,7 +43,6 @@ const DragHandle = styled(Reanimated.View)`
|
|||||||
width: ${DRAG_HANDLE_SIZE}px;
|
width: ${DRAG_HANDLE_SIZE}px;
|
||||||
height: ${DRAG_HANDLE_SIZE}px;
|
height: ${DRAG_HANDLE_SIZE}px;
|
||||||
border-radius: ${DRAG_HANDLE_SIZE}px;
|
border-radius: ${DRAG_HANDLE_SIZE}px;
|
||||||
background-color: ${THEME_COLOR};
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -${DRAG_HANDLE_SIZE / 2}px;
|
left: -${DRAG_HANDLE_SIZE / 2}px;
|
||||||
top: ${PADDING_TOP - DRAG_HANDLE_SIZE / 2 + 2.5}px;
|
top: ${PADDING_TOP - DRAG_HANDLE_SIZE / 2 + 2.5}px;
|
||||||
@@ -51,6 +50,7 @@ const DragHandle = styled(Reanimated.View)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
function ProgressBar() {
|
function ProgressBar() {
|
||||||
|
const styles = useDefaultStyles();
|
||||||
const { position, buffered } = useProgress();
|
const { position, buffered } = useProgress();
|
||||||
const { track } = useCurrentTrack();
|
const { track } = useCurrentTrack();
|
||||||
|
|
||||||
@@ -182,16 +182,28 @@ function ProgressBar() {
|
|||||||
<ProgressTrackContainer>
|
<ProgressTrackContainer>
|
||||||
<ProgressTrack
|
<ProgressTrack
|
||||||
opacity={0.15}
|
opacity={0.15}
|
||||||
|
style={styles.themeBackground}
|
||||||
/>
|
/>
|
||||||
<ProgressTrack
|
<ProgressTrack
|
||||||
style={bufferStyles}
|
style={[
|
||||||
|
styles.themeBackground,
|
||||||
|
bufferStyles
|
||||||
|
]}
|
||||||
opacity={0.15}
|
opacity={0.15}
|
||||||
/>
|
/>
|
||||||
<ProgressTrack
|
<ProgressTrack
|
||||||
style={progressStyles}
|
style={[
|
||||||
|
progressStyles,
|
||||||
|
styles.themeBackground,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</ProgressTrackContainer>
|
</ProgressTrackContainer>
|
||||||
<DragHandle style={dragHandleStyles} />
|
<DragHandle
|
||||||
|
style={[
|
||||||
|
styles.themeBackground,
|
||||||
|
dragHandleStyles,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<NumberBar style={{ flex: 1 }}>
|
<NumberBar style={{ flex: 1 }}>
|
||||||
<Number text={timePassed} style={timePassedStyles} />
|
<Number text={timePassed} style={timePassedStyles} />
|
||||||
<Number text={timeRemaining} style={timeRemainingStyles} />
|
<Number text={timeRemaining} style={timeRemainingStyles} />
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { Text } from '@/components/Typography';
|
|||||||
import RepeatIcon from '@/assets/icons/repeat.svg';
|
import RepeatIcon from '@/assets/icons/repeat.svg';
|
||||||
import RepeatSingleIcon from '@/assets/icons/repeat.1.svg';
|
import RepeatSingleIcon from '@/assets/icons/repeat.1.svg';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import DownloadIcon from '@/components/DownloadIcon';
|
import DownloadIcon from '@/components/DownloadIcon';
|
||||||
import Divider from '@/components/Divider';
|
import Divider from '@/components/Divider';
|
||||||
import ticksToDuration from '@/utility/ticksToDuration';
|
import ticksToDuration from '@/utility/ticksToDuration';
|
||||||
@@ -123,7 +122,7 @@ export default function Queue({ header }: Props) {
|
|||||||
onPress={toggleTrackLoop}
|
onPress={toggleTrackLoop}
|
||||||
>
|
>
|
||||||
<RepeatSingleIcon
|
<RepeatSingleIcon
|
||||||
fill={repeatMode === RepeatMode.Track ? THEME_COLOR : defaultStyles.textHalfOpacity.color}
|
fill={repeatMode === RepeatMode.Track ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color}
|
||||||
width={ICON_SIZE}
|
width={ICON_SIZE}
|
||||||
height={ICON_SIZE}
|
height={ICON_SIZE}
|
||||||
/>
|
/>
|
||||||
@@ -133,7 +132,7 @@ export default function Queue({ header }: Props) {
|
|||||||
onPress={toggleQueueLoop}
|
onPress={toggleQueueLoop}
|
||||||
>
|
>
|
||||||
<RepeatIcon
|
<RepeatIcon
|
||||||
fill={repeatMode === RepeatMode.Queue ? THEME_COLOR : defaultStyles.textHalfOpacity.color}
|
fill={repeatMode === RepeatMode.Queue ? defaultStyles.themeColor.color : defaultStyles.textHalfOpacity.color}
|
||||||
width={ICON_SIZE}
|
width={ICON_SIZE}
|
||||||
height={ICON_SIZE}
|
height={ICON_SIZE}
|
||||||
/>
|
/>
|
||||||
@@ -154,14 +153,14 @@ export default function Queue({ header }: Props) {
|
|||||||
>
|
>
|
||||||
<View style={{ flex: 1, marginRight: 16 }}>
|
<View style={{ flex: 1, marginRight: 16 }}>
|
||||||
<Text
|
<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}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{track.title}
|
{track.title}
|
||||||
</Text>
|
</Text>
|
||||||
{(track.artist || track.album) && (
|
{(track.artist || track.album) && (
|
||||||
<TextHalfOpacity
|
<TextHalfOpacity
|
||||||
style={currentIndex === index ? { color: THEME_COLOR, fontWeight: '400' } : undefined}
|
style={currentIndex === index ? { color: defaultStyles.themeColor.color, fontWeight: '400' } : undefined}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{track.artist}{track.album && ' — ' + track.album}
|
{track.artist}{track.album && ' — ' + track.album}
|
||||||
@@ -170,13 +169,13 @@ export default function Queue({ header }: Props) {
|
|||||||
</View>
|
</View>
|
||||||
<View style={{ marginLeft: 'auto', marginRight: 8 }}>
|
<View style={{ marginLeft: 'auto', marginRight: 8 }}>
|
||||||
<TextHalfOpacity
|
<TextHalfOpacity
|
||||||
style={currentIndex === index ? { color: THEME_COLOR, fontWeight: '400' } : undefined}
|
style={currentIndex === index ? { color: defaultStyles.themeColor.color, fontWeight: '400' } : undefined}
|
||||||
>
|
>
|
||||||
{ticksToDuration(track.duration || 0)}
|
{ticksToDuration(track.duration || 0)}
|
||||||
</TextHalfOpacity>
|
</TextHalfOpacity>
|
||||||
</View>
|
</View>
|
||||||
<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>
|
</View>
|
||||||
</QueueItem>
|
</QueueItem>
|
||||||
</TouchableHandler>
|
</TouchableHandler>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import DateTimePickerModal from 'react-native-modal-datetime-picker';
|
import DateTimePickerModal from 'react-native-modal-datetime-picker';
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useTypedSelector } from '@/store';
|
import { useTypedSelector } from '@/store';
|
||||||
import TimerIcon from '@/assets/icons/timer.svg';
|
import TimerIcon from '@/assets/icons/timer.svg';
|
||||||
@@ -109,7 +108,7 @@ export default function Timer() {
|
|||||||
<View>
|
<View>
|
||||||
<TimerIcon
|
<TimerIcon
|
||||||
fill={showPicker || date
|
fill={showPicker || date
|
||||||
? THEME_COLOR
|
? defaultStyles.themeColor.color
|
||||||
: defaultStyles.textHalfOpacity.color
|
: defaultStyles.textHalfOpacity.color
|
||||||
}
|
}
|
||||||
width={16}
|
width={16}
|
||||||
@@ -117,7 +116,7 @@ export default function Timer() {
|
|||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
style={{ color: showPicker || date
|
style={{ color: showPicker || date
|
||||||
? THEME_COLOR
|
? defaultStyles.themeColor.color
|
||||||
: defaultStyles.textHalfOpacity.color
|
: defaultStyles.textHalfOpacity.color
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Input from '@/components/Input';
|
|||||||
import { setJellyfinCredentials } from '@/store/settings/actions';
|
import { setJellyfinCredentials } from '@/store/settings/actions';
|
||||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||||
import CredentialGenerator from './components/CredentialGenerator';
|
import CredentialGenerator from './components/CredentialGenerator';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
|
||||||
import { t } from '@/localisation';
|
import { t } from '@/localisation';
|
||||||
import useDefaultStyles from '@/components/Colors';
|
import useDefaultStyles from '@/components/Colors';
|
||||||
import { Text } from '@/components/Typography';
|
import { Text } from '@/components/Typography';
|
||||||
@@ -55,7 +54,7 @@ export default function SetJellyfinServer() {
|
|||||||
title={t('set-jellyfin-server')}
|
title={t('set-jellyfin-server')}
|
||||||
onPress={() => setIsLogginIn(true)}
|
onPress={() => setIsLogginIn(true)}
|
||||||
disabled={!serverUrl?.length}
|
disabled={!serverUrl?.length}
|
||||||
color={THEME_COLOR}
|
color={defaultStyles.themeColor.color}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ const baseTrackOptions: Record<string, string> = {
|
|||||||
export function generateTrack(track: AlbumTrack, credentials: Credentials): Track {
|
export function generateTrack(track: AlbumTrack, credentials: Credentials): Track {
|
||||||
// Also construct the URL for the stream
|
// Also construct the URL for the stream
|
||||||
const url = generateTrackUrl(track.Id, credentials);
|
const url = generateTrackUrl(track.Id, credentials);
|
||||||
console.log(url);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url,
|
url,
|
||||||
|
|||||||
Reference in New Issue
Block a user