From d7402bb40919ea0b15b833cb822ea50f0d54dbc7 Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Wed, 11 May 2022 23:57:30 +0200 Subject: [PATCH] First overhaul of search screen --- src/components/App.tsx | 22 +++++++++- src/components/Colors.tsx | 6 +-- src/components/Input.tsx | 1 + src/components/ListButton.tsx | 27 +++++------- src/screens/Downloads/index.tsx | 3 +- src/screens/Music/stacks/Albums.tsx | 36 ++++++++-------- src/screens/Music/stacks/RecentAlbums.tsx | 17 ++++++-- .../Music/stacks/components/AlbumImage.ts | 13 +++--- .../Music/stacks/components/ListContainer.ts | 2 +- src/screens/Search/index.tsx | 43 ++++++++++++------- 10 files changed, 103 insertions(+), 67 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index af030b0..2dd5ef8 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -7,13 +7,29 @@ import store, { persistedStore } from 'store'; import { NavigationContainer, DefaultTheme, - DarkTheme, + DarkTheme as BaseDarkTheme, } from '@react-navigation/native'; import { useColorScheme } from 'react-native'; import { ColorSchemeContext, themes } from './Colors'; import DownloadManager from './DownloadManager'; // import ErrorReportingAlert from 'utility/ErrorReportingAlert'; +const LightTheme = { + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + background: themes.light.view.backgroundColor, + } +}; + +const DarkTheme = { + ...BaseDarkTheme, + colors: { + ...BaseDarkTheme.colors, + background: themes.light.view.backgroundColor, + } +}; + export default function App(): JSX.Element { const colorScheme = useColorScheme(); // const colorScheme = 'dark'; @@ -41,7 +57,9 @@ export default function App(): JSX.Element { - + diff --git a/src/components/Colors.tsx b/src/components/Colors.tsx index 0c28b38..7c36775 100644 --- a/src/components/Colors.tsx +++ b/src/components/Colors.tsx @@ -48,13 +48,9 @@ function generateStyles(scheme: ColorSchemeName) { backgroundColor: scheme === 'dark' ? '#161616' : '#eee', }, input: { - backgroundColor: scheme === 'dark' ? '#161616' : '#e6e6e6', + backgroundColor: scheme === 'dark' ? '#161616' : '#f3f3f3', color: scheme === 'dark' ? '#fff' : '#000', }, - sectionHeading: { - backgroundColor: scheme === 'dark' ? '#111' : '#eee', - borderColor: scheme === 'dark' ? '#333' : '#ddd', - }, stackHeader: { color: scheme === 'dark' ? 'white' : 'black' }, diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 05ea019..85e2794 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -4,6 +4,7 @@ const Input = styled.TextInput` margin: 10px 0; border-radius: 8px; padding: 15px; + padding-left: 40px; `; export default Input; \ No newline at end of file diff --git a/src/components/ListButton.tsx b/src/components/ListButton.tsx index 4fdf655..c8c32c9 100644 --- a/src/components/ListButton.tsx +++ b/src/components/ListButton.tsx @@ -1,30 +1,24 @@ import React, { useCallback, useState } from 'react'; import { TouchableOpacityProps } from 'react-native'; import ChevronRight from 'assets/icons/chevron-right.svg'; -import styled, { css } from 'styled-components/native'; +import styled from 'styled-components/native'; import { THEME_COLOR } from 'CONSTANTS'; import useDefaultStyles from './Colors'; const BUTTON_SIZE = 14; const Container = styled.Pressable<{ active?: boolean }>` - padding: 18px 20px; - border-bottom-width: 1px; + padding: 14px 16px; + border-radius: 8px; + margin: 4px 8px; flex-direction: row; justify-content: space-between; - - ${props => props.active && css` - background-color: ${THEME_COLOR}; - `} + align-items: center; `; const Label = styled.Text<{ active?: boolean }>` color: ${THEME_COLOR}; font-size: 16px; - - ${props => props.active && css` - color: white; - `} `; const ListButton: React.FC = ({ children, ...props }) => { @@ -34,16 +28,17 @@ const ListButton: React.FC = ({ children, ...props }) => const handlePressOut = useCallback(() => setPressed(false), []); return ( - // @ts-expect-error styled-components has outdated react-native typings - - + + ); }; diff --git a/src/screens/Downloads/index.tsx b/src/screens/Downloads/index.tsx index 7d7985f..72bce4c 100644 --- a/src/screens/Downloads/index.tsx +++ b/src/screens/Downloads/index.tsx @@ -22,7 +22,6 @@ const DownloadedTrack = styled.View` padding: 8px 0; align-items: center; margin: 0 20px; - border-bottom-width: 1px; `; function Downloads() { @@ -87,7 +86,7 @@ function Downloads() { ), [totalDownloadSize, defaultStyles, failedIds.length, handleRetryFailed, handleDeleteAllTracks, ids.length]); const renderItem = useCallback['renderItem']>>(({ item }) => ( - + diff --git a/src/screens/Music/stacks/Albums.tsx b/src/screens/Music/stacks/Albums.tsx index 7b81e3b..88ef9e3 100644 --- a/src/screens/Music/stacks/Albums.tsx +++ b/src/screens/Music/stacks/Albums.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useRef, ReactText } from 'react'; import { useGetImage } from 'utility/JellyfinApi'; import { MusicNavigationProp } from '../types'; -import { Text, SafeAreaView, SectionList, View } from 'react-native'; +import { SafeAreaView, SectionList, View } from 'react-native'; import { useDispatch } from 'react-redux'; import { useNavigation } from '@react-navigation/native'; import { differenceInDays } from 'date-fns'; @@ -9,13 +9,16 @@ import { useTypedSelector } from 'store'; import { fetchAllAlbums } from 'store/music/actions'; import { ALBUM_CACHE_AMOUNT_OF_DAYS } from 'CONSTANTS'; import TouchableHandler from 'components/TouchableHandler'; -import AlbumImage, { AlbumItem } from './components/AlbumImage'; +import AlbumImage, { AlbumHeight, AlbumItem } from './components/AlbumImage'; import { selectAlbumsByAlphabet, SectionedId } from 'store/music/selectors'; import AlphabetScroller from 'components/AlphabetScroller'; import { EntityId } from '@reduxjs/toolkit'; import styled from 'styled-components/native'; -import useDefaultStyles from 'components/Colors'; +import useDefaultStyles, { ColoredBlurView } from 'components/Colors'; import { Album } from 'store/music/types'; +import { Text } from 'components/Typography'; + +const HeadingHeight = 50; interface VirtualizedItemInfo { section: SectionedId, @@ -40,25 +43,25 @@ function generateSection({ section }: { section: SectionedId }) { } const SectionContainer = styled.View` - border-bottom-width: 1px; - height: 50px; + height: ${HeadingHeight}px; justify-content: center; - padding: 0 10px; + padding: 0 24px; `; -const SectionText = styled.Text` +const SectionText = styled(Text)` font-size: 24px; - font-weight: bold; + font-weight: 400; `; const SectionHeading = React.memo(function SectionHeading(props: { label: string }) { - const defaultStyles = useDefaultStyles(); const { label } = props; return ( - - {label} - + + + {label} + + ); }); @@ -118,21 +121,20 @@ const Albums: React.FC = () => { // We can then determine the "length" (=height) of this item. Header items // end up with an itemIndex of -1, thus are easy to identify. - const length = header ? 50 : (itemIndex % 2 === 0 ? 220 : 0); + const length = header ? 50 : (itemIndex % 2 === 0 ? AlbumHeight : 0); // We'll also need to account for any unevenly-ended lists up until the // current item. const previousRows = data?.filter((row, i) => i < sectionIndex) .reduce((sum, row) => sum + Math.ceil(row.data.length / 2), 0) || 0; - // We must also calcuate the offset, total distance from the top of the // screen. First off, we'll account for each sectionIndex that is shown up // until now. This only includes the heading for the current section if the // item is not the section header - const headingOffset = 50 * (header ? sectionIndex : sectionIndex + 1); + const headingOffset = HeadingHeight * (header ? sectionIndex : sectionIndex + 1); const currentRows = itemIndex > 1 ? Math.ceil((itemIndex + 1) / 2) : 0; - const itemOffset = 220 * (previousRows + currentRows); + const itemOffset = AlbumHeight * (previousRows + currentRows); const offset = headingOffset + itemOffset; return { index, length, offset }; @@ -189,7 +191,7 @@ const Albums: React.FC = () => { onRefresh={retrieveData} getItemLayout={getItemLayout} ref={listRef} - keyExtractor={(item, index) => `${item}_${index}`} + keyExtractor={(item) => item as string} renderSectionHeader={generateSection} renderItem={generateItem} /> diff --git a/src/screens/Music/stacks/RecentAlbums.tsx b/src/screens/Music/stacks/RecentAlbums.tsx index fd309ad..667da3a 100644 --- a/src/screens/Music/stacks/RecentAlbums.tsx +++ b/src/screens/Music/stacks/RecentAlbums.tsx @@ -15,17 +15,23 @@ import ListButton from 'components/ListButton'; import { t } from '@localisation'; import useDefaultStyles from 'components/Colors'; import { Album } from 'store/music/types'; +import Divider from 'components/Divider'; +import styled from 'styled-components/native'; const styles = StyleSheet.create({ columnWrapper: { - paddingLeft: 10, - paddingRight: 10 + paddingHorizontal: 16, } }); +const HeaderContainer = styled.View` + display: flex; + flex-direction: row; + align-items: center; +`; + const NavigationHeader: React.FC = () => { const navigation = useNavigation(); - const defaultStyles = useDefaultStyles(); const handleAllAlbumsClick = useCallback(() => { navigation.navigate('Albums'); }, [navigation]); const handlePlaylistsClick = useCallback(() => { navigation.navigate('Playlists'); }, [navigation]); @@ -34,7 +40,10 @@ const NavigationHeader: React.FC = () => { {t('all-albums')} {t('playlists')} -
{t('recent-albums')}
+ +
{t('recent-albums')}
+ +
); diff --git a/src/screens/Music/stacks/components/AlbumImage.ts b/src/screens/Music/stacks/components/AlbumImage.ts index 79c4421..bc823d2 100644 --- a/src/screens/Music/stacks/components/AlbumImage.ts +++ b/src/screens/Music/stacks/components/AlbumImage.ts @@ -3,17 +3,20 @@ import FastImage from 'react-native-fast-image'; import { Dimensions } from 'react-native'; const Screen = Dimensions.get('screen'); +export const AlbumWidth = Screen.width / 2 - 24; +export const AlbumHeight = AlbumWidth + 40; +export const CoverSize = AlbumWidth - 16; export const AlbumItem = styled.View` - width: ${Screen.width / 2 - 10}px; - padding: 10px; - height: 220px; + width: ${AlbumWidth}px; + height: ${AlbumHeight}px; + padding: 8px; `; const AlbumImage = styled(FastImage)` border-radius: 10px; - width: ${Screen.width / 2 - 40}px; - height: ${Screen.width / 2 - 40}px; + width: ${CoverSize}px; + height: ${CoverSize}px; margin-bottom: 5px; `; diff --git a/src/screens/Music/stacks/components/ListContainer.ts b/src/screens/Music/stacks/components/ListContainer.ts index aed2f44..504d2c0 100644 --- a/src/screens/Music/stacks/components/ListContainer.ts +++ b/src/screens/Music/stacks/components/ListContainer.ts @@ -1,7 +1,7 @@ import styled from 'styled-components/native'; const ListContainer = styled.View` - padding: 10px; + padding: 24px; `; export default ListContainer; \ No newline at end of file diff --git a/src/screens/Search/index.tsx b/src/screens/Search/index.tsx index f27d9c4..af7dc93 100644 --- a/src/screens/Search/index.tsx +++ b/src/screens/Search/index.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef, useCallback } from 'react'; import Input from 'components/Input'; -import { ActivityIndicator, SafeAreaView, Text, TextInput, View } from 'react-native'; +import { ActivityIndicator, SafeAreaView, TextInput, View } from 'react-native'; import styled from 'styled-components/native'; import { useTypedSelector } from 'store'; import Fuse from 'fuse.js'; @@ -16,9 +16,14 @@ import useDefaultStyles from 'components/Colors'; import { searchAndFetchAlbums } from 'store/music/actions'; import { debounce } from 'lodash'; import { useDispatch } from 'react-redux'; +import { Text } from 'components/Typography'; +import DownloadIcon from 'components/DownloadIcon'; +import ChevronRight from 'assets/icons/chevron-right.svg'; +import SearchIcon from 'assets/icons/magnifying-glass.svg'; + const Container = styled.View` - padding: 0 20px; + padding: 0 32px; position: relative; `; @@ -37,8 +42,8 @@ const Loading = styled.View` const AlbumImage = styled(FastImage)` border-radius: 4px; - width: 25px; - height: 25px; + width: 32px; + height: 32px; margin-right: 10px; `; @@ -46,15 +51,14 @@ const HalfOpacity = styled.Text` opacity: 0.5; margin-top: 2px; font-size: 12px; + flex: 1 1 auto; `; const SearchResult = styled.View` flex-direction: row; align-items: center; - border-bottom-width: 1px; - margin-left: 15px; - padding-right: 15px; - height: 50px; + padding: 8px 32px; + height: 54px; `; const fuseOptions = { @@ -208,13 +212,13 @@ export default function Search() { const HeaderComponent = React.useMemo(() => ( + {isLoading && } ), [searchTerm, setSearchTerm, defaultStyles, isLoading]); @@ -249,16 +253,25 @@ export default function Search() { return ( id={album.Id} onPress={selectAlbum}> - + - - - {trackName || album.Name} - {album.AlbumArtist} + + + {trackName || album.Name} - - {type === 'AlbumArtist' ? t('album'): t('track')} + + {type === 'AlbumArtist' + ? `${t('album')} • ${album.AlbumArtist}` + : `${t('track')} • ${album.AlbumArtist} — ${album.Name}` + } + + + + + + );