diff --git a/src/components/common/Appbar.js b/src/components/common/Appbar.js index e7f618c2..4d3cdca0 100644 --- a/src/components/common/Appbar.js +++ b/src/components/common/Appbar.js @@ -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, diff --git a/src/redux/actions/novel.js b/src/redux/actions/novel.js index 1630bb08..b6acfcee 100644 --- a/src/redux/actions/novel.js +++ b/src/redux/actions/novel.js @@ -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); +// }; diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js index ef28b4f5..308e3802 100644 --- a/src/redux/actions/types.js +++ b/src/redux/actions/types.js @@ -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"; diff --git a/src/redux/reducers/novel.js b/src/redux/reducers/novel.js index 8be74c67..a4f85c41 100644 --- a/src/redux/reducers/novel.js +++ b/src/redux/reducers/novel.js @@ -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; } diff --git a/src/screens/Library/Library.js b/src/screens/Library/Library.js index dfd8e1e7..1a12269a 100644 --- a/src/screens/Library/Library.js +++ b/src/screens/Library/Library.js @@ -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({ diff --git a/src/screens/More/About.js b/src/screens/More/About.js index 1b0cb888..e5664bd1 100644 --- a/src/screens/More/About.js +++ b/src/screens/More/About.js @@ -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" /> { - 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 }) => ( - - ); + const renderChapterCard = ({ item }) => + !loading && ( + + ); return ( @@ -128,7 +105,6 @@ const Novel = ({ > item.chapterUrl} removeClippedSubviews={true} @@ -138,14 +114,10 @@ const Novel = ({ renderItem={renderChapterCard} ListHeaderComponent={() => ( @@ -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: { diff --git a/src/screens/Novel/components/BottomSheet.js b/src/screens/Novel/components/BottomSheet.js index 6c7c26e9..451514b7 100644 --- a/src/screens/Novel/components/BottomSheet.js +++ b/src/screens/Novel/components/BottomSheet.js @@ -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 = () => ( - -); - 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 = ({ }, ]} > - + sortChapters(newValue)} - value={sort} + value={chapterFlags.sort} c > filterChapters("")} @@ -129,7 +128,9 @@ export const BottomSheet = ({ { @@ -136,7 +135,7 @@ const NovelInfoHeader = ({ icon={() => ( )} - 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"} { sourceUrl: res.sourceUrl, source: res.sourceName, unread: 1, + libraryStatus: 0, lastRead: res.novelChapters[0].chapterUrl, lastReadName: res.novelChapters[0].chapterName, extensionId: res.extensionId, diff --git a/src/services/db.js b/src/services/db.js index f21e6d06..87b1eb86 100644 --- a/src/services/db.js +++ b/src/services/db.js @@ -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 (?, ?, ?, ?, ?, ?)`,