Move more novel logic into redux

This commit is contained in:
Rajarshee Chatterjee
2021-03-21 11:11:09 +05:30
parent 625cdff212
commit 5ec08b3add
11 changed files with 231 additions and 114 deletions
+2 -2
View File
@@ -123,12 +123,12 @@ export const SearchAppbar = ({
const styles = StyleSheet.create({
searchAppbarContainer: {
marginTop: Constants.statusBarHeight + 4,
marginTop: Constants.statusBarHeight + 8,
height: 48,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
marginBottom: 8,
marginBottom: 12,
borderRadius: 8,
marginHorizontal: 12,
elevation: 2,
+89 -1
View File
@@ -1,16 +1,35 @@
import { LOADING_NOVEL, GET_NOVEL, GET_CHAPTERS } from "./types";
import { ToastAndroid } from "react-native";
import {
LOADING_NOVEL,
GET_NOVEL,
GET_CHAPTERS,
FETCHING_NOVEL,
SET_NOVEL,
UPDATE_IN_LIBRARY,
CHAPTER_DOWNLOADING,
CHAPTER_DOWNLOADED,
CHAPTER_DELETED,
} from "./types";
import {
checkNovelInDb,
getChaptersFromDb,
getNovelInfoFromDb,
insertChaptersInDb,
insertNovelInfoInDb,
toggleFavourite,
downloadChapterFromSource,
deleteChapterFromDb,
} from "../../services/db";
import {
fetchChaptersFromSource,
fetchNovelFromSource,
} from "../../services/api";
export const setNovel = (novel) => async (dispatch) => {
dispatch({ type: SET_NOVEL, payload: novel });
};
export const getNovel = (navigatingFrom, extensionId, novelUrl) => async (
dispatch
) => {
@@ -23,6 +42,8 @@ export const getNovel = (navigatingFrom, extensionId, novelUrl) => async (
payload: chapters,
});
} else {
dispatch({ type: FETCHING_NOVEL });
const inCache = await checkNovelInDb(novelUrl);
if (inCache) {
@@ -50,3 +71,70 @@ export const getNovel = (navigatingFrom, extensionId, novelUrl) => async (
}
}
};
export const sortAndFilterChapters = (novelUrl, filter, sort) => async (
dispatch
) => {
const chapters = await getChaptersFromDb(novelUrl, filter, sort);
dispatch({
type: GET_CHAPTERS,
payload: chapters,
});
};
export const insertNovelInLibrary = (libraryStatus, novelUrl) => async (
dispatch
) => {
const inLibrary = await toggleFavourite(libraryStatus, novelUrl);
dispatch({
type: UPDATE_IN_LIBRARY,
payload: inLibrary,
});
ToastAndroid.show(
inLibrary ? "Added to library" : "Removed from library",
ToastAndroid.SHORT
);
};
// export const downloadChapter = (
// extensionId,
// novelUrl,
// chapterUrl,
// chapterName
// ) => async (dispatch) => {
// dispatch({
// type: CHAPTER_DOWNLOADING,
// payload: {
// extensionId,
// novelUrl,
// chapterUrl,
// },
// });
// await downloadChapterFromSource(extensionId, novelUrl, chapterUrl);
// dispatch({
// type: CHAPTER_DOWNLOADED,
// payload: { extensionId, novelUrl, chapterUrl },
// });
// ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
// };
// export const deleteChapter = (
// extensionId,
// novelUrl,
// chapterUrl,
// chapterName
// ) => (dispatch) => {
// deleteChapterFromDb(extensionId, novelUrl, chapterUrl);
// dispatch({
// type: CHAPTER_DELETED,
// payload: { extensionId, novelUrl, chapterUrl },
// });
// ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
// };
+6
View File
@@ -10,10 +10,16 @@ export const GET_EXTENSIONS = "GET_EXTENSIONS";
export const LOADING_NOVEL = "LOADING_NOVEL";
export const FETCHING_NOVEL = "FETCHING_NOVEL";
export const LOADING_CHAPTERS = "LOADING_CHAPTERS";
export const SET_NOVEL = "SET_NOVEL";
export const GET_NOVEL = "GET_NOVEL";
export const UPDATE_IN_LIBRARY = "UPDATE_IN_LIBRARY";
export const GET_CHAPTERS = "GET_CHAPTERS";
export const CHAPTER_DOWNLOADED = "CHAPTER_DOWNLOADED";
+42 -17
View File
@@ -5,12 +5,16 @@ import {
GET_CHAPTERS,
LOADING_NOVEL,
GET_NOVEL,
FETCHING_NOVEL,
SET_NOVEL,
UPDATE_IN_LIBRARY,
} from "../actions/types";
const initialState = {
novel: null,
chapters: [],
loading: true,
fetching: false,
};
const novelReducer = (state = initialState, action) => {
@@ -19,39 +23,60 @@ const novelReducer = (state = initialState, action) => {
switch (type) {
case LOADING_NOVEL:
return { ...state, loading: true };
case FETCHING_NOVEL:
return { ...state, loading: true, fetching: true };
case SET_NOVEL:
return { ...state, novel: payload };
case GET_NOVEL:
return {
...state,
novel: payload.novel,
chapters: payload.chapters,
loading: false,
fetching: false,
};
case GET_CHAPTERS:
return {
...state,
chapters: payload,
loading: false,
fetching: false,
};
case CHAPTER_DOWNLOADING:
return { ...state, downloads: [...state.downloads, payload] };
case CHAPTER_DOWNLOADED:
case UPDATE_IN_LIBRARY:
return {
...state,
downloads: state.downloads.map((chapter) =>
chapter.chapterUrl === payload.chapterUrl &&
chapter.extensionId === payload.extensionId
? { ...chapter, downloading: false, downloaded: true }
: chapter
),
};
case CHAPTER_DELETED:
return {
downloads: state.downloads.filter(
(chapter) =>
chapter.chapterUrl === payload.chapterUrl &&
chapter.extensionId === payload.extensionId
),
novel: { ...state.novel, libraryStatus: payload },
};
// 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
// ),
// };
default:
return state;
}
+6 -1
View File
@@ -19,6 +19,7 @@ import {
searchLibraryNovels,
} from "../../redux/actions/library";
import { SearchAppbar } from "../../components/common/Appbar";
import { setNovel } from "../../redux/actions/novel";
const LibraryScreen = ({
navigation,
@@ -27,6 +28,7 @@ const LibraryScreen = ({
loading,
getLibraryNovels,
searchLibraryNovels,
setNovel,
}) => {
const [refreshing, setRefreshing] = useState(false);
@@ -49,11 +51,13 @@ const LibraryScreen = ({
setRefreshing(false);
};
const redirectToNovel = (item) =>
const redirectToNovel = (item) => {
navigation.navigate("NovelItem", {
...item,
navigatingFrom: 1,
});
setNovel(item);
};
return (
<>
@@ -129,6 +133,7 @@ const mapStateToProps = (state) => ({
export default connect(mapStateToProps, {
getLibraryNovels,
searchLibraryNovels,
setNovel,
})(LibraryScreen);
const styles = StyleSheet.create({
+2 -2
View File
@@ -24,13 +24,13 @@ const AboutScreen = ({ navigation }) => {
titleStyle={{ color: theme.textColorPrimary }}
title="Version"
descriptionStyle={{ color: theme.textColorSecondary }}
description="Stable 1.0.3"
description="Stable 1.0.4"
/>
<List.Item
titleStyle={{ color: theme.textColorPrimary }}
title="Build Time"
descriptionStyle={{ color: theme.textColorSecondary }}
description="20-03-21 14:12 PM"
description="21-03-21 11:10 AM"
/>
<List.Item
+35 -60
View File
@@ -1,5 +1,4 @@
import React, { useState, useCallback, useRef, useEffect } from "react";
import { useFocusEffect } from "@react-navigation/native";
import React, { useState, useEffect, useRef } from "react";
import {
StyleSheet,
View,
@@ -13,11 +12,15 @@ import ChapterCard from "./components/ChapterCard";
import NovelInfoHeader from "./components/NovelHeader";
import { BottomSheet } from "./components/BottomSheet";
import { toggleFavourite, downloadOrDeleteChapter } from "../../services/db";
import { downloadOrDeleteChapter } from "../../services/db";
import { connect } from "react-redux";
import { getNovel } from "../../redux/actions/novel";
import {
getNovel,
insertNovelInLibrary,
sortAndFilterChapters,
} from "../../redux/actions/novel";
const Novel = ({
route,
@@ -27,9 +30,10 @@ const Novel = ({
chapters,
loading,
getNovel,
fetching,
insertNovelInLibrary,
sortAndFilterChapters,
}) => {
const item = route.params;
const {
extensionId,
novelUrl,
@@ -38,13 +42,6 @@ const Novel = ({
novelCover,
} = route.params;
// const [refreshing, setRefreshing] = useState(false);
const [libraryStatus, setlibraryStatus] = useState(item.libraryStatus);
const [sort, setSort] = useState("");
const [filter, setFilter] = useState("");
const [downloading, setDownloading] = useState({
downloading: false,
chapterUrl: "",
@@ -52,20 +49,6 @@ const Novel = ({
let _panel = useRef(null); // Bottomsheet ref
/**
* Insert or remove novel from library
*/
const insertNovelInLib = async () => {
toggleFavourite(libraryStatus, novelUrl).then((res) => {
setlibraryStatus(res);
ToastAndroid.show(
res ? "Added to library" : "Removed from library",
ToastAndroid.SHORT
);
});
};
/**
* Download chapter from source
*
@@ -96,27 +79,21 @@ const Novel = ({
});
};
useFocusEffect(
useCallback(() => {
getNovel(navigatingFrom, extensionId, novelUrl);
}, [sort, filter])
);
useEffect(() => {
getNovel(navigatingFrom, extensionId, novelUrl);
}, []);
const onRefresh = async () => {
// setRefreshing(true);
// checkIfExistsInDb();
};
const renderChapterCard = ({ item }) => (
<ChapterCard
navigation={navigation}
novelUrl={novelUrl}
extensionId={extensionId}
chapter={item}
downloadChapter={downloadChapter}
downloading={downloading}
/>
);
const renderChapterCard = ({ item }) =>
!loading && (
<ChapterCard
navigation={navigation}
novelUrl={novelUrl}
extensionId={extensionId}
chapter={item}
downloadChapter={downloadChapter}
downloading={downloading}
/>
);
return (
<Provider>
@@ -128,7 +105,6 @@ const Novel = ({
>
<FlatList
data={chapters}
extraData={[sort, filter]}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.chapterUrl}
removeClippedSubviews={true}
@@ -138,14 +114,10 @@ const Novel = ({
renderItem={renderChapterCard}
ListHeaderComponent={() => (
<NovelInfoHeader
item={{
novelCover: novelCover,
novelName: novelName,
}}
item={{ novelName, novelCover }}
novel={novel}
noOfChapters={chapters.length}
libraryStatus={libraryStatus}
insertNovelInLib={insertNovelInLib}
insertNovelInLibrary={insertNovelInLibrary}
navigatingFrom={navigatingFrom}
loading={loading}
bottomSheetRef={_panel}
@@ -153,7 +125,7 @@ const Novel = ({
)}
refreshControl={
<RefreshControl
refreshing={loading}
refreshing={fetching}
// onRefresh={onRefresh}
colors={[theme.textColorPrimary]}
progressBackgroundColor={theme.colorPrimary}
@@ -162,11 +134,9 @@ const Novel = ({
/>
<Portal>
<BottomSheet
novelUrl={novelUrl}
bottomSheetRef={_panel}
sort={sort}
filter={filter}
sortChapters={setSort}
filterChapters={setFilter}
sortAndFilterChapters={sortAndFilterChapters}
/>
</Portal>
</View>
@@ -178,9 +148,14 @@ const mapStateToProps = (state) => ({
novel: state.novelReducer.novel,
chapters: state.novelReducer.chapters,
loading: state.novelReducer.loading,
fetching: state.novelReducer.fetching,
});
export default connect(mapStateToProps, { getNovel })(Novel);
export default connect(mapStateToProps, {
getNovel,
insertNovelInLibrary,
sortAndFilterChapters,
})(Novel);
const styles = StyleSheet.create({
container: {
+35 -24
View File
@@ -1,32 +1,29 @@
import React from "react";
import React, { useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { List, RadioButton, Checkbox } from "react-native-paper";
import Bottomsheet from "rn-sliding-up-panel";
import { useSelector } from "react-redux";
const BottomSheetHandle = () => (
<View
style={{
backgroundColor: "rgba(255,255,255,0.25)",
height: 4,
width: 50,
borderRadius: 50,
top: 10,
alignSelf: "center",
}}
/>
);
export const BottomSheet = ({
bottomSheetRef,
sort,
filter,
sortChapters,
filterChapters,
sortAndFilterChapters,
novelUrl,
}) => {
const checkBoxStyle = { flexDirection: "row", alignItems: "center" };
const [chapterFlags, setChapterFlags] = useState({ sort: "", filter: "" });
const sortChapters = (sort) => {
setChapterFlags({ ...chapterFlags, sort: sort });
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
};
const filterChapters = (filter) => {
setChapterFlags({ ...chapterFlags, filter: filter });
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
};
const theme = useSelector((state) => state.themeReducer.theme);
return (
@@ -43,7 +40,7 @@ export const BottomSheet = ({
},
]}
>
<BottomSheetHandle />
<View style={styles.bottomSheetHandle} />
<List.Subheader
style={{
color: theme.textColorPrimary,
@@ -55,7 +52,7 @@ export const BottomSheet = ({
<View>
<RadioButton.Group
onValueChange={(newValue) => sortChapters(newValue)}
value={sort}
value={chapterFlags.sort}
c
>
<View
@@ -117,7 +114,9 @@ export const BottomSheet = ({
</List.Subheader>
<View style={checkBoxStyle}>
<Checkbox.Item
status={filter === "" ? "checked" : "unchecked"}
status={
chapterFlags.filter === "" ? "checked" : "unchecked"
}
uncheckedColor={theme.textColorHintDark}
color={theme.colorAccentDark}
onPress={() => filterChapters("")}
@@ -129,7 +128,9 @@ export const BottomSheet = ({
<View style={checkBoxStyle}>
<Checkbox.Item
status={
filter === "AND `read`=1" ? "checked" : "unchecked"
chapterFlags.filter === "AND `read`=1"
? "checked"
: "unchecked"
}
uncheckedColor={theme.textColorHintDark}
color={theme.colorAccentDark}
@@ -142,7 +143,9 @@ export const BottomSheet = ({
<View style={checkBoxStyle}>
<Checkbox.Item
status={
filter === "AND `read`=0" ? "checked" : "unchecked"
chapterFlags.filter === "AND `read`=0"
? "checked"
: "unchecked"
}
uncheckedColor={theme.textColorHintDark}
color={theme.colorAccentDark}
@@ -155,7 +158,7 @@ export const BottomSheet = ({
<View style={checkBoxStyle}>
<Checkbox.Item
status={
filter === "AND downloaded=1"
chapterFlags.filter === "AND downloaded=1"
? "checked"
: "unchecked"
}
@@ -178,4 +181,12 @@ const styles = StyleSheet.create({
borderTopRightRadius: 8,
borderTopLeftRadius: 8,
},
bottomSheetHandle: {
backgroundColor: "rgba(255,255,255,0.25)",
height: 4,
width: 50,
borderRadius: 50,
top: 10,
alignSelf: "center",
},
});
+11 -5
View File
@@ -20,10 +20,9 @@ const NovelInfoHeader = ({
item,
novel,
noOfChapters,
insertNovelInLib,
insertNovelInLibrary,
loading,
navigatingFrom,
libraryStatus,
bottomSheetRef,
theme,
}) => {
@@ -136,7 +135,7 @@ const NovelInfoHeader = ({
icon={() => (
<MaterialCommunityIcons
name={
libraryStatus
novel.libraryStatus
? "heart"
: "heart-outline"
}
@@ -144,7 +143,12 @@ const NovelInfoHeader = ({
color={theme.colorAccentDark}
/>
)}
onPress={() => insertNovelInLib()}
onPress={() =>
insertNovelInLibrary(
novel.libraryStatus,
novel.novelUrl
)
}
style={[
{
backgroundColor: theme.colorPrimaryDark,
@@ -168,7 +172,9 @@ const NovelInfoHeader = ({
color: theme.textColorPrimary,
}}
>
{libraryStatus ? "In Library" : "Add to library"}
{novel.libraryStatus
? "In Library"
: "Add to library"}
</Chip>
<IconButton
+1
View File
@@ -20,6 +20,7 @@ export const fetchNovelFromSource = async (extensionId, novelUrl) => {
sourceUrl: res.sourceUrl,
source: res.sourceName,
unread: 1,
libraryStatus: 0,
lastRead: res.novelChapters[0].chapterUrl,
lastReadName: res.novelChapters[0].chapterName,
extensionId: res.extensionId,
+2 -2
View File
@@ -386,8 +386,8 @@ export const downloadChapterFromSource = async (
db.transaction((tx) => {
tx.executeSql(
`UPDATE ChapterTable SET downloaded = 1 WHERE chapterUrl = ?`,
[chapterUrl]
`UPDATE ChapterTable SET downloaded = 1 WHERE chapterUrl = ? AND novelUrl = ?`,
[chapterUrl, novelUrl]
);
tx.executeSql(
`INSERT INTO DownloadsTable (chapterUrl, novelUrl, chapterName, chapterText, prevChapter, nextChapter) VALUES (?, ?, ?, ?, ?, ?)`,