From cf19eb68921ea64729ae3fa846675e4054595bf2 Mon Sep 17 00:00:00 2001 From: Patrick Loser <69157453+CD-Z@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:59:48 +0100 Subject: [PATCH] feat: Collapsible Updates and Downloads (#562) * implemented extendable list for updates * finished creating extendable list (buggy deleteChapter) * fixed downloadbutton bug * fixed ts problems * set navigatetonovel to onLongPress * removed * key fixes * implemented extendable list for updates * finished creating extendable list (buggy deleteChapter) * fixed downloadbutton bug * fixed ts problems * set navigatetonovel to onLongPress * removed * key fixes * translation strings * single chapter + style updates * removed test method --- src/database/types/index.ts | 6 + src/screens/more/DownloadsScreen.js | 93 +++------ src/screens/novel/NovelScreen.js | 19 +- .../Chapter/ChapterDownloadButtons.js | 88 -------- .../Chapter/ChapterDownloadButtons.tsx | 120 +++++++++++ src/screens/novel/components/ChapterItem.js | 116 ----------- src/screens/novel/components/ChapterItem.tsx | 160 +++++++++++++++ src/screens/updates/UpdatesScreen.tsx | 82 ++------ .../updates/components/DownloadButton.js | 64 ------ .../components/UpdateCard/UpdateCard.tsx | 178 ---------------- .../updates/components/UpdateNovelCard.tsx | 194 ++++++++++++++++++ src/screens/updates/components/UpdatesItem.js | 96 --------- strings/languages/en/strings.json | 9 +- strings/types/index.ts | 5 + 14 files changed, 555 insertions(+), 675 deletions(-) delete mode 100644 src/screens/novel/components/Chapter/ChapterDownloadButtons.js create mode 100644 src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx delete mode 100644 src/screens/novel/components/ChapterItem.js create mode 100644 src/screens/novel/components/ChapterItem.tsx delete mode 100644 src/screens/updates/components/DownloadButton.js delete mode 100644 src/screens/updates/components/UpdateCard/UpdateCard.tsx create mode 100644 src/screens/updates/components/UpdateNovelCard.tsx delete mode 100644 src/screens/updates/components/UpdatesItem.js diff --git a/src/database/types/index.ts b/src/database/types/index.ts index 8d85bb3f..a82cfb1d 100644 --- a/src/database/types/index.ts +++ b/src/database/types/index.ts @@ -30,6 +30,12 @@ export interface ChapterItem { bookmark: number; downloaded: number; } +export interface ChapterItemExtended extends ChapterItem { + novelId: number; + novelUrl: string; + sourceId: number; + novelName: string; +} export interface DownloadedChapter { downloadId: number; diff --git a/src/screens/more/DownloadsScreen.js b/src/screens/more/DownloadsScreen.js index 7ab45332..563adf12 100644 --- a/src/screens/more/DownloadsScreen.js +++ b/src/screens/more/DownloadsScreen.js @@ -1,9 +1,9 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { FlatList, StyleSheet } from 'react-native'; import { Appbar as MaterialAppbar } from 'react-native-paper'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { ScreenContainer } from '../../components/Common'; import EmptyView from '../../components/EmptyView'; @@ -15,25 +15,31 @@ import { import { useTheme } from '@hooks/useTheme'; -import { - deleteChapterAction, - downloadChapterAction, -} from '../../redux/novel/novel.actions'; - -import UpdatesItem from '../updates/components/UpdatesItem'; import RemoveDownloadsDialog from './components/RemoveDownloadsDialog'; -import { openChapter, openNovel } from '@utils/handleNavigateParams'; import UpdatesSkeletonLoading from '@screens/updates/components/UpdatesSkeletonLoading'; +import UpdateNovelCard from '@screens/updates/components/UpdateNovelCard'; +import { getString } from '@strings/translations'; const Downloads = ({ navigation }) => { const theme = useTheme(); const dispatch = useDispatch(); - const { downloadQueue } = useSelector(state => state.downloadsReducer); - const [loading, setLoading] = useState(true); const [chapters, setChapters] = useState([]); + const groupUpdatesByDate = chapters => { + const dateGroups = chapters.reduce((groups, item) => { + const novelId = item.novelId; + if (!groups[novelId]) { + groups[novelId] = []; + } + + groups[novelId].push(item); + + return groups; + }, {}); + return Object.values(dateGroups); + }; /** * Confirm Clear downloads Dialog @@ -49,57 +55,25 @@ const Downloads = ({ navigation }) => { }; const ListEmptyComponent = () => - !loading && ; + !loading && ( + + ); useEffect(() => { getChapters(); }, []); - const onPress = useCallback( - item => navigation.navigate('Chapter', openChapter(item, item)), - // eslint-disable-next-line react-hooks/exhaustive-deps - [], - ); - - const onPressCover = useCallback( - item => navigation.navigate('Novel', openNovel(item)), - // eslint-disable-next-line react-hooks/exhaustive-deps - [], - ); - - const downloadChapter = ( - sourceId, - novelUrl, - novelId, - chapterUrl, - chapterName, - chapterId, - ) => - dispatch( - downloadChapterAction( - sourceId, - novelUrl, - novelId, - chapterUrl, - chapterName, - chapterId, - ), - ); - - const deleteChapter = (sourceId, novelId, chapterId, chapterName) => { - dispatch(deleteChapterAction(sourceId, novelId, chapterId, chapterName)); - setChapters(chaps => chaps.filter(item => item.chapterId !== chapterId)); - }; - - const renderItem = ({ item }) => ( - ( + onPress(item)} - onPressCover={() => onPressCover(item)} - downloadQueue={downloadQueue} - deleteChapter={deleteChapter} - downloadChapter={downloadChapter} + descriptionText={getString('downloadScreen.downloadChapters')} + removeItemFromList /> ); @@ -114,17 +88,14 @@ const Downloads = ({ navigation }) => { /> )} - + {loading ? ( ) : ( item.chapterId.toString()} + data={groupUpdatesByDate(chapters)} + //keyExtractor={item => item.chapterId.toString()} renderItem={renderItem} ListEmptyComponent={} /> diff --git a/src/screens/novel/NovelScreen.js b/src/screens/novel/NovelScreen.js index 904e7f34..dcdc71f6 100644 --- a/src/screens/novel/NovelScreen.js +++ b/src/screens/novel/NovelScreen.js @@ -140,15 +140,15 @@ const Novel = ({ route, navigation }) => { const [jumpToChapterModal, showJumpToChapterModal] = useState(false); - const downloadChapter = (chapterUrl, chapterName, chapterId) => + const downloadChapter = chapter => dispatch( downloadChapterAction( sourceId, novelUrl, novelId, - chapterUrl, - chapterName, - chapterId, + chapter.chapterUrl, + chapter.chapterName, + chapter.chapterId, ), ); @@ -224,8 +224,15 @@ const Novel = ({ route, navigation }) => { return list; }, [selected]); - const deleteChapter = (chapterId, chapterName) => - dispatch(deleteChapterAction(sourceId, novelId, chapterId, chapterName)); + const deleteChapter = chapter => + dispatch( + deleteChapterAction( + sourceId, + novelId, + chapter.chapterId, + chapter.chapterName, + ), + ); const isSelected = chapterId => { return selected.some(obj => obj.chapterId === chapterId); diff --git a/src/screens/novel/components/Chapter/ChapterDownloadButtons.js b/src/screens/novel/components/Chapter/ChapterDownloadButtons.js deleted file mode 100644 index 798d031b..00000000 --- a/src/screens/novel/components/Chapter/ChapterDownloadButtons.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; -import { ActivityIndicator } from 'react-native'; - -import { IconButton, Menu, overlay } from 'react-native-paper'; - -export const DownloadButton = ({ - downloadQueue, - chapter, - theme, - deleteChapter, - downloadChapter, - hideDeleteChapterMenu, - showDeleteChapterMenu, - deleteChapterMenu, -}) => { - if (downloadQueue.some(chap => chap.chapterId === chapter.chapterId)) { - return ; - } else if (chapter.downloaded === 1) { - return ( - - } - contentStyle={{ backgroundColor: overlay(2, theme.surface) }} - > - deleteChapter(chapter.chapterId, chapter.chapterName)} - title="Delete" - titleStyle={{ color: theme.onSurface }} - /> - - ); - } else { - return ( - - downloadChapter( - chapter.chapterUrl, - chapter.chapterName, - chapter.chapterId, - ) - } - /> - ); - } -}; - -export const ChapterDownloadingButton = ({ theme }) => ( - -); - -export const DownloadChapterButton = ({ theme, onPress }) => ( - -); - -export const DeleteChapterButton = ({ theme, onPress }) => ( - -); - -export const ChapterBookmarkButton = ({ theme }) => ( - -); diff --git a/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx b/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx new file mode 100644 index 00000000..25ac3a6c --- /dev/null +++ b/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx @@ -0,0 +1,120 @@ +import { ChapterItemExtended } from '@database/types'; +import { MD3ThemeType } from '@theme/types'; +import React, { useState } from 'react'; +import { ActivityIndicator, StyleSheet } from 'react-native'; + +import { IconButton, Menu, overlay } from 'react-native-paper'; + +interface DownloadButtonProps { + downloadQueue: ChapterItemExtended[]; + chapter: ChapterItemExtended; + theme: MD3ThemeType; + deleteChapterMenuVisible: boolean; + deleteChapter: (chapter: ChapterItemExtended) => void; + downloadChapter: (chapter: ChapterItemExtended) => (dispatch: any) => void; + hideDeleteChapterMenu: () => void; + showDeleteChapterMenu: () => void; +} + +export const DownloadButton: React.FC = ({ + downloadQueue, + chapter, + theme, + deleteChapter, + downloadChapter, + hideDeleteChapterMenu, + showDeleteChapterMenu, + deleteChapterMenuVisible, +}) => { + const [downloaded, setDownloaded] = useState(chapter.downloaded); + if (downloadQueue.some(chap => chap.chapterId === chapter.chapterId)) { + return ; + } else if (downloaded === 0) { + return ( + { + downloadChapter(chapter); + setDownloaded(1); + }} + /> + ); + } else { + return ( + + } + contentStyle={{ backgroundColor: overlay(2, theme.surface) }} + > + { + deleteChapter(chapter); + setDownloaded(0); + }} + title="Delete" + titleStyle={{ color: theme.onSurface }} + /> + + ); + } +}; + +interface theme { + theme: MD3ThemeType; +} +type buttonPropType = theme & { + onPress: () => void; +}; +export const ChapterDownloadingButton: React.FC = ({ theme }) => ( + +); + +export const DownloadChapterButton: React.FC = ({ + theme, + onPress, +}) => ( + +); + +export const DeleteChapterButton: React.FC = ({ + theme, + onPress, +}) => ( + +); + +export const ChapterBookmarkButton: React.FC = ({ theme }) => ( + +); + +const styles = StyleSheet.create({ + activityIndicator: { margin: 3.5, padding: 5 }, + iconButton: { margin: 2 }, + iconButtonLeft: { marginLeft: 2 }, +}); diff --git a/src/screens/novel/components/ChapterItem.js b/src/screens/novel/components/ChapterItem.js deleted file mode 100644 index f8b46a9c..00000000 --- a/src/screens/novel/components/ChapterItem.js +++ /dev/null @@ -1,116 +0,0 @@ -import React, { memo, useState } from 'react'; -import { View, Text, StyleSheet, Pressable } from 'react-native'; -import color from 'color'; - -import { Row } from '../../../components/Common'; - -import { - ChapterBookmarkButton, - DownloadButton, -} from './Chapter/ChapterDownloadButtons'; -import { parseChapterNumber } from '@utils/parseChapterNumber'; - -const ChapterItem = ({ - chapter, - theme, - index, - showChapterTitles, - downloadQueue, - downloadChapter, - deleteChapter, - isSelected, - onSelectPress, - onSelectLongPress, - navigateToChapter, - showProgressPercentage, -}) => { - const { chapterId, chapterName, read, releaseDate, bookmark } = chapter; - - const [deleteChapterMenu, setDeleteChapterMenu] = useState(false); - const showDeleteChapterMenu = () => setDeleteChapterMenu(true); - const hideDeleteChapterMenu = () => setDeleteChapterMenu(false); - - const chapterNumber = parseChapterNumber(chapterName); - - return ( - onSelectPress(chapter, () => navigateToChapter(chapter))} - onLongPress={() => onSelectLongPress(chapter)} - android_ripple={{ color: theme.rippleColor }} - > - - {!!bookmark && } - - - {showChapterTitles - ? chapterNumber - ? 'Chapter ' + chapterNumber - : 'Chapter ' + index - : chapterName} - - - {releaseDate ? ( - - {releaseDate} - - ) : null} - {showProgressPercentage(chapter)} - - - - - - ); -}; - -export default memo(ChapterItem); - -const styles = StyleSheet.create({ - chapterCardContainer: { - height: 64, - paddingHorizontal: 16, - paddingVertical: 8, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, -}); diff --git a/src/screens/novel/components/ChapterItem.tsx b/src/screens/novel/components/ChapterItem.tsx new file mode 100644 index 00000000..25447a25 --- /dev/null +++ b/src/screens/novel/components/ChapterItem.tsx @@ -0,0 +1,160 @@ +import React, { memo, useState } from 'react'; +import { View, Text, StyleSheet, Pressable, ViewStyle } from 'react-native'; +import color from 'color'; + +import { Row } from '../../../components/Common'; + +import { + ChapterBookmarkButton, + DownloadButton, +} from './Chapter/ChapterDownloadButtons'; +import { parseChapterNumber } from '@utils/parseChapterNumber'; + +import { ThemeColors } from '@theme/types'; +import { ChapterItemExtended } from '@database/types'; + +interface ChapterItemProps { + chapter: ChapterItemExtended; + theme: ThemeColors; + index?: number; + downloadQueue: any; + showChapterTitles: boolean; + isSelected?: (chapterId: number) => boolean; + downloadChapter: (chapter: ChapterItemExtended) => (dispatch: any) => void; + deleteChapter: (chapter: ChapterItemExtended) => void; + onSelectPress?: (chapter: ChapterItemExtended, arg1: () => void) => void; + onSelectLongPress?: (chapter: ChapterItemExtended) => void; + navigateToChapter: (chapter: ChapterItemExtended) => void; + showProgressPercentage?: (chapter: ChapterItemExtended) => void; + containerStyle?: ViewStyle; + showDate?: boolean; + left?: Function; + textSize?: number; +} + +const ChapterItem: React.FC = ({ + chapter, + theme, + index, + showChapterTitles, + downloadQueue, + downloadChapter, + deleteChapter, + isSelected, + onSelectPress, + onSelectLongPress, + navigateToChapter, + showProgressPercentage, + containerStyle, + showDate, + left, + textSize, +}) => { + const { chapterId, chapterName, read, releaseDate, bookmark } = chapter; + const [deleteChapterMenuVisible, setDeleteChapterMenuVisible] = + useState(false); + const showDeleteChapterMenu = () => setDeleteChapterMenuVisible(true); + const hideDeleteChapterMenu = () => setDeleteChapterMenuVisible(false); + const chapterNumber = parseChapterNumber(chapterName); + + return ( + { + onSelectPress + ? onSelectPress(chapter, () => navigateToChapter(chapter)) + : navigateToChapter(chapter); + }} + onLongPress={() => onSelectLongPress?.(chapter)} + android_ripple={{ color: theme.rippleColor }} + > + + {left?.()} + {!!bookmark && } + + + {showChapterTitles + ? chapterNumber + ? 'Chapter ' + chapterNumber + : 'Chapter ' + index + : chapterName} + + + {releaseDate && showDate ? ( + + {releaseDate} + + ) : null} + {showProgressPercentage?.(chapter)} + + + + + + ); +}; + +export default memo(ChapterItem); + +const styles = StyleSheet.create({ + chapterCardContainer: { + height: 64, + paddingHorizontal: 16, + paddingVertical: 8, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + title: { + marginTop: 5, + }, + text: { + fontSize: 12, + }, + textRow: { + flexDirection: 'row', + marginTop: 5, + }, + row: { flex: 1, overflow: 'hidden' }, +}); diff --git a/src/screens/updates/UpdatesScreen.tsx b/src/screens/updates/UpdatesScreen.tsx index 598f80a2..d534db6a 100644 --- a/src/screens/updates/UpdatesScreen.tsx +++ b/src/screens/updates/UpdatesScreen.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { useNavigation } from '@react-navigation/native'; import dayjs from 'dayjs'; import { RefreshControl, SectionList, StyleSheet, Text } from 'react-native'; @@ -7,32 +6,20 @@ import { EmptyView, ErrorScreenV2, SearchbarV2 } from '../../components'; import { convertDateToISOString } from '../../database/utils/convertDateToISOString'; -import { ChapterItem, Update } from '../../database/types'; +import { Update } from '../../database/types'; import { useSearch, useUpdates } from '../../hooks'; import { useAppDispatch } from '../../redux/hooks'; -import UpdateCard from './components/UpdateCard/UpdateCard'; import { updateLibraryAction } from '../../redux/updates/updates.actions'; -import { - deleteChapterAction, - downloadChapterAction, -} from '../../redux/novel/novel.actions'; + import { getString } from '../../../strings/translations'; import { ThemeColors } from '../../theme/types'; import { useTheme } from '@hooks/useTheme'; -import { - openChapter, - openChapterChapterTypes, - openChapterNovelTypes, - openChapterFunctionTypes, - openNovel, - openNovelProps, -} from '@utils/handleNavigateParams'; import UpdatesSkeletonLoading from './components/UpdatesSkeletonLoading'; +import UpdateNovelCard from './components/UpdateNovelCard'; const UpdatesScreen = () => { const theme = useTheme(); - const { navigate } = useNavigation(); const dispatch = useAppDispatch(); const { @@ -47,7 +34,6 @@ const UpdatesScreen = () => { } = useUpdates(); const { searchText, setSearchText, clearSearchbar } = useSearch(); - const onChangeText = (text: string) => { setSearchText(text); searchUpdates(text); @@ -59,15 +45,19 @@ const UpdatesScreen = () => { }; const groupUpdatesByDate = (rawHistory: Update[]) => { - const dateGroups = rawHistory.reduce>( + const dateGroups = rawHistory.reduce>( (groups, item) => { const date = convertDateToISOString(item.updateTime); + const novelId = item.novelId; if (!groups[date]) { groups[date] = []; } + if (!groups[date][novelId]) { + groups[date][novelId] = []; + } - groups[date].push(item); + groups[date][novelId].push(item); return groups; }, @@ -77,49 +67,12 @@ const UpdatesScreen = () => { const groupedHistory = Object.keys(dateGroups).map(date => { return { date, - data: dateGroups[date], + data: dateGroups[date].filter(val => val !== null), }; }); return groupedHistory; }; - - const handleDownloadChapter = ( - sourceId: number, - novelUrl: string, - novelId: number, - chapter: ChapterItem, - ) => - dispatch( - downloadChapterAction( - sourceId, - novelUrl, - novelId, - chapter.chapterUrl, - chapter.chapterName, - chapter.chapterId, - ), - ); - - const handleDeleteChapter = ( - sourceId: number, - novelId: number, - chapterId: number, - chapterName: string, - ) => dispatch(deleteChapterAction(sourceId, novelId, chapterId, chapterName)); - - const navigateToChapter = ( - novel: openChapterNovelTypes, - chapter: openChapterChapterTypes, - ) => - navigate( - 'Chapter' as never, - openChapter(novel, chapter) as openChapterFunctionTypes as never, - ); - - const navigateToNovel = (novel: openNovelProps) => - navigate('Novel' as never, openNovel(novel) as openNovelProps as never); - return ( <> { ) : null } contentContainerStyle={styles.listContainer} - keyExtractor={item => item.chapterId.toString()} sections={groupUpdatesByDate(searchText ? searchResults : updates)} renderSectionHeader={({ section: { date } }) => ( {dayjs(date).calendar()} )} + keyExtractor={item => item[0].chapterId.toString()} renderItem={({ item }) => ( - )} ListEmptyComponent={ } @@ -205,7 +156,8 @@ const styles = StyleSheet.create({ }, dateHeader: { paddingHorizontal: 16, - paddingVertical: 8, + paddingTop: 8, + paddingBottom: 2, }, lastUpdateTime: { paddingHorizontal: 16, diff --git a/src/screens/updates/components/DownloadButton.js b/src/screens/updates/components/DownloadButton.js deleted file mode 100644 index 79d0b33c..00000000 --- a/src/screens/updates/components/DownloadButton.js +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; - -import { Menu, overlay } from 'react-native-paper'; -import { - ChapterDownloadingButton, - DeleteChapterButton, - DownloadChapterButton, -} from '../../novel/components/Chapter/ChapterDownloadButtons'; - -export const DownloadButton = ({ - downloadQueue, - theme, - chapter, - downloadChapter, - deleteChapter, - deleteChapterMenu, - showDeleteChapterMenu, - hideDeleteChapterMenu, -}) => { - if (downloadQueue.some(chap => chap.chapterId === chapter.chapterId)) { - return ; - } else if (chapter.downloaded === 1) { - return ( - - } - contentStyle={{ backgroundColor: overlay(2, theme.surface) }} - > - { - deleteChapter( - chapter.sourceId, - chapter.novelId, - chapter.chapterId, - chapter.chapterName, - ); - hideDeleteChapterMenu(); - }} - title="Delete" - titleStyle={{ color: theme.onSurface }} - /> - - ); - } else { - return ( - - downloadChapter( - chapter.sourceId, - chapter.novelUrl, - chapter.novelId, - chapter.chapterUrl, - chapter.chapterName, - chapter.chapterId, - ) - } - /> - ); - } -}; diff --git a/src/screens/updates/components/UpdateCard/UpdateCard.tsx b/src/screens/updates/components/UpdateCard/UpdateCard.tsx deleted file mode 100644 index d203d9d4..00000000 --- a/src/screens/updates/components/UpdateCard/UpdateCard.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { - ActivityIndicator, - Pressable, - StyleSheet, - Text, - View, -} from 'react-native'; -import React, { useState } from 'react'; - -import { ChapterItem, Update } from '../../../../database/types'; -import FastImage from 'react-native-fast-image'; -import { IconButtonV2 } from '../../../../components'; -import { useDownloadQueue } from '../../../../redux/hooks'; -import { Menu } from 'react-native-paper'; -import { ThemeColors } from '../../../../theme/types'; -import { coverPlaceholderColor } from '../../../../theme/colors'; -import { - openChapterChapterTypes, - openChapterNovelTypes, - openNovelProps, -} from '@utils/handleNavigateParams'; - -interface UpdateCardProps { - item: Update; - navigateToChapter: ( - novel: openChapterNovelTypes, - chapter: openChapterChapterTypes, - ) => void; - navigateToNovel: (novel: openNovelProps) => void; - handleDownloadChapter: ( - sourceId: number, - novelUrl: string, - novelId: number, - chapter: ChapterItem, - ) => void; - handleDeleteChapter: ( - sourceId: number, - novelId: number, - chapterId: number, - chapterName: string, - ) => void; - theme: ThemeColors; -} - -const UpdateCard: React.FC = ({ - item, - navigateToChapter, - navigateToNovel, - handleDownloadChapter, - handleDeleteChapter, - theme, -}) => { - const titleColor = item.read ? theme.outline : theme.onSurface; - const chapterNameColor = item.bookmark - ? theme.primary - : item.read - ? theme.outline - : theme.onSurfaceVariant; - - const downloadQueue = useDownloadQueue(); - - const isDownloading = downloadQueue.some( - (chapter: ChapterItem) => chapter.chapterId === item.chapterId, - ); - - const [menu, setMenu] = useState(false); - - return ( - { - navigateToChapter(item, item); - }} - > - - navigateToNovel(item)}> - - - - - {item.novelName} - - - {item.chapterName} - - - - {isDownloading ? ( - - ) : !item.downloaded ? ( - - handleDownloadChapter( - item.sourceId, - item.novelUrl, - item.novelId, - item as ChapterItem, - ) - } - color={theme.outline} - theme={theme} - /> - ) : ( - setMenu(false)} - anchor={ - setMenu(true)} - theme={theme} - /> - } - contentStyle={{ backgroundColor: theme.overlay3 }} - > - - handleDeleteChapter( - item.sourceId, - item.novelId, - item.chapterId, - item.chapterName, - ) - } - title="Delete" - titleStyle={{ color: theme.onSurface }} - /> - - )} - - ); -}; - -export default UpdateCard; - -const styles = StyleSheet.create({ - container: { - paddingHorizontal: 16, - paddingVertical: 12, - flexDirection: 'row', - justifyContent: 'space-between', - }, - cover: { - height: 40, - width: 40, - borderRadius: 4, - backgroundColor: coverPlaceholderColor, - }, - imageContainer: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - }, - nameContainer: { - flex: 1, - marginLeft: 16, - paddingRight: 16, - }, - name: {}, - chapterName: { - marginTop: 4, - fontSize: 12, - }, - downloading: { - margin: 8, - }, -}); diff --git a/src/screens/updates/components/UpdateNovelCard.tsx b/src/screens/updates/components/UpdateNovelCard.tsx new file mode 100644 index 00000000..e56c52e5 --- /dev/null +++ b/src/screens/updates/components/UpdateNovelCard.tsx @@ -0,0 +1,194 @@ +import { FlatList, StyleSheet, View } from 'react-native'; +import React, { useState } from 'react'; + +import { ChapterItemExtended, Update } from '../../../database/types'; +import FastImage from 'react-native-fast-image'; +import { List } from 'react-native-paper'; +import { ThemeColors } from '../../../theme/types'; +import { coverPlaceholderColor } from '../../../theme/colors'; +import { + openChapter, + openChapterFunctionTypes, + openNovel, + openNovelProps, +} from '@utils/handleNavigateParams'; +import { Dispatch } from 'redux'; +import { + deleteChapterAction, + downloadChapterAction, +} from '@redux/novel/novel.actions'; +import { useNavigation } from '@react-navigation/native'; +import ChapterItem from '@screens/novel/components/ChapterItem'; +import { useSelector } from 'react-redux'; +import { RootState } from '@redux/store'; + +interface UpdateCardProps { + item: Update[]; + dispatch: Dispatch; + theme: ThemeColors; + descriptionText: string; + removeItemFromList?: boolean; +} + +const UpdateNovelCard: React.FC = ({ + item, + dispatch, + theme, + descriptionText, + removeItemFromList, +}) => { + const { navigate } = useNavigation(); + const [chapterList, setChapterList] = useState(item); + const handleDownloadChapter = (chapter: ChapterItemExtended) => + dispatch( + downloadChapterAction( + chapter.sourceId, + chapter.novelUrl, + chapter.novelId, + chapter.chapterUrl, + chapter.chapterName, + chapter.chapterId, + ), + ); + + const handleDeleteChapter = (chapter: ChapterItemExtended) => { + dispatch( + deleteChapterAction( + chapter.sourceId, + chapter.novelId, + chapter.chapterId, + chapter.chapterName, + ), + ); + if (removeItemFromList) { + //@ts-ignore + let index = item.indexOf(chapter); + item.splice(index, 1); + setChapterList(Array.from(item)); + } + }; + + const navigateToChapter = (chapter: ChapterItemExtended) => + navigate( + 'Chapter' as never, + openChapter(chapter, chapter) as openChapterFunctionTypes as never, + ); + + const navigateToNovel = (novel: openNovelProps) => + navigate('Novel' as never, openNovel(novel) as openNovelProps as never); + + const { downloadQueue } = useSelector( + (state: RootState) => state.downloadsReducer, + ); + + const BookCover = () => { + return ( + + ); + }; + if (chapterList.length > 1) { + return ( + + navigateToNovel(chapterList[0])} + > + it.chapterId.toString()} + style={styles.flatList} + renderItem={it => { + return ( + navigateToNovel(chapterList[0])} + /> + ); + }} + scrollEnabled={false} + /> + + + ); + } else if (chapterList.length > 0) { + return ( + + navigateToNovel(chapterList[0])} + textSize={16} + left={() => ( + + + + )} + /> + + ); + } + return null; +}; + +export default UpdateNovelCard; + +const styles = StyleSheet.create({ + relativ: { + position: 'relative', + }, + padding: { + paddingHorizontal: 16, + paddingVertical: 3, + height: 64, + }, + container: { + justifyContent: 'space-between', + }, + cover: { + height: 40, + width: 40, + borderRadius: 4, + backgroundColor: coverPlaceholderColor, + }, + chapterCover: { + marginRight: 8, + }, + imageContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + }, + nameContainer: { + flex: 1, + marginLeft: 16, + paddingRight: 16, + }, + chapterName: { + marginTop: 4, + fontSize: 12, + }, + downloading: { + margin: 8, + }, + flatList: { marginLeft: -64 }, + chapterItem: { paddingLeft: 48 }, +}); diff --git a/src/screens/updates/components/UpdatesItem.js b/src/screens/updates/components/UpdatesItem.js deleted file mode 100644 index bb93ac9d..00000000 --- a/src/screens/updates/components/UpdatesItem.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { memo, useState } from 'react'; -import { View, Text, StyleSheet, Pressable } from 'react-native'; - -import FastImage from 'react-native-fast-image'; -import { coverPlaceholderColor } from '../../../theme/colors'; -import { DownloadButton } from './DownloadButton'; - -const UpdateCard = ({ - item, - theme, - onPress, - onPressCover, - downloadQueue, - downloadChapter, - deleteChapter, -}) => { - const [deleteChapterMenu, setDeleteChapterMenu] = useState(false); - const showDeleteChapterMenu = () => setDeleteChapterMenu(true); - const hideDeleteChapterMenu = () => setDeleteChapterMenu(false); - - return ( - - <> - - - - - - - {item.novelName} - - - {item.chapterName} - - - - - - - ); -}; - -export default memo(UpdateCard); - -const styles = StyleSheet.create({ - updateCard: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 8, - paddingHorizontal: 16, - }, - updateIcon: { - width: 42, - height: 42, - borderRadius: 4, - backgroundColor: coverPlaceholderColor, - }, - chapterDetails: { - flex: 1, - marginLeft: 16, - }, -}); diff --git a/strings/languages/en/strings.json b/strings/languages/en/strings.json index 005078f3..f7fae43a 100644 --- a/strings/languages/en/strings.json +++ b/strings/languages/en/strings.json @@ -54,7 +54,9 @@ "updates": "Updates", "updatesScreen": { "searchbar": "Search updates", - "lastUpdatedAt": "Library last updated:" + "lastUpdatedAt": "Library last updated:", + "newChapters": "new Chapters", + "emptyView":"No recent updates" }, "history": "History", "historyScreen": { @@ -224,5 +226,10 @@ "settings": { "icognitoMode": "Incognito mode", "downloadedOnly": "Downloaded only" + }, + "downloadScreen": { + "dbInfo": "Downloads are saved in a SQLite Database.", + "downloadChapters": "downloaded Chapters", + "noDownloads": "No downloads" } } diff --git a/strings/types/index.ts b/strings/types/index.ts index 2ba3af16..6c2807e5 100644 --- a/strings/types/index.ts +++ b/strings/types/index.ts @@ -46,6 +46,8 @@ export interface StringMap { 'updates': 'string'; 'updatesScreen.searchbar': 'string'; 'updatesScreen.lastUpdatedAt': 'string'; + 'updatesScreen.newChapters': 'string'; + 'updatesScreen.emptyView': 'string'; 'history': 'string'; 'historyScreen.searchbar': 'string'; 'historyScreen.clearHistorWarning': 'string'; @@ -168,4 +170,7 @@ export interface StringMap { 'categories.defaultCategory': 'string'; 'settings.icognitoMode': 'string'; 'settings.downloadedOnly': 'string'; + 'downloadScreen.dbInfo': 'string'; + 'downloadScreen.downloadChapters': 'string'; + 'downloadScreen.noDownloads': 'string'; }