Compare commits

...

5 Commits

Author SHA1 Message Date
Rajarshee Chatterjee 419420efd1 Add items per row settings 2021-03-23 18:34:58 +05:30
Rajarshee Chatterjee 871fc905ce Fix sorting and filter options 2021-03-23 07:59:08 +05:30
Rajarshee Chatterjee 5a8c652f68 Move delete chapter logic into redux 2021-03-22 21:34:04 +05:30
Rajarshee Chatterjee 97f2f84fa5 Move download chapter logic into redux 2021-03-22 20:52:39 +05:30
Rajarshee Chatterjee 65a3e8fd2d Fix statusbar 2021-03-22 14:23:32 +05:30
30 changed files with 350 additions and 264 deletions
-3
View File
@@ -7,7 +7,6 @@ import AppLoading from "expo-app-loading";
import Router from "./src/navigation/Router";
import { createTables, createIndexes } from "./src/services/db";
import { setStatusBar } from "./src/services/asyncStorage";
// Redux
import { Provider } from "react-redux";
@@ -22,8 +21,6 @@ const getFonts = () =>
const App = () => {
const [fontsLoaded, setFontsLoaded] = useState(false);
setStatusBar();
const createDb = () => {
createTables();
createIndexes();
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 32 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 24 KiB

+8
View File
@@ -7120,6 +7120,14 @@
"prop-types": "^15.7.2"
}
},
"react-native-input-spinner": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/react-native-input-spinner/-/react-native-input-spinner-1.7.7.tgz",
"integrity": "sha512-6zYFU2uhChJ+JC/5yujlZu+r5PYQSYIXluUqTNR3jOCKai0dlCkWiz7d3K2rM7Q2SAIiw4bSBR6mWZIuhL+DEg==",
"requires": {
"prop-types": "^15.7.2"
}
},
"react-native-iphone-x-helper": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
+1
View File
@@ -30,6 +30,7 @@
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
"react-native-collapsible-header-views": "^1.1.2",
"react-native-gesture-handler": "^1.8.0",
"react-native-input-spinner": "^1.7.7",
"react-native-offline": "^5.7.0",
"react-native-paper": "^4.5.0",
"react-native-reanimated": "^1.13.2",
+16 -11
View File
@@ -59,17 +59,22 @@ export const SearchAppbar = ({
}}
>
<View style={{ flex: 1, flexDirection: "row" }}>
<IconButton
icon={screen === "Library" ? "magnify" : "arrow-left"}
color={theme.textColorSecondary}
style={{ marginLeft: 0 }}
size={23}
onPress={() => {
if (screen === "Extension") {
navigation.goBack();
}
}}
/>
{screen === "Library" ? (
<IconButton
icon="magnify"
color={theme.textColorSecondary}
style={{ marginLeft: 0 }}
size={23}
/>
) : (
<IconButton
icon="arrow-left"
color={theme.textColorSecondary}
style={{ marginLeft: 0 }}
size={23}
onPress={() => navigation.goBack()}
/>
)}
<TextInput
ref={searchRef}
style={{
+63 -10
View File
@@ -1,5 +1,5 @@
import React from "react";
import { StyleSheet, View, Text, ImageBackground } from "react-native";
import { StyleSheet, View, Text, ImageBackground, Image } from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { TouchableRipple } from "react-native-paper";
@@ -12,12 +12,25 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
const theme = useSelector((state) => state.themeReducer.theme);
// const itemsPerRow = useSelector(
// (state) => state.settingsReducer.itemsPerRow
// );
const itemsPerRow = useSelector(
(state) => state.settingsReducer.itemsPerRow
);
return (
<View style={{ flex: 1 / 3 }}>
const height = {
1: 550,
2: 280,
3: 180,
4: 140,
5: 100,
6: 80,
7: 70,
8: 58,
9: 55,
10: 50,
};
return displayMode !== 2 ? (
<View style={{ flex: 1 / itemsPerRow }}>
<TouchableRipple
borderless
centered
@@ -29,7 +42,7 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
{item.novelCover && (
<ImageBackground
source={{ uri: item.novelCover }}
style={styles.logo}
style={{ height: height[itemsPerRow] }}
imageStyle={[
{ borderRadius: 4 },
libraryStatus && { opacity: 0.5 },
@@ -79,15 +92,42 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
</>
</TouchableRipple>
</View>
) : (
<TouchableRipple
borderless
centered
rippleColor={theme.rippleColor}
style={styles.listView}
onPress={onPress}
>
<>
<Image
source={{ uri: item.novelCover }}
style={styles.extensionIcon}
/>
<Text
style={{
flex: 1,
color: theme.textColorPrimary,
marginLeft: 16,
fontSize: 15,
paddingRight: 8,
}}
numberOfLines={1}
>
{item.novelName}
</Text>
</>
</TouchableRipple>
);
};
export default NovelCover;
const styles = StyleSheet.create({
logo: {
height: 180,
},
// logo: {
// height: 180,
// },
titleContainer: {
flex: 1,
justifyContent: "flex-end",
@@ -108,4 +148,17 @@ const styles = StyleSheet.create({
paddingVertical: 4,
borderRadius: 4,
},
extensionIcon: {
width: 42,
height: 42,
borderRadius: 4,
},
listView: {
flex: 1,
paddingVertical: 8,
paddingHorizontal: 12,
flexDirection: "row",
alignItems: "center",
borderRadius: 4,
},
});
+3 -2
View File
@@ -6,8 +6,9 @@ export const DisplayCheckbox = ({ displayMode, onPress, value }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const label = {
0: "Compact",
1: "Comfortable",
0: "Compact Grid",
1: "Comfortable Grid",
2: "List",
};
return (
+7 -1
View File
@@ -1,10 +1,11 @@
import React from "react";
import React, { useEffect } from "react";
import {
createStackNavigator,
TransitionPresets,
} from "@react-navigation/stack";
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import { Provider } from "react-native-paper";
import { setStatusBarStyle } from "expo-status-bar";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
@@ -62,6 +63,7 @@ import { useSelector } from "react-redux";
const BottomNavigator = () => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
<Tab.Navigator
shifting={false}
@@ -139,6 +141,10 @@ const BottomNavigator = () => {
};
const Router = () => {
const themeCode = useSelector((state) => state.themeReducer.themeCode);
useEffect(() => setStatusBarStyle(themeCode === 1 ? "dark" : "light"), []);
return (
<Provider>
<Stack.Navigator screenOptions={stackNavigatorConfig}>
+36 -40
View File
@@ -63,18 +63,14 @@ export const getNovel = (inLibrary, extensionId, novelUrl) => async (
});
} else {
const novel = await fetchNovelFromSource(extensionId, novelUrl);
const chapters = await fetchChaptersFromSource(
extensionId,
novelUrl
);
dispatch({
type: GET_NOVEL,
payload: { novel, chapters },
payload: { novel, chapters: novel.novelChapters },
});
insertNovelInfoInDb(novel);
insertChaptersInDb(novelUrl, chapters);
insertChaptersInDb(novelUrl, novel.novelChapters);
}
}
};
@@ -130,43 +126,43 @@ export const updateChapterRead = (chapterUrl, novelUrl) => async (dispatch) => {
dispatch({ type: CHAPTER_READ, payload: { chapterUrl, novelUrl } });
};
// export const downloadChapter = (
// extensionId,
// novelUrl,
// chapterUrl,
// chapterName
// ) => async (dispatch) => {
// dispatch({
// type: CHAPTER_DOWNLOADING,
// payload: {
// extensionId,
// novelUrl,
// chapterUrl,
// },
// });
export const downloadChapter = (
extensionId,
novelUrl,
chapterUrl,
chapterName
) => async (dispatch) => {
dispatch({
type: CHAPTER_DOWNLOADING,
payload: {
extensionId,
novelUrl,
chapterUrl,
},
});
// await downloadChapterFromSource(extensionId, novelUrl, chapterUrl);
await downloadChapterFromSource(extensionId, novelUrl, chapterUrl);
// dispatch({
// type: CHAPTER_DOWNLOADED,
// payload: { extensionId, novelUrl, chapterUrl },
// });
dispatch({
type: CHAPTER_DOWNLOADED,
payload: { extensionId, novelUrl, chapterUrl },
});
// ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
// };
ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
};
// export const deleteChapter = (
// extensionId,
// novelUrl,
// chapterUrl,
// chapterName
// ) => (dispatch) => {
// deleteChapterFromDb(extensionId, novelUrl, chapterUrl);
export const deleteChapter = (
extensionId,
novelUrl,
chapterUrl,
chapterName
) => async (dispatch) => {
await deleteChapterFromDb(novelUrl, chapterUrl);
// dispatch({
// type: CHAPTER_DELETED,
// payload: { extensionId, novelUrl, chapterUrl },
// });
dispatch({
type: CHAPTER_DELETED,
payload: { extensionId, novelUrl, chapterUrl },
});
// ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
// };
ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
};
+1
View File
@@ -13,6 +13,7 @@ export const setDisplayMode = (val) => (dispatch) => {
};
export const setItemsPerRow = (val) => (dispatch) => {
console.log("called" + val);
dispatch({
type: SET_ITEMS_PER_ROW,
payload: val,
+31 -30
View File
@@ -17,6 +17,7 @@ const initialState = {
novel: null,
chapters: [],
chapter: null,
downloading: [],
chapterLoading: true,
loading: true,
fetching: false,
@@ -70,36 +71,36 @@ const novelReducer = (state = initialState, action) => {
};
case GET_CHAPTER:
return { ...state, chapter: payload, chapterLoading: false };
// case CHAPTER_DOWNLOADING:
// return {
// ...state,
// chapters: [
// state.chapters.map((chap) =>
// chap.novelUrl === payload.novelUrl &&
// chap.chapterUrl === payload.novelUrl
// ? { ...chap, downloading: true, downloaded: false }
// : chap
// ),
// ],
// };
// case CHAPTER_DOWNLOADED:
// return {
// ...state,
// downloads: state.downloads.map((chap) =>
// chap.chapterUrl === payload.chapterUrl &&
// chap.novelUrl === payload.novelUrl
// ? { ...chap, downloading: false, downloaded: true }
// : chap
// ),
// };
// case CHAPTER_DELETED:
// return {
// downloads: state.downloads.filter(
// (chapter) =>
// chapter.chapterUrl === payload.chapterUrl &&
// chapter.novelUrl === payload.novelUrl
// ),
// };
case CHAPTER_DOWNLOADING:
return {
...state,
downloading: [...state.downloading, payload],
};
case CHAPTER_DOWNLOADED:
return {
...state,
downloading: state.downloading.filter(
(chapter) =>
chapter.chapterUrl === payload.chapterUrl &&
chapter.novelUrl === payload.novelUrl
),
chapters: state.chapters.map((chapter) =>
chapter.chapterUrl === payload.chapterUrl &&
chapter.novelUrl === payload.novelUrl
? { ...chapter, downloaded: 1 }
: chapter
),
};
case CHAPTER_DELETED:
return {
...state,
chapters: state.chapters.map((chapter) =>
chapter.chapterUrl === payload.chapterUrl &&
chapter.novelUrl === payload.novelUrl
? { ...chapter, downloaded: 0 }
: chapter
),
};
default:
return state;
}
+9 -2
View File
@@ -34,9 +34,16 @@ const settingsReducer = (state = initialState, action) => {
switch (type) {
case SET_DISPLAY_MODE:
return { ...state, displayMode: payload };
return {
...state,
displayMode: payload,
itemsPerRow: payload === 2 ? 1 : state.itemsPerRow,
};
case SET_ITEMS_PER_ROW:
return { ...state, itemsPerRow: payload };
return {
...state,
itemsPerRow: state.displayMode !== 2 ? payload : 1,
};
case UPDATE_READER_THEME:
return { ...state, reader: { ...state.reader, theme: payload } };
case UPDATE_READER_TEXT_SIZE:
@@ -17,6 +17,7 @@ const ExtensionCard = ({ item }) => {
style={styles.extensionCard}
onPress={() => navigation.navigate(sourceName + "Stack")}
rippleColor={theme.rippleColor}
borderless
>
<>
<Image
@@ -74,7 +75,7 @@ const styles = StyleSheet.create({
marginVertical: 4,
paddingVertical: 8,
paddingHorizontal: 20,
borderRadius: 6,
borderRadius: 4,
},
extensionIcon: {
width: 40,
+2 -2
View File
@@ -42,7 +42,7 @@ const ChapterItem = ({
};
const readerTextColor = (val) => {
const textColor = val === 1 ? theme.textColorSecondary : "#000000";
const textColor = val === 1 ? "rgba(255,255,255,0.7)" : "#000000";
return textColor;
};
@@ -71,7 +71,7 @@ const ChapterItem = ({
return (
<Provider>
<CollapsibleHeaderScrollView
headerContainerBackgroundColor={"rgba(0,0,0,0.4)"}
headerContainerBackgroundColor="rgba(0,0,0,0.4)"
CollapsibleHeaderComponent={
<Appbar.Header
style={{
@@ -28,12 +28,7 @@ const ChapterBottomSheet = ({
snappingPoints={[0, 200]}
showBackdrop={false}
>
<View
style={[
styles.contentContainer,
{ backgroundColor: "rgba(0,0,0,0.4)" },
]}
>
<View style={styles.contentContainer}>
<BottomSheetHandle />
<View style={styles.readerSettingsContainer}>
<Text
@@ -118,6 +113,9 @@ export default connect(mapStateToProps, {
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
backgroundColor: "rgba(0,0,0,0.4)",
},
readerSettingsContainer: { flex: 1, alignItems: "center", paddingTop: 30 },
});
@@ -21,7 +21,6 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
novelCover: item.novelCover,
extensionId: item.extensionId,
novelUrl: item.novelUrl,
libraryStatus: item.libraryStatus,
})
}
>
+4 -1
View File
@@ -29,6 +29,7 @@ const LibraryScreen = ({
getLibraryNovels,
searchLibraryNovels,
setNovel,
itemsPerRow,
}) => {
const [refreshing, setRefreshing] = useState(false);
@@ -83,7 +84,8 @@ const LibraryScreen = ({
flexGrow: 1,
paddingBottom: 8,
}}
numColumns={3}
numColumns={itemsPerRow}
key={itemsPerRow}
data={novels}
keyExtractor={(item) => item.novelUrl}
renderItem={({ item }) => (
@@ -126,6 +128,7 @@ const LibraryScreen = ({
const mapStateToProps = (state) => ({
theme: state.themeReducer.theme,
itemsPerRow: state.settingsReducer.itemsPerRow,
novels: state.libraryReducer.novels,
loading: state.libraryReducer.loading,
});
-86
View File
@@ -1,86 +0,0 @@
import React from "react";
import { StyleSheet, TextInput } from "react-native";
import { Appbar } from "react-native-paper";
import { useSelector } from "react-redux";
const LibraryAppbar = ({
search,
setSearch,
getLibraryNovels,
searchLibraryNovels,
}) => {
const theme = useSelector((state) => state.themeReducer.theme);
const { searching, searchText } = search;
return (
<Appbar.Header style={{ backgroundColor: theme.colorPrimary }}>
{!searching ? (
<>
<Appbar.Content
title="Library"
titleStyle={{ color: theme.textColorPrimary }}
/>
<Appbar.Action
icon="magnify"
onPress={() =>
setSearch({ ...search, searching: true })
}
color={theme.textColorPrimary}
/>
<Appbar.Action
color={theme.textColorPrimary}
icon="filter-variant"
disabled
// onPress={() => _panel.show({ velocity: -1.5 })}
/>
</>
) : (
<>
<Appbar.BackAction
onPress={() => {
setSearch({ searching: false, searchText: "" });
getLibraryNovels();
}}
color={theme.textColorPrimary}
size={24}
/>
<TextInput
placeholder="Search..."
defaultValue={searchText}
style={[
styles.searchBar,
{
color: theme.textColorPrimary,
},
]}
autoFocus
placeholderTextColor={theme.textColorHintDark}
blurOnSubmit={true}
onChangeText={(text) => {
setSearch({ ...search, searchText: text });
searchLibraryNovels(text);
}}
/>
{searchText !== "" && (
<Appbar.Action
icon="close"
onPress={() => {
setSearch({ ...search, searchText: "" });
getLibraryNovels();
}}
color={theme.textColorPrimary}
/>
)}
</>
)}
</Appbar.Header>
);
};
export default LibraryAppbar;
const styles = StyleSheet.create({
searchBar: { fontSize: 18, flex: 1, marginLeft: 15 },
});
+1 -1
View File
@@ -24,7 +24,7 @@ const AboutScreen = ({ navigation }) => {
titleStyle={{ color: theme.textColorPrimary }}
title="Version"
descriptionStyle={{ color: theme.textColorSecondary }}
description="Stable 1.0.6"
description="Stable 1.0.9"
/>
<List.Item
titleStyle={{ color: theme.textColorPrimary }}
+70 -4
View File
@@ -1,7 +1,8 @@
import React, { useState } from "react";
import { View } from "react-native";
import { View, Text } from "react-native";
import { setStatusBarStyle } from "expo-status-bar";
import { List, Modal, Portal } from "react-native-paper";
import InputSpinner from "react-native-input-spinner";
import { connect } from "react-redux";
import { switchTheme } from "../../redux/actions/theme";
@@ -24,12 +25,15 @@ const SettingsScreen = ({
theme,
themeCode,
displayMode,
itemsPerRow,
switchTheme,
setDisplayMode,
setItemsPerRow,
}) => {
const display = {
0: "Compact",
1: "Comfortable",
0: "Compact Grid",
1: "Comfortable Grid",
2: "List",
};
const themes = [
@@ -59,6 +63,10 @@ const SettingsScreen = ({
const showthemeModal = () => setthemeModalVisible(true);
const hidethemeModal = () => setthemeModalVisible(false);
const [itemsModalVisible, setitemsModalVisible] = useState(false);
const showitemsModal = () => setitemsModalVisible(true);
const hideitemsModal = () => setitemsModalVisible(false);
return (
<>
<Appbar title="Settings" onBackAction={() => navigation.goBack()} />
@@ -130,8 +138,66 @@ const SettingsScreen = ({
displayMode={displayMode}
onPress={() => setDisplayMode(0)}
/>
<DisplayCheckbox
value={2}
displayMode={displayMode}
onPress={() => setDisplayMode(2)}
/>
</Modal>
</Portal>
<List.Item
titleStyle={titleStyles}
title="Items per row in library"
descriptionStyle={desciptionStyles}
description={itemsPerRow}
onPress={showitemsModal}
rippleColor={theme.rippleColor}
/>
<Portal>
<Modal
visible={itemsModalVisible}
onDismiss={hideitemsModal}
contentContainerStyle={{
backgroundColor: theme.colorPrimaryDark,
padding: 20,
margin: 20,
borderRadius: 6,
}}
>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 18,
marginBottom: 10,
}}
>
Items per row
</Text>
<InputSpinner
style={{
paddingHorizontal: 16,
paddingVertical: 16,
elevation: 0,
}}
max={10}
min={1}
skin="paper"
background={theme.colorPrimaryDark}
colorMax={theme.colorAccentDark}
colorLeft={theme.colorAccentDark}
colorRight={theme.colorAccentDark}
buttonTextColor={theme.textColorPrimary}
colorPress={"white"}
colorMin={theme.colorAccentDark}
value={itemsPerRow}
onChange={(num) => setItemsPerRow(num)}
inputStyle={{
color: theme.textColorPrimary,
}}
/>
</Modal>
</Portal>
{/* <List.Subheader
style={{
color: theme.colorAccentDark,
@@ -148,7 +214,6 @@ const SettingsScreen = ({
rippleColor={theme.rippleColor}
onPress={() => deleteDatabase()}
/> */}
<List.Subheader
style={{
color: theme.colorAccentDark,
@@ -203,6 +268,7 @@ const mapStateToProps = (state) => ({
themeCode: state.themeReducer.themeCode,
theme: state.themeReducer.theme,
displayMode: state.settingsReducer.displayMode,
itemsPerRow: state.settingsReducer.itemsPerRow,
});
export default connect(mapStateToProps, {
+29 -29
View File
@@ -41,10 +41,10 @@ const Novel = ({
libraryStatus,
} = route.params;
const [downloading, setDownloading] = useState({
downloading: false,
chapterUrl: "",
});
// const [downloading, setDownloading] = useState({
// downloading: false,
// chapterUrl: "",
// });
let _panel = useRef(null); // Bottomsheet ref
@@ -54,29 +54,29 @@ const Novel = ({
* If downloaded then delete from db
*/
const downloadChapter = (
downloadStatus,
extensionId,
novelUrl,
chapterUrl,
chapterName
) => {
setDownloading({ downloading: true, chapterUrl: chapterUrl });
downloadOrDeleteChapter(
downloadStatus,
extensionId,
novelUrl,
chapterUrl
).then((res) => {
setDownloading({ downloading: false });
ToastAndroid.show(
!downloadStatus
? `Downloaded ${chapterName}`
: `Deleted ${chapterName}`,
ToastAndroid.SHORT
);
});
};
// const downloadChapter = (
// downloadStatus,
// extensionId,
// novelUrl,
// chapterUrl,
// chapterName
// ) => {
// setDownloading({ downloading: true, chapterUrl: chapterUrl });
// downloadOrDeleteChapter(
// downloadStatus,
// extensionId,
// novelUrl,
// chapterUrl
// ).then((res) => {
// setDownloading({ downloading: false });
// ToastAndroid.show(
// !downloadStatus
// ? `Downloaded ${chapterName}`
// : `Deleted ${chapterName}`,
// ToastAndroid.SHORT
// );
// });
// };
useEffect(() => {
getNovel(libraryStatus, extensionId, novelUrl);
@@ -87,8 +87,8 @@ const Novel = ({
novelUrl={novelUrl}
extensionId={extensionId}
chapter={item}
downloadChapter={downloadChapter}
downloading={downloading}
// downloadChapter={downloadChapter}
// downloading={downloading}
/>
);
+2 -3
View File
@@ -16,12 +16,12 @@ export const BottomSheet = ({
const sortChapters = (sort) => {
setChapterFlags({ ...chapterFlags, sort: sort });
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
sortAndFilterChapters(novelUrl, chapterFlags.filter, sort);
};
const filterChapters = (filter) => {
setChapterFlags({ ...chapterFlags, filter: filter });
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
sortAndFilterChapters(novelUrl, filter, chapterFlags.sort);
};
const theme = useSelector((state) => state.themeReducer.theme);
@@ -53,7 +53,6 @@ export const BottomSheet = ({
<RadioButton.Group
onValueChange={(newValue) => sortChapters(newValue)}
value={chapterFlags.sort}
c
>
<View
style={{
+38 -19
View File
@@ -3,21 +3,27 @@ import { View, Text, ActivityIndicator, StyleSheet } from "react-native";
import { TouchableRipple, IconButton } from "react-native-paper";
import { useNavigation } from "@react-navigation/native";
import { useSelector } from "react-redux";
import { deleteChapter, downloadChapter } from "../../../redux/actions/novel";
import { connect } from "react-redux";
const ChapterCard = ({
novelUrl,
chapter,
downloadChapter,
extensionId,
theme,
downloading,
downloadChapter,
deleteChapter,
}) => {
const [downloaded, setDownloaded] = useState(chapter.downloaded);
const theme = useSelector((state) => state.themeReducer.theme);
const navigation = useNavigation();
const isDownloading = () =>
downloading.some(
(obj) =>
obj.chapterUrl === chapter.chapterUrl &&
obj.novelUrl === novelUrl
);
return (
<TouchableRipple
style={styles.chapterCardContainer}
@@ -65,8 +71,7 @@ const ChapterCard = ({
)}
</View>
<View>
{downloading.downloading &&
downloading.chapterUrl === chapter.chapterUrl ? (
{isDownloading() && !chapter.downloaded ? (
<ActivityIndicator
color={theme.textColorHintDark}
size={25}
@@ -75,26 +80,33 @@ const ChapterCard = ({
) : (
<IconButton
icon={
downloaded
chapter.downloaded
? "check-circle"
: "arrow-down-circle-outline"
}
animated
color={
downloaded
chapter.downloaded
? theme.textColorPrimary
: theme.textColorHintDark
}
size={25}
onPress={() => {
downloadChapter(
downloaded ? 1 : 0,
extensionId,
novelUrl,
chapter.chapterUrl,
chapter.chapterName
);
setDownloaded(!downloaded);
if (!chapter.downloaded) {
downloadChapter(
extensionId,
novelUrl,
chapter.chapterUrl,
chapter.chapterName
);
} else {
deleteChapter(
extensionId,
novelUrl,
chapter.chapterUrl,
chapter.chapterName
);
}
}}
style={{ margin: 2 }}
/>
@@ -105,7 +117,14 @@ const ChapterCard = ({
);
};
export default ChapterCard;
const mapStateToProps = (state) => ({
theme: state.themeReducer.theme,
downloading: state.novelReducer.downloading,
});
export default connect(mapStateToProps, { downloadChapter, deleteChapter })(
ChapterCard
);
const styles = StyleSheet.create({
chapterCardContainer: {
@@ -81,6 +81,7 @@ const NovelInfoHeader = ({
fontSize: 14,
fontWeight: "bold",
}}
numberOfLines={2}
>
{novel["Author(s)"].replace(",", ", ")}
</Text>
+5 -1
View File
@@ -10,6 +10,9 @@ import { useSelector } from "react-redux";
const BoxNovel = ({ navigation }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const itemsPerRow = useSelector(
(state) => state.settingsReducer.itemsPerRow
);
const library = useSelector((state) => state.libraryReducer.novels);
@@ -83,7 +86,8 @@ const BoxNovel = ({ navigation }) => {
) : (
<FlatList
contentContainerStyle={styles.list}
numColumns={3}
numColumns={itemsPerRow}
key={itemsPerRow}
data={novels}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.novelUrl}
@@ -11,6 +11,9 @@ import EmptyView from "../../../components/common/EmptyView";
const ReadLightNovel = ({ navigation }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const library = useSelector((state) => state.libraryReducer.novels);
const itemsPerRow = useSelector(
(state) => state.settingsReducer.itemsPerRow
);
const [loading, setLoading] = useState(true);
@@ -75,7 +78,8 @@ const ReadLightNovel = ({ navigation }) => {
) : (
<FlatList
contentContainerStyle={{ flexGrow: 1 }}
numColumns={3}
numColumns={itemsPerRow}
key={itemsPerRow}
data={novels}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.novelUrl}
+1
View File
@@ -24,6 +24,7 @@ export const fetchNovelFromSource = async (extensionId, novelUrl) => {
lastRead: res.novelChapters[0].chapterUrl,
lastReadName: res.novelChapters[0].chapterName,
extensionId: res.extensionId,
novelChapters: res.novelChapters,
};
return novel;
+10 -9
View File
@@ -395,19 +395,20 @@ export const downloadChapterFromSource = async (
});
};
export const deleteChapterFromDb = (extensionId, novelUrl, chapterUrl) => {
const updateIsDownloadedQuery = `UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ? AND novelUrl = ? AND extensionId = ?`;
const deleteChapterQuery = `DELETE FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ? AND extensionId = ?`;
export const deleteChapterFromDb = async (novelUrl, chapterUrl) => {
const updateIsDownloadedQuery = `UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ? AND novelUrl = ?`;
const deleteChapterQuery = `DELETE FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ?`;
db.transaction((tx) => {
tx.executeSql(updateIsDownloadedQuery, [
chapterUrl,
novelUrl,
extensionId,
]);
tx.executeSql(
updateIsDownloadedQuery,
[chapterUrl, novelUrl],
(tx, res) => console.log(`Chapter deleted`),
(txObj, error) => console.log("Error ", error)
);
tx.executeSql(
deleteChapterQuery,
[chapterUrl, novelUrl, extensionId],
[chapterUrl, novelUrl],
(tx, res) => console.log(`Chapter deleted`),
(txObj, error) => console.log("Error ", error)
);
+1 -1
View File
@@ -21,7 +21,7 @@ export const lightTheme = {
textColorPrimary: "#000000",
textColorSecondary: "rgba(0,0,0,0.54)",
textColorHintDark: "rgba(0,0,0,0.38)",
rippleColor: "#C2C2C2",
rippleColor: "rgba(0,0,0,0.12)",
searchBarColor: "#FFFFFF",
};