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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 && <EmptyView icon="(˘・_・˘)" description="No downloads" />;
|
||||
!loading && (
|
||||
<EmptyView
|
||||
icon="(˘・_・˘)"
|
||||
description={getString('downloadScreen.noDownloads')}
|
||||
/>
|
||||
);
|
||||
|
||||
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 }) => (
|
||||
<UpdatesItem
|
||||
const renderItem = ({ item, index }) => (
|
||||
<UpdateNovelCard
|
||||
keyProp={index}
|
||||
item={item}
|
||||
dispatch={dispatch}
|
||||
theme={theme}
|
||||
onPress={() => onPress(item)}
|
||||
onPressCover={() => onPressCover(item)}
|
||||
downloadQueue={downloadQueue}
|
||||
deleteChapter={deleteChapter}
|
||||
downloadChapter={downloadChapter}
|
||||
descriptionText={getString('downloadScreen.downloadChapters')}
|
||||
removeItemFromList
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -114,17 +88,14 @@ const Downloads = ({ navigation }) => {
|
||||
/>
|
||||
)}
|
||||
</Appbar>
|
||||
<List.InfoItem
|
||||
title="Downloads are saved in a SQLite Database."
|
||||
theme={theme}
|
||||
/>
|
||||
<List.InfoItem title={getString('downloadScreen.dbInfo')} theme={theme} />
|
||||
{loading ? (
|
||||
<UpdatesSkeletonLoading theme={theme} />
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={styles.flatList}
|
||||
data={chapters}
|
||||
keyExtractor={item => item.chapterId.toString()}
|
||||
data={groupUpdatesByDate(chapters)}
|
||||
//keyExtractor={item => item.chapterId.toString()}
|
||||
renderItem={renderItem}
|
||||
ListEmptyComponent={<ListEmptyComponent />}
|
||||
/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <ChapterDownloadingButton theme={theme} />;
|
||||
} else if (chapter.downloaded === 1) {
|
||||
return (
|
||||
<Menu
|
||||
visible={deleteChapterMenu}
|
||||
onDismiss={hideDeleteChapterMenu}
|
||||
anchor={
|
||||
<DeleteChapterButton theme={theme} onPress={showDeleteChapterMenu} />
|
||||
}
|
||||
contentStyle={{ backgroundColor: overlay(2, theme.surface) }}
|
||||
>
|
||||
<Menu.Item
|
||||
onPress={() => deleteChapter(chapter.chapterId, chapter.chapterName)}
|
||||
title="Delete"
|
||||
titleStyle={{ color: theme.onSurface }}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<DownloadChapterButton
|
||||
theme={theme}
|
||||
onPress={() =>
|
||||
downloadChapter(
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName,
|
||||
chapter.chapterId,
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const ChapterDownloadingButton = ({ theme }) => (
|
||||
<ActivityIndicator
|
||||
color={theme.outline}
|
||||
size={25}
|
||||
style={{ margin: 3.5, padding: 5 }}
|
||||
/>
|
||||
);
|
||||
|
||||
export const DownloadChapterButton = ({ theme, onPress }) => (
|
||||
<IconButton
|
||||
icon="arrow-down-circle-outline"
|
||||
animated
|
||||
iconColor={theme.outline}
|
||||
size={25}
|
||||
onPress={onPress}
|
||||
style={{ margin: 2 }}
|
||||
/>
|
||||
);
|
||||
|
||||
export const DeleteChapterButton = ({ theme, onPress }) => (
|
||||
<IconButton
|
||||
icon="check-circle"
|
||||
animated
|
||||
iconColor={theme.onSurface}
|
||||
size={25}
|
||||
onPress={onPress}
|
||||
style={{ margin: 2 }}
|
||||
/>
|
||||
);
|
||||
|
||||
export const ChapterBookmarkButton = ({ theme }) => (
|
||||
<IconButton
|
||||
icon="bookmark"
|
||||
iconColor={theme.primary}
|
||||
size={18}
|
||||
style={{ marginLeft: 2 }}
|
||||
/>
|
||||
);
|
||||
@@ -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<DownloadButtonProps> = ({
|
||||
downloadQueue,
|
||||
chapter,
|
||||
theme,
|
||||
deleteChapter,
|
||||
downloadChapter,
|
||||
hideDeleteChapterMenu,
|
||||
showDeleteChapterMenu,
|
||||
deleteChapterMenuVisible,
|
||||
}) => {
|
||||
const [downloaded, setDownloaded] = useState(chapter.downloaded);
|
||||
if (downloadQueue.some(chap => chap.chapterId === chapter.chapterId)) {
|
||||
return <ChapterDownloadingButton theme={theme} />;
|
||||
} else if (downloaded === 0) {
|
||||
return (
|
||||
<DownloadChapterButton
|
||||
theme={theme}
|
||||
onPress={() => {
|
||||
downloadChapter(chapter);
|
||||
setDownloaded(1);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Menu
|
||||
visible={deleteChapterMenuVisible}
|
||||
onDismiss={hideDeleteChapterMenu}
|
||||
anchor={
|
||||
<DeleteChapterButton theme={theme} onPress={showDeleteChapterMenu} />
|
||||
}
|
||||
contentStyle={{ backgroundColor: overlay(2, theme.surface) }}
|
||||
>
|
||||
<Menu.Item
|
||||
onPress={() => {
|
||||
deleteChapter(chapter);
|
||||
setDownloaded(0);
|
||||
}}
|
||||
title="Delete"
|
||||
titleStyle={{ color: theme.onSurface }}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface theme {
|
||||
theme: MD3ThemeType;
|
||||
}
|
||||
type buttonPropType = theme & {
|
||||
onPress: () => void;
|
||||
};
|
||||
export const ChapterDownloadingButton: React.FC<theme> = ({ theme }) => (
|
||||
<ActivityIndicator
|
||||
color={theme.outline}
|
||||
size={25}
|
||||
style={styles.activityIndicator}
|
||||
/>
|
||||
);
|
||||
|
||||
export const DownloadChapterButton: React.FC<buttonPropType> = ({
|
||||
theme,
|
||||
onPress,
|
||||
}) => (
|
||||
<IconButton
|
||||
icon="arrow-down-circle-outline"
|
||||
animated
|
||||
iconColor={theme.outline}
|
||||
size={25}
|
||||
onPress={onPress}
|
||||
style={styles.iconButton}
|
||||
/>
|
||||
);
|
||||
|
||||
export const DeleteChapterButton: React.FC<buttonPropType> = ({
|
||||
theme,
|
||||
onPress,
|
||||
}) => (
|
||||
<IconButton
|
||||
icon="check-circle"
|
||||
animated
|
||||
iconColor={theme.onSurface}
|
||||
size={25}
|
||||
onPress={onPress}
|
||||
style={styles.iconButton}
|
||||
/>
|
||||
);
|
||||
|
||||
export const ChapterBookmarkButton: React.FC<theme> = ({ theme }) => (
|
||||
<IconButton
|
||||
icon="bookmark"
|
||||
iconColor={theme.primary}
|
||||
size={18}
|
||||
style={styles.iconButtonLeft}
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
activityIndicator: { margin: 3.5, padding: 5 },
|
||||
iconButton: { margin: 2 },
|
||||
iconButtonLeft: { marginLeft: 2 },
|
||||
});
|
||||
@@ -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 (
|
||||
<Pressable
|
||||
style={[
|
||||
styles.chapterCardContainer,
|
||||
isSelected(chapterId) && {
|
||||
backgroundColor: color(theme.primary).alpha(0.12).string(),
|
||||
},
|
||||
]}
|
||||
onPress={() => onSelectPress(chapter, () => navigateToChapter(chapter))}
|
||||
onLongPress={() => onSelectLongPress(chapter)}
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
>
|
||||
<Row style={{ flex: 1, overflow: 'hidden' }}>
|
||||
{!!bookmark && <ChapterBookmarkButton theme={theme} />}
|
||||
<View>
|
||||
<Text
|
||||
style={{
|
||||
color: read
|
||||
? theme.outline
|
||||
: bookmark
|
||||
? theme.primary
|
||||
: theme.onSurface,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{showChapterTitles
|
||||
? chapterNumber
|
||||
? 'Chapter ' + chapterNumber
|
||||
: 'Chapter ' + index
|
||||
: chapterName}
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginTop: 5,
|
||||
}}
|
||||
>
|
||||
{releaseDate ? (
|
||||
<Text
|
||||
style={{
|
||||
color: read
|
||||
? theme.outline
|
||||
: bookmark
|
||||
? theme.primary
|
||||
: theme.onSurfaceVariant,
|
||||
fontSize: 12,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{releaseDate}
|
||||
</Text>
|
||||
) : null}
|
||||
{showProgressPercentage(chapter)}
|
||||
</View>
|
||||
</View>
|
||||
</Row>
|
||||
<DownloadButton
|
||||
downloadQueue={downloadQueue}
|
||||
chapter={chapter}
|
||||
theme={theme}
|
||||
deleteChapter={deleteChapter}
|
||||
downloadChapter={downloadChapter}
|
||||
hideDeleteChapterMenu={hideDeleteChapterMenu}
|
||||
showDeleteChapterMenu={showDeleteChapterMenu}
|
||||
deleteChapterMenu={deleteChapterMenu}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ChapterItem);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
chapterCardContainer: {
|
||||
height: 64,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
@@ -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<ChapterItemProps> = ({
|
||||
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 (
|
||||
<Pressable
|
||||
key={chapterId.toString()}
|
||||
style={[
|
||||
styles.chapterCardContainer,
|
||||
isSelected?.(chapterId) && {
|
||||
backgroundColor: color(theme.primary).alpha(0.12).string(),
|
||||
},
|
||||
containerStyle,
|
||||
]}
|
||||
onPress={() => {
|
||||
onSelectPress
|
||||
? onSelectPress(chapter, () => navigateToChapter(chapter))
|
||||
: navigateToChapter(chapter);
|
||||
}}
|
||||
onLongPress={() => onSelectLongPress?.(chapter)}
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
>
|
||||
<Row style={styles.row}>
|
||||
{left?.()}
|
||||
{!!bookmark && <ChapterBookmarkButton theme={theme} />}
|
||||
<View>
|
||||
<Text
|
||||
style={[
|
||||
styles.title,
|
||||
{
|
||||
fontSize: textSize ?? 14,
|
||||
color: read
|
||||
? theme.outline
|
||||
: bookmark
|
||||
? theme.primary
|
||||
: theme.onSurface,
|
||||
},
|
||||
]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{showChapterTitles
|
||||
? chapterNumber
|
||||
? 'Chapter ' + chapterNumber
|
||||
: 'Chapter ' + index
|
||||
: chapterName}
|
||||
</Text>
|
||||
<View style={styles.textRow}>
|
||||
{releaseDate && showDate ? (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
color: read
|
||||
? theme.outline
|
||||
: bookmark
|
||||
? theme.primary
|
||||
: theme.onSurfaceVariant,
|
||||
},
|
||||
styles.text,
|
||||
]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{releaseDate}
|
||||
</Text>
|
||||
) : null}
|
||||
{showProgressPercentage?.(chapter)}
|
||||
</View>
|
||||
</View>
|
||||
</Row>
|
||||
<DownloadButton
|
||||
downloadQueue={downloadQueue}
|
||||
chapter={chapter}
|
||||
theme={theme}
|
||||
deleteChapter={deleteChapter}
|
||||
downloadChapter={downloadChapter}
|
||||
hideDeleteChapterMenu={hideDeleteChapterMenu}
|
||||
showDeleteChapterMenu={showDeleteChapterMenu}
|
||||
deleteChapterMenuVisible={deleteChapterMenuVisible}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
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' },
|
||||
});
|
||||
@@ -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<Record<string, Update[]>>(
|
||||
const dateGroups = rawHistory.reduce<Record<string, Update[][]>>(
|
||||
(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 (
|
||||
<>
|
||||
<SearchbarV2
|
||||
@@ -148,27 +101,25 @@ const UpdatesScreen = () => {
|
||||
) : null
|
||||
}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
keyExtractor={item => item.chapterId.toString()}
|
||||
sections={groupUpdatesByDate(searchText ? searchResults : updates)}
|
||||
renderSectionHeader={({ section: { date } }) => (
|
||||
<Text style={[styles.dateHeader, { color: theme.onSurface }]}>
|
||||
{dayjs(date).calendar()}
|
||||
</Text>
|
||||
)}
|
||||
keyExtractor={item => item[0].chapterId.toString()}
|
||||
renderItem={({ item }) => (
|
||||
<UpdateCard
|
||||
<UpdateNovelCard
|
||||
item={item}
|
||||
navigateToChapter={navigateToChapter}
|
||||
navigateToNovel={navigateToNovel}
|
||||
handleDownloadChapter={handleDownloadChapter}
|
||||
handleDeleteChapter={handleDeleteChapter}
|
||||
dispatch={dispatch}
|
||||
theme={theme}
|
||||
descriptionText={getString('updatesScreen.newChapters')}
|
||||
/>
|
||||
)}
|
||||
ListEmptyComponent={
|
||||
<EmptyView
|
||||
icon="(˘・_・˘)"
|
||||
description="No recent updates"
|
||||
description={getString('updatesScreen.emptyView')}
|
||||
theme={theme}
|
||||
/>
|
||||
}
|
||||
@@ -205,7 +156,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
dateHeader: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 2,
|
||||
},
|
||||
lastUpdateTime: {
|
||||
paddingHorizontal: 16,
|
||||
|
||||
@@ -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 <ChapterDownloadingButton theme={theme} />;
|
||||
} else if (chapter.downloaded === 1) {
|
||||
return (
|
||||
<Menu
|
||||
visible={deleteChapterMenu}
|
||||
onDismiss={hideDeleteChapterMenu}
|
||||
anchor={
|
||||
<DeleteChapterButton theme={theme} onPress={showDeleteChapterMenu} />
|
||||
}
|
||||
contentStyle={{ backgroundColor: overlay(2, theme.surface) }}
|
||||
>
|
||||
<Menu.Item
|
||||
onPress={() => {
|
||||
deleteChapter(
|
||||
chapter.sourceId,
|
||||
chapter.novelId,
|
||||
chapter.chapterId,
|
||||
chapter.chapterName,
|
||||
);
|
||||
hideDeleteChapterMenu();
|
||||
}}
|
||||
title="Delete"
|
||||
titleStyle={{ color: theme.onSurface }}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<DownloadChapterButton
|
||||
theme={theme}
|
||||
onPress={() =>
|
||||
downloadChapter(
|
||||
chapter.sourceId,
|
||||
chapter.novelUrl,
|
||||
chapter.novelId,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName,
|
||||
chapter.chapterId,
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -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<UpdateCardProps> = ({
|
||||
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 (
|
||||
<Pressable
|
||||
style={styles.container}
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
onPress={() => {
|
||||
navigateToChapter(item, item);
|
||||
}}
|
||||
>
|
||||
<View style={styles.imageContainer}>
|
||||
<Pressable onPress={() => navigateToNovel(item)}>
|
||||
<FastImage source={{ uri: item.novelCover }} style={styles.cover} />
|
||||
</Pressable>
|
||||
<View style={styles.nameContainer}>
|
||||
<Text style={[styles.name, { color: titleColor }]} numberOfLines={1}>
|
||||
{item.novelName}
|
||||
</Text>
|
||||
<Text
|
||||
style={[styles.chapterName, { color: chapterNameColor }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.chapterName}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
{isDownloading ? (
|
||||
<ActivityIndicator
|
||||
color={theme.outline}
|
||||
size={26}
|
||||
style={styles.downloading}
|
||||
/>
|
||||
) : !item.downloaded ? (
|
||||
<IconButtonV2
|
||||
name="arrow-down-circle-outline"
|
||||
size={25}
|
||||
onPress={() =>
|
||||
handleDownloadChapter(
|
||||
item.sourceId,
|
||||
item.novelUrl,
|
||||
item.novelId,
|
||||
item as ChapterItem,
|
||||
)
|
||||
}
|
||||
color={theme.outline}
|
||||
theme={theme}
|
||||
/>
|
||||
) : (
|
||||
<Menu
|
||||
visible={menu}
|
||||
onDismiss={() => setMenu(false)}
|
||||
anchor={
|
||||
<IconButtonV2
|
||||
name="check-circle"
|
||||
size={25}
|
||||
onPress={() => setMenu(true)}
|
||||
theme={theme}
|
||||
/>
|
||||
}
|
||||
contentStyle={{ backgroundColor: theme.overlay3 }}
|
||||
>
|
||||
<Menu.Item
|
||||
onPress={() =>
|
||||
handleDeleteChapter(
|
||||
item.sourceId,
|
||||
item.novelId,
|
||||
item.chapterId,
|
||||
item.chapterName,
|
||||
)
|
||||
}
|
||||
title="Delete"
|
||||
titleStyle={{ color: theme.onSurface }}
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -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<any>;
|
||||
theme: ThemeColors;
|
||||
descriptionText: string;
|
||||
removeItemFromList?: boolean;
|
||||
}
|
||||
|
||||
const UpdateNovelCard: React.FC<UpdateCardProps> = ({
|
||||
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 (
|
||||
<FastImage
|
||||
source={{ uri: chapterList[0].novelCover }}
|
||||
style={styles.cover}
|
||||
/>
|
||||
);
|
||||
};
|
||||
if (chapterList.length > 1) {
|
||||
return (
|
||||
<View style={styles.relativ}>
|
||||
<List.Accordion
|
||||
title={chapterList[0].novelName}
|
||||
left={BookCover}
|
||||
theme={{ colors: theme }}
|
||||
style={[styles.container, styles.padding]}
|
||||
description={chapterList.length + ' ' + descriptionText}
|
||||
onLongPress={() => navigateToNovel(chapterList[0])}
|
||||
>
|
||||
<FlatList
|
||||
data={chapterList}
|
||||
keyExtractor={it => it.chapterId.toString()}
|
||||
style={styles.flatList}
|
||||
renderItem={it => {
|
||||
return (
|
||||
<ChapterItem
|
||||
chapter={it.item}
|
||||
theme={theme}
|
||||
showChapterTitles={false}
|
||||
downloadQueue={downloadQueue}
|
||||
downloadChapter={handleDownloadChapter}
|
||||
deleteChapter={handleDeleteChapter}
|
||||
navigateToChapter={navigateToChapter}
|
||||
containerStyle={styles.chapterItem}
|
||||
onSelectLongPress={() => navigateToNovel(chapterList[0])}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
scrollEnabled={false}
|
||||
/>
|
||||
</List.Accordion>
|
||||
</View>
|
||||
);
|
||||
} else if (chapterList.length > 0) {
|
||||
return (
|
||||
<View style={[styles.relativ]}>
|
||||
<ChapterItem
|
||||
chapter={chapterList[0]}
|
||||
theme={theme}
|
||||
showChapterTitles={false}
|
||||
downloadQueue={downloadQueue}
|
||||
downloadChapter={handleDownloadChapter}
|
||||
deleteChapter={handleDeleteChapter}
|
||||
navigateToChapter={navigateToChapter}
|
||||
onSelectLongPress={() => navigateToNovel(chapterList[0])}
|
||||
textSize={16}
|
||||
left={() => (
|
||||
<View style={styles.chapterCover}>
|
||||
<BookCover />
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
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 },
|
||||
});
|
||||
@@ -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 (
|
||||
<Pressable
|
||||
style={styles.updateCard}
|
||||
onPress={onPress}
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
>
|
||||
<>
|
||||
<Pressable onPress={onPressCover}>
|
||||
<FastImage
|
||||
source={{ uri: item.novelCover }}
|
||||
style={styles.updateIcon}
|
||||
/>
|
||||
</Pressable>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<View style={styles.chapterDetails}>
|
||||
<Text
|
||||
style={{
|
||||
color: item.read ? theme.outline : theme.onSurface,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.novelName}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: item.read ? theme.outline : theme.onSurfaceVariant,
|
||||
fontSize: 12,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.chapterName}
|
||||
</Text>
|
||||
</View>
|
||||
<DownloadButton
|
||||
chapter={item}
|
||||
deleteChapter={deleteChapter}
|
||||
downloadChapter={downloadChapter}
|
||||
downloadQueue={downloadQueue}
|
||||
showDeleteChapterMenu={showDeleteChapterMenu}
|
||||
deleteChapterMenu={deleteChapterMenu}
|
||||
hideDeleteChapterMenu={hideDeleteChapterMenu}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user