Skip to content

Commit

Permalink
feat: new home screen
Browse files Browse the repository at this point in the history
  • Loading branch information
isdenmois committed Mar 21, 2021
1 parent bdc2b62 commit d6e8295
Show file tree
Hide file tree
Showing 21 changed files with 358 additions and 207 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
"@react-native-community/async-storage": "1.12.1",
"@react-native-community/datetimepicker": "3.4.0",
"@react-native-community/masked-view": "0.1.10",
"@react-navigation/bottom-tabs": "5.11.8",
"@react-navigation/native": "5.9.3",
"@react-navigation/stack": "5.14.3",
"@shopify/restyle": "1.4.0",
"bbcode-to-react": "0.2.9",
"i18next": "19.9.2",
"i18next-react-native-language-detector": "1.0.2",
Expand Down Expand Up @@ -114,6 +116,7 @@
"postinstall-postinstall": "2.1.0",
"prettier": "2.2.1",
"pretty-quick": "3.1.0",
"process": "^0.11.10",
"react-native-bundle-visualizer": "2.2.1",
"react-native-dotenv": "2.5.3",
"react-native-testing-library": "6.0.0",
Expand Down
3 changes: 2 additions & 1 deletion src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React from 'react';
import { ColorSchemeProvider } from 'react-native-dynamic';

import { StatusBarColor } from 'components/status-bar-color';
import { ThemeProvider } from 'components/theme';
import { MultiProvider } from 'utils/multi-provider';
import { Initializator } from 'services/initializator';
import { Root } from 'navigation';

const providers = [ColorSchemeProvider];
const providers = [ThemeProvider, ColorSchemeProvider];

export default function App() {
return (
Expand Down
10 changes: 5 additions & 5 deletions src/components/carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ScrollView, View, StyleSheet, ViewStyle } from 'react-native';
import { color, dark } from 'types/colors';
import { DynamicStyleSheet, DynamicValue, useDynamicValue } from 'react-native-dynamic';

const BUBLE_SIZE = 10;
const BUBLE_SIZE = 5;

interface Props {
children: any;
Expand Down Expand Up @@ -64,16 +64,16 @@ const dynamicStyles = new DynamicStyleSheet({
width: BUBLE_SIZE,
height: BUBLE_SIZE,
backgroundColor: new DynamicValue(color.Border, dark.Border),
borderRadius: 15,
borderRadius: BUBLE_SIZE,
alignSelf: 'center',
marginLeft: 1,
marginLeft: 3,
},
filled: {
width: BUBLE_SIZE,
height: BUBLE_SIZE,
backgroundColor: new DynamicValue(color.Primary, dark.Primary),
borderRadius: 15,
borderRadius: BUBLE_SIZE,
alignSelf: 'center',
marginLeft: 1,
marginLeft: 3,
},
});
13 changes: 8 additions & 5 deletions src/components/recycler-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ interface Props<T> {
data: T[];
rowRenderer: (type: string | number, data: any, index: number, extendedState?: object) => ReactElement;
itemHeight: number;
itemWidth?: number;
style?: any;
contentContainerStyle?: StyleProp<ViewStyle>;
ListEmptyComponent?: any;
ListHeaderComponent?: any;
ListFooterComponent?: any;
isHorizontal?: boolean;
showsHorizontalScrollIndicator?: boolean;
}

class ExtendedScrollView extends BaseScrollView {
Expand All @@ -33,11 +36,11 @@ class ExtendedScrollView extends BaseScrollView {
}

function RecyclerListComponent<T>(
{ data, itemHeight, ListEmptyComponent, ListFooterComponent, ...props }: Props<T>,
{ data, itemHeight, itemWidth, ListEmptyComponent, ListFooterComponent, ...props }: Props<T>,
ref,
) {
const provider = useDataProvider(data);
const layoutProvider = useLayoutProvider(itemHeight);
const layoutProvider = useLayoutProvider(itemHeight, itemWidth);

if (!data?.length) {
return ListEmptyComponent || null;
Expand Down Expand Up @@ -67,19 +70,19 @@ function useDataProvider<T>(data: T[]) {
return provider;
}

function useLayoutProvider(itemHeight: number) {
function useLayoutProvider(itemHeight: number, itemWidth?: number) {
const { width } = useWindowDimensions();

return useMemo(
() =>
new LayoutProvider(
() => 0,
(_, dim) => {
dim.width = width;
dim.width = itemWidth || width;
dim.height = itemHeight;
},
),
[width, itemHeight],
[width, itemHeight, itemWidth],
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const s = StyleSheet.create({
fontSize: 21,
},
xl: {
fontSize: 24,
fontSize: 20,
},
});

Expand Down
80 changes: 80 additions & 0 deletions src/components/theme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { FC } from 'react';
import { useColorScheme } from 'react-native';
import {
ThemeProvider as ReThemeProvider,
createBox,
createText,
createTheme,
useTheme as useThemeRN,
} from '@shopify/restyle';
import { dark, light } from 'types/colors';

export const palette = {
purple: '#5A31F4',
white: '#FFF',
black: '#111',
darkGray: '#333',
lightGray: '#EEE',
};

export const theme = createTheme({
spacing: {
1: 8,
2: 16,
3: 24,
4: 32,
},
colors: light,
breakpoints: {},
textVariants: {
title: {
fontSize: 20,
lineHeight: 24,
color: 'PrimaryText',
},
body: {
fontSize: 16,
color: 'PrimaryText',
},
empty: {
fontSize: 16,
color: 'Empty',
},
small: {
fontSize: 12,
lineHeight: 16,
color: 'SecondaryText',
},
},
cardVariants: {
primary: {
backgroundColor: 'primaryCardBackground',
shadowOpacity: 0.3,
},
secondary: {
backgroundColor: 'secondaryCardBackground',
shadowOpacity: 0.1,
},
},
});

type Theme = typeof theme;

const darkTheme: Theme = {
...theme,
colors: {
...theme.colors,
...dark,
},
};

export const Box = createBox<Theme>();
export const Text = createText<Theme>();

export const ThemeProvider: FC = ({ children }) => {
const currentMode = useColorScheme();

return <ReThemeProvider theme={currentMode === 'dark' ? darkTheme : theme}>{children}</ReThemeProvider>;
};

export const useTheme = () => useThemeRN<Theme>();
49 changes: 40 additions & 9 deletions src/screens/home/components/book-challenge.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import _ from 'lodash';
import React, { useMemo, useCallback } from 'react';
import { StyleSheet, TouchableOpacity, ViewStyle, ToastAndroid } from 'react-native';
import { StyleSheet, TouchableOpacity, ViewStyle, Alert, Platform } from 'react-native';
import withObservables from '@nozbe/with-observables';
import { t } from 'services/i18n';
import { useSetting } from 'services/settings';
import { Counter } from 'components';
import { dayOfYear, format, daysAmount } from 'utils/date';
import { readBooksThisYearQuery, booksReadForecast, lastReadDateObserver } from '../home.queries';
import { Box, Text } from 'components/theme';

const DATE_FORMAT = 'DD.MM';
const formatDate = date => format(date, DATE_FORMAT);
Expand All @@ -16,19 +16,50 @@ interface Props {
lastReadDate?: Date;
}

const showAlert = text => (Platform.OS === 'web' ? window.alert(text) : Alert.alert('', text));

function BookChallengeComponent({ readCount, lastReadDate }: Props) {
const totalBooks = useSetting('totalBooks');
const forecast = useMemo(() => booksReadForecast(readCount, totalBooks), [readCount, totalBooks]);
const percent = Math.round(100 * (readCount / totalBooks));
const showProgress = useCallback(
() => ToastAndroid.show(getChallengeMessage(readCount, totalBooks, new Date(lastReadDate)), ToastAndroid.LONG),
() => showAlert(getChallengeMessage(readCount, totalBooks, new Date(lastReadDate))),
[readCount, totalBooks, lastReadDate],
);

return (
<TouchableOpacity style={s.row} onPress={showProgress}>
<Counter label={t('home.challenge.completed')} value={readCount} />
<Counter label={t('home.challenge.anticipate')} value={totalBooks} testID='plannedBooksCount' />
<Counter label={t('home.challenge.ahead')} value={forecast} />
<TouchableOpacity onPress={showProgress}>
<Box mt={4} alignItems='center'>
<Text variant='title'>{t('home.challenge.title')}</Text>

<Box mt={2} backgroundColor='LightBackground' height={12} width='100%' borderRadius={6}>
<Box
position='absolute'
backgroundColor='Primary'
top={0}
bottom={0}
left={1}
borderRadius={6}
width={`${percent}%`}
/>
</Box>

<Box mt={1} alignItems='center'>
<Text variant='body'>{t('home.challenge.progress', { readCount, totalBooks })}</Text>

{forecast > 0 && (
<Text variant='small' color='Green' mt={1}>
{t('home.challenge.youare-ahead', { count: forecast, postProcess: 'rp' })}
</Text>
)}

{forecast < 0 && (
<Text variant='small' color='Red' mt={1}>
{t('home.challenge.youare-behind', { count: -forecast, postProcess: 'rp' })}
</Text>
)}
</Box>
</Box>
</TouchableOpacity>
);
}
Expand All @@ -51,7 +82,7 @@ export function getChallengeMessage(readCount: number, totalBooks: number, lastR
getForecastMessage(readCount, totalBooks, lastRead),
]
.filter(_.identity)
.join('\n\n');
.join('\n');
}

export function getProgressMessage(readCount: number, totalBooks: number): string {
Expand All @@ -68,7 +99,7 @@ export function getProgressMessage(readCount: number, totalBooks: number): strin

date.setMonth(0, dueDate);

return t('home.challenge.progress', {
return t('home.challenge.advice', {
date: formatDate(date),
});
}
Expand Down
40 changes: 0 additions & 40 deletions src/screens/home/components/current-book.tsx

This file was deleted.

27 changes: 27 additions & 0 deletions src/screens/home/components/current-books.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { FC } from 'react';
import { Carousel } from 'components';
import { currentBooksQuery } from '../home.queries';
import { NowReadingBook } from './now-reading-book';
import { EmptyBook } from './empty-book';
import { Dimensions } from 'react-native';
import { useObservable } from 'utils/use-observable';

const PADDINGS = 48;

function getCurrentBooks() {
return currentBooksQuery().observeWithColumns(['thumbnail']);
}

export const CurrentBook: FC = () => {
const books = useObservable(getCurrentBooks, null, []);
const width = Dimensions.get('screen').width;

if (!books) return null;
if (!books.length) return <EmptyBook />;

return (
<Carousel width={width - PADDINGS}>
{books.map(book => <NowReadingBook key={book.id} book={book} />).concat(<EmptyBook key='empty' />)}
</Carousel>
);
};
Loading

0 comments on commit d6e8295

Please sign in to comment.