Files
jellyfin-audio-player/src/components/Colors.tsx
T
3d0f7ff9ef feat: Search upgrade (#285)
* Upgraded search page

- Removed artificial combinding of Fuse and normal fetch results.
- Jellyfin API results are now properly integrated into Fuse.
- Fuse results are now given better ranking weights.
- Added support for artist searching.
- Added support for playlist searching.
- Added support for instant mixes when a track is selected from search.
- Made artist page to look and feel like the albums.
- Added support for artist overview display.

Fixed searching

* Removed separate album retrieving from search

* Utilized fix version of react-native-reanimated to solve build issues

Code cleanup

Fixed package-lock

* Fixed linting

* Removed debug code and fixed linting errors

* [Search Upgrade] Made tracks to instant play from search
- InstantMix is no longer treated as a regular playlist.
- When a music track is selected from the search results - it will play a mix based on the song.
- Refactored Artist page to have full-width artist image and gradient on the overview text.
- Made overview fetching to be part of the original artist result to skip additional requests.
- Made navigating back to work for all supported items in the search results.

* Fixed variable names

* feat: filters on search page and saved search queries

* fix: artists view

* fix: dont dispatch instantmix call

* fix: update search parameters to include AlbumArtist

* fix: dont query for artists in api yet

* fix: remove any

* fix: add missing translations for "artist" in multiple languages

* fix: lint

---------

Co-authored-by: Nikolay Babanov <nikolay.babanov@chaos.com>
Co-authored-by: Lei Nelissen <lei@codified.nl>
2026-01-24 12:42:28 +01:00

190 lines
6.6 KiB
TypeScript

import { BlurView, BlurViewProps } from '@react-native-community/blur';
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;
/**
* 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, highContrast: boolean) {
return StyleSheet.create({
text: {
color: scheme === 'dark' ? '#fff' : '#000',
fontSize: 14,
fontFamily: 'Inter',
},
textSmall: {
color: scheme === 'dark' ? '#fff' : '#000',
fontSize: 12,
lineHeight: 20,
opacity: 0.5,
},
textHalfOpacity: {
color: highContrast
? (scheme === 'dark' ? '#ffffffbb' : '#000000bb')
: (scheme === 'dark' ? '#ffffff88' : '#00000088'),
fontSize: 14,
},
textQuarterOpacity: {
color: highContrast
? (scheme === 'dark' ? '#ffffff88' : '#00000088')
: (scheme === 'dark' ? '#ffffff44' : '#00000044'),
fontSize: 14,
},
view: {
backgroundColor: scheme === 'dark' ? '#111' : '#fff',
},
border: {
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
},
activeBackground: {
backgroundColor: highContrast
? `#8b513c${scheme === 'dark' ? '26' : '10'}`
: `#FF3C00${scheme === 'dark' ? '26' : '16'}`,
},
imageBackground: {
backgroundColor: scheme === 'dark' ? '#191919' : '#eee',
borderWidth: 0.5,
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
},
modal: {
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
},
modalInner: {
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
},
button: {
backgroundColor: highContrast
? (scheme === 'dark' ? '#ffffff0f' : '#0000000f')
: (scheme === 'dark' ? '#ffffff09' : '#00000009'),
},
input: {
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
color: scheme === 'dark' ? '#fff' : '#000',
},
trackBackground: {
backgroundColor: scheme === 'dark' ? '#161616' : '#fff',
},
stackHeader: {
color: scheme === 'dark' ? 'white' : 'black'
},
icon: {
color: scheme === 'dark' ? '#ffffff4d' : '#0000004d',
},
divider: {
backgroundColor: scheme === 'dark' ? '#333' : '#eee',
},
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' | '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
export const ColorSchemeContext = React.createContext(themes.dark);
/**
* This hook returns the proper color scheme, taking into account potential user overrides.
*/
export function useUserOrSystemScheme() {
const systemScheme = useColorScheme();
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
return userScheme === ColorScheme.System ? systemScheme : userScheme;
}
/**
* This provider contains the logic for settings the right theme on the ColorSchemeContext.
*/
export function ColorSchemeProvider({ children }: PropsWithChildren<{}>) {
const highContrast = useAccessibilitySetting('darkerSystemColors');
const scheme = useUserOrSystemScheme();
const theme = highContrast
? themes[`${scheme || 'light'}-highcontrast`]
: themes[scheme || 'light'];
return (
<ColorSchemeContext.Provider value={theme}>
{children}
</ColorSchemeContext.Provider>
);
}
/**
* Retrieves the default styles object in hook form
*/
export default function useDefaultStyles() {
return useContext(ColorSchemeContext);
}
interface DefaultStylesProviderProps {
children: (defaultStyles: ReturnType<typeof useDefaultStyles>) => React.JSX.Element;
}
/**
* A render props component to supply the defaultStyles object.
*/
export function DefaultStylesProvider(props: DefaultStylesProviderProps) {
const defaultStyles = useDefaultStyles();
return props.children(defaultStyles);
}
export function ColoredBlurView({ children, style, ...props }: PropsWithChildren<BlurViewProps>) {
const systemScheme = useColorScheme();
const userScheme = useTypedSelector((state) => state.settings.colorScheme);
const scheme = userScheme === ColorScheme.System ? systemScheme : userScheme;
return Platform.OS === 'ios' ? (
<View style={[style, { overflow: 'hidden' }]}>
<BlurView
style={[ StyleSheet.absoluteFill, { overflow: 'hidden'} ]}
{...props}
blurType={Platform.OS === 'ios' && majorPlatformVersion >= 13
? scheme === 'dark' ? 'materialDark' : 'materialLight'
: scheme === 'dark' ? 'extraDark' : 'xlight'
}
/>
{children}
</View>
) : (
<View {...props} style={[ style, {
backgroundColor: scheme === 'light' ? '#f6f6f6fb' : '#333333fb',
} ]}>
{children}
</View>
);
}