diff --git a/App.js b/App.js index 3784fb10..6323795e 100644 --- a/App.js +++ b/App.js @@ -1,5 +1,4 @@ import React, { useEffect } from "react"; -import { NavigationContainer } from "@react-navigation/native"; import { useFonts } from "expo-font"; import { Provider as PaperProvider } from "react-native-paper"; import Main from "./src/Navigators/Main"; @@ -8,17 +7,10 @@ import { Provider } from "react-redux"; import { persistor, store } from "./src/redux/store"; import { PersistGate } from "redux-persist/lib/integration/react"; import { createDB, deleteDb } from "./src/Database/DBHelper"; +import { fonts } from "./src/Theme/fonts"; const App = () => { - const [loaded] = useFonts({ - "pt-sans-bold": require("./assets/fonts/PTSansNarrow-Bold.ttf"), - "arbutus-slab": require("./assets/fonts/ArbutusSlab-Regular.ttf"), - domine: require("./assets/fonts/Domine-VariableFont_wght.ttf"), - lato: require("./assets/fonts/Lato-Regular.ttf"), - "noto-sans": require("./assets/fonts/NotoSans-Regular.ttf"), - "open-sans": require("./assets/fonts/OpenSans-Regular.ttf"), - "pt-serif": require("./assets/fonts/PTSerif-Regular.ttf"), - }); + const [loaded] = useFonts(fonts); useEffect(() => createDB(), []); @@ -30,9 +22,7 @@ const App = () => { - -
- +
diff --git a/src/Components/List.js b/src/Components/List.js index 1618853f..5bc5f00f 100644 --- a/src/Components/List.js +++ b/src/Components/List.js @@ -45,15 +45,10 @@ const ListItem = ({ title, description, icon, onPress, theme, right }) => ( ); const Divider = ({ theme }) => ( - + ); -const InfoItem = ({ title, description, icon, onPress, theme, right }) => ( +const InfoItem = ({ title, icon, theme }) => ( {}; + +export default UpdateCard; + +const styles = StyleSheet.create({}); diff --git a/src/Hooks/githubUpdateChecker.js b/src/Hooks/githubUpdateChecker.js index 54824029..1bd673b5 100644 --- a/src/Hooks/githubUpdateChecker.js +++ b/src/Hooks/githubUpdateChecker.js @@ -22,7 +22,7 @@ export const githubUpdateChecker = () => { }; const isNewVersion = (versionTag) => { - let currentVersion = "v1.0.16"; + let currentVersion = "v1.0.17"; const regex = /[^\\d.]/; let newVersion = versionTag.replace(regex, ""); diff --git a/src/Hooks/reduxHooks.js b/src/Hooks/reduxHooks.js index 61760317..acb33c73 100644 --- a/src/Hooks/reduxHooks.js +++ b/src/Hooks/reduxHooks.js @@ -27,6 +27,12 @@ const useLibrary = () => { return library; }; +const useNovel = () => { + const novel = useSelector((state) => state.novelReducer); + + return novel; +}; + const findNovel = (novelId) => { let novel = useSelector((state) => state.preferenceReducer.novelSettings); novel = novel.find((novel) => novel.novelId === novelId); @@ -74,5 +80,5 @@ export { useSettings, usePreferences, useContinueReading, - findNovel, + useNovel, }; diff --git a/src/Navigators/Main.js b/src/Navigators/Main.js index 71813ca5..c155f597 100644 --- a/src/Navigators/Main.js +++ b/src/Navigators/Main.js @@ -16,6 +16,7 @@ import { View } from "react-native"; import { useTheme } from "../Hooks/reduxHooks"; import { githubUpdateChecker } from "../Hooks/githubUpdateChecker"; import NewUpdateDialog from "../Components/NewUpdateDialog"; +import { NavigationContainer } from "@react-navigation/native"; const Stack = createStackNavigator(); @@ -29,34 +30,39 @@ const MainNavigator = () => { const { isNewVersion, latestRelease } = githubUpdateChecker() || {}; return ( - - {isNewVersion && } - - - - - - - - - + + + {isNewVersion && } + + + + + + + + + + ); }; diff --git a/src/Screens/Browse/GlobalSearch.js b/src/Screens/Browse/GlobalSearch.js index 63db358f..90a3a167 100644 --- a/src/Screens/Browse/GlobalSearch.js +++ b/src/Screens/Browse/GlobalSearch.js @@ -114,12 +114,18 @@ const GlobalSearch = ({ navigation }) => { renderItem={renderItem} extraData={pinned} ListEmptyComponent={ - !loading && ( - - ) + <> + {!loading && ( + + )} + } ListFooterComponent={ loading && ( diff --git a/src/Screens/History/History.js b/src/Screens/History/History.js index f107a65f..c708d3d1 100644 --- a/src/Screens/History/History.js +++ b/src/Screens/History/History.js @@ -27,6 +27,7 @@ const History = ({ const renderHistoryCard = ({ item }) => ( diff --git a/src/Screens/History/components/HistoryCard.js b/src/Screens/History/components/HistoryCard.js index 03a1fc1a..86fca2fb 100644 --- a/src/Screens/History/components/HistoryCard.js +++ b/src/Screens/History/components/HistoryCard.js @@ -7,9 +7,7 @@ import moment from "moment"; import { useSelector } from "react-redux"; -const HistoryCard = ({ item, deleteHistoryAction, navigation }) => { - const theme = useSelector((state) => state.settingsReducer.theme); - +const HistoryCard = ({ item, deleteHistoryAction, navigation, theme }) => { return ( { data={novels} renderItem={renderItem} refreshControl={refreshControl()} - ListEmptyComponent={listEmptyComponent()} + ListEmptyComponent={listEmptyComponent} /> )} diff --git a/src/Screens/Library/components/BottomSheet.js b/src/Screens/Library/components/LibraryFilterSheet.js similarity index 100% rename from src/Screens/Library/components/BottomSheet.js rename to src/Screens/Library/components/LibraryFilterSheet.js diff --git a/src/Screens/More/About.js b/src/Screens/More/About.js index 289fef94..93ae44de 100644 --- a/src/Screens/More/About.js +++ b/src/Screens/More/About.js @@ -17,12 +17,12 @@ const AboutScreen = ({ navigation }) => { setDisplayModalVisible(true); @@ -116,14 +119,13 @@ const SettingsScreen = ({ dispatch(restoreLibraryAction())} theme={theme} /> diff --git a/src/Screens/Novel/Novel.js b/src/Screens/Novel/Novel.js index bb7e2211..b0677ac1 100644 --- a/src/Screens/Novel/Novel.js +++ b/src/Screens/Novel/Novel.js @@ -3,15 +3,16 @@ import { StyleSheet, View, FlatList, RefreshControl } from "react-native"; import { Provider, Portal } from "react-native-paper"; import { useContinueReading, + useNovel, usePreferences, useTheme, } from "../../Hooks/reduxHooks"; -import ChapterCard from "./components/ChapterCard"; +import ChapterItem from "./components/ChapterItem"; import NovelInfoHeader from "./components/NovelHeader"; import ChaptersSettingsSheet from "./components/ChaptersSettingsSheet"; -import { connect } from "react-redux"; +import { useDispatch } from "react-redux"; import { getNovelAction, @@ -21,16 +22,7 @@ import { import TrackSheet from "./components/Tracker/TrackSheet"; import { showToast } from "../../Hooks/showToast"; -const Novel = ({ - route, - novel, - chapters, - loading, - getNovelAction, - fetching, - sortAndFilterChapters, - updateNovelAction, -}) => { +const Novel = ({ route, navigation }) => { const item = route.params; const { sourceId, @@ -42,6 +34,8 @@ const Novel = ({ } = item; const theme = useTheme(); + const dispatch = useDispatch(); + const { novel, chapters, loading, fetching } = useNovel(); let chaptersSettingsSheetRef = useRef(null); let trackerSheetRef = useRef(null); @@ -56,19 +50,49 @@ const Novel = ({ ); useEffect(() => { - getNovelAction(followed, sourceId, novelUrl, novelId, sort, filter); + dispatch( + getNovelAction(followed, sourceId, novelUrl, novelId, sort, filter) + ); }, [getNovelAction]); - const onRefresh = async () => { - await updateNovelAction(sourceId, novelUrl, novelId); + const onRefresh = () => { + dispatch(updateNovelAction(sourceId, novelUrl, novelId)); showToast(`Updated ${novelName}`); }; - const renderChapterCard = ({ item }) => ( - ( + + ); + + const ListHeaderComponent = () => ( + + ); + + const renderItem = ({ item }) => ( + ); @@ -87,45 +111,25 @@ const Novel = ({ maxToRenderPerBatch={5} windowSize={15} initialNumToRender={7} - renderItem={renderChapterCard} - ListHeaderComponent={ - - } - refreshControl={ - - } + renderItem={renderItem} + ListHeaderComponent={ListHeaderComponent} + refreshControl={refreshControl()} /> {!loading && ( @@ -134,18 +138,8 @@ const Novel = ({ ); }; -const mapStateToProps = (state) => ({ - novel: state.novelReducer.novel, - chapters: state.novelReducer.chapters, - loading: state.novelReducer.loading, - fetching: state.novelReducer.fetching, -}); -export default connect(mapStateToProps, { - getNovelAction, - sortAndFilterChapters, - updateNovelAction, -})(Novel); +export default Novel; const styles = StyleSheet.create({ container: { flex: 1 }, diff --git a/src/Screens/Novel/components/ChapterCard.js b/src/Screens/Novel/components/ChapterItem.js similarity index 80% rename from src/Screens/Novel/components/ChapterCard.js rename to src/Screens/Novel/components/ChapterItem.js index cc78c02e..2ea983f9 100644 --- a/src/Screens/Novel/components/ChapterCard.js +++ b/src/Screens/Novel/components/ChapterItem.js @@ -7,19 +7,15 @@ import { deleteChapterAction, downloadChapterAction, } from "../../../redux/novel/novel.actions"; -import { connect } from "react-redux"; -const ChapterCard = ({ +const ChapterItem = ({ novelUrl, chapter, - extensionId, + sourceId, + dispatch, theme, - // downloading, - downloadChapterAction, - deleteChapterAction, + navigation, }) => { - const navigation = useNavigation(); - const [visible, setVisible] = useState(false); const openMenu = () => setVisible(true); const closeMenu = () => setVisible(false); @@ -52,9 +48,11 @@ const ChapterCard = ({ > - deleteChapterAction( - chapter.chapterId, - chapter.chapterName + dispatch( + deleteChapterAction( + chapter.chapterId, + chapter.chapterName + ) ) } title="Delete" @@ -69,16 +67,17 @@ const ChapterCard = ({ animated color={theme.textColorHint} size={25} - onPress={() => { - downloadChapterAction( - extensionId, - novelUrl, - chapter.chapterUrl, - chapter.chapterName, - chapter.chapterId - ); - // console.log(downloading); - }} + onPress={() => + dispatch( + downloadChapterAction( + sourceId, + novelUrl, + chapter.chapterUrl, + chapter.chapterName, + chapter.chapterId + ) + ) + } style={{ margin: 2 }} /> ); @@ -92,7 +91,7 @@ const ChapterCard = ({ navigation.navigate("Chapter", { chapterId: chapter.chapterId, chapterUrl: chapter.chapterUrl, - extensionId, + extensionId: sourceId, novelUrl: novelUrl, chapterName: chapter.chapterName, novelId: chapter.novelId, @@ -139,15 +138,7 @@ const ChapterCard = ({ ); }; -const mapStateToProps = (state) => ({ - theme: state.settingsReducer.theme, - // downloading: state.novelReducer.downloading, -}); - -export default connect(mapStateToProps, { - downloadChapterAction, - deleteChapterAction, -})(ChapterCard); +export default ChapterItem; const styles = StyleSheet.create({ chapterCardContainer: { diff --git a/src/Screens/Novel/components/ChaptersSettingsSheet.js b/src/Screens/Novel/components/ChaptersSettingsSheet.js index 1a1a38dd..d80c6546 100644 --- a/src/Screens/Novel/components/ChaptersSettingsSheet.js +++ b/src/Screens/Novel/components/ChaptersSettingsSheet.js @@ -9,6 +9,7 @@ const ChaptersSettingsSheet = ({ bottomSheetRef, novelId, sortAndFilterChapters, + dispatch, savedSort, savedFilter, }) => { @@ -17,12 +18,12 @@ const ChaptersSettingsSheet = ({ const sortChapters = (val) => { setSort(val); - sortAndFilterChapters(novelId, val, filter); + dispatch(sortAndFilterChapters(novelId, val, filter)); }; const filterChapters = (val) => { setFilter(val); - sortAndFilterChapters(novelId, sort, val); + dispatch(sortAndFilterChapters(novelId, sort, val)); }; const checkBoxStyle = { flexDirection: "row", alignItems: "center" }; diff --git a/src/Screens/Novel/components/NovelHeader.js b/src/Screens/Novel/components/NovelHeader.js index ca394894..82159309 100644 --- a/src/Screens/Novel/components/NovelHeader.js +++ b/src/Screens/Novel/components/NovelHeader.js @@ -18,18 +18,17 @@ import ReadButton from "./Info/ReadButton"; const NovelInfoHeader = ({ item, - loading, novel, - chapters, theme, - chaptersSettingsSheetRef, - trackerSheetRef, filter, + loading, + dispatch, + chapters, lastRead, + navigation, + trackerSheetRef, + chaptersSettingsSheetRef, }) => { - const navigation = useNavigation(); - const dispatch = useDispatch(); - const [downloadMenu, showDownloadMenu] = useState(false); const tracker = useSelector((state) => state.trackerReducer.tracker); diff --git a/src/Screens/Updates/Updates.js b/src/Screens/Updates/Updates.js index ba61f432..56957fba 100644 --- a/src/Screens/Updates/Updates.js +++ b/src/Screens/Updates/Updates.js @@ -6,25 +6,26 @@ import { ActivityIndicator, RefreshControl, } from "react-native"; -import { connect, useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import EmptyView from "../../Components/EmptyView"; import { getUpdatesAction, updateLibraryAction, } from "../../redux/updates/updates.actions"; import { Appbar } from "./components/Appbar"; -import UpdateCard from "./components/UpdateCard"; +import UpdatesItem from "./components/UpdatesItem"; import { useTheme } from "../../Hooks/reduxHooks"; -const Updates = ({ updates, loading, getUpdatesAction }) => { +const Updates = ({ navigation }) => { const theme = useTheme(); const dispatch = useDispatch(); + const { updates, loading } = useSelector((state) => state.updatesReducer); const [refreshing, setRefreshing] = useState(false); useEffect(() => { - getUpdatesAction(); - }, [getUpdatesAction]); + dispatch(getUpdatesAction()); + }, [getUpdatesAction, updates]); const onRefresh = async () => { setRefreshing(true); @@ -32,11 +33,40 @@ const Updates = ({ updates, loading, getUpdatesAction }) => { setRefreshing(false); }; - const renderUpdateCard = ({ item }) => ; + const onPress = (item) => + navigation.navigate("Chapter", { + chapterId: item.chapterId, + chapterUrl: item.chapterUrl, + extensionId: item.sourceId, + novelUrl: item.novelUrl, + chapterName: item.chapterName, + novelId: item.novelId, + }); + + const renderItem = ({ item }) => ( + onPress(item)} /> + ); + + const ListFooterComponent = () => + loading && ; + + const ListEmptyComponent = () => + !loading && ( + + ); + + const refreshControl = () => ( + + ); return ( <> - + { ]} > item.updateId.toString()} - renderItem={renderUpdateCard} - ListFooterComponent={ - loading && ( - - ) - } - ListEmptyComponent={ - !loading && ( - - ) - } - refreshControl={ - - } + renderItem={renderItem} + ListFooterComponent={ListFooterComponent()} + ListEmptyComponent={ListEmptyComponent()} + refreshControl={refreshControl()} /> ); }; -const mapStateToProps = (state) => ({ - updates: state.updatesReducer.updates, - loading: state.updatesReducer.loading, -}); - -export default connect(mapStateToProps, { getUpdatesAction })(Updates); +export default Updates; const styles = StyleSheet.create({ container: { flex: 1 }, + flatList: { flexGrow: 1, paddingVertical: 8 }, }); diff --git a/src/Screens/Updates/components/Appbar.js b/src/Screens/Updates/components/Appbar.js index 13d7a828..64d214ac 100644 --- a/src/Screens/Updates/components/Appbar.js +++ b/src/Screens/Updates/components/Appbar.js @@ -1,22 +1,17 @@ import React from "react"; import { Appbar as MaterialAppbar } from "react-native-paper"; -import { useDispatch } from "react-redux"; import { updateLibraryAction } from "../../../redux/updates/updates.actions"; -export const Appbar = ({ theme }) => { - const dispatch = useDispatch(); - - return ( - - - dispatch(updateLibraryAction())} - /> - - ); -}; +export const Appbar = ({ theme, dispatch }) => ( + + + dispatch(updateLibraryAction())} + /> + +); diff --git a/src/Screens/Updates/components/UpdateCard.js b/src/Screens/Updates/components/UpdateCard.js deleted file mode 100644 index cb8c9217..00000000 --- a/src/Screens/Updates/components/UpdateCard.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from "react"; -import { View, Text, Image, StyleSheet } from "react-native"; -import { TouchableRipple, Button } from "react-native-paper"; -import moment from "moment"; - -import { useNavigation } from "@react-navigation/native"; -import { useSelector } from "react-redux"; - -const UpdateCard = ({ item }) => { - const navigation = useNavigation(); - - const theme = useSelector((state) => state.settingsReducer.theme); - - return ( - - // console.log(item); - navigation.navigate("Chapter", { - chapterId: item.chapterId, - chapterUrl: item.chapterUrl, - extensionId: item.sourceId, - novelUrl: item.novelUrl, - chapterName: item.chapterName, - novelId: item.novelId, - }) - } - rippleColor={theme.rippleColor} - borderless - > - <> - - - - - {item.novelName} - - - - {`${item.chapterName} · ${moment( - item.updateTime - ).calendar()}`} - - - - - - - ); -}; - -export default UpdateCard; - -const styles = StyleSheet.create({ - updateCard: { - flexDirection: "row", - alignItems: "center", - paddingVertical: 8, - paddingHorizontal: 12, - // borderRadius: 4, - }, - updateIcon: { - width: 42, - height: 42, - borderRadius: 4, - }, - extensionDetails: { - flex: 1, - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginLeft: 16, - }, -}); diff --git a/src/Screens/Updates/components/UpdatesItem.js b/src/Screens/Updates/components/UpdatesItem.js new file mode 100644 index 00000000..ee69383b --- /dev/null +++ b/src/Screens/Updates/components/UpdatesItem.js @@ -0,0 +1,65 @@ +import React from "react"; +import { View, Text, Image, StyleSheet } from "react-native"; +import { TouchableRipple } from "react-native-paper"; +import moment from "moment"; + +const UpdateCard = ({ item, theme, onPress }) => { + return ( + + <> + + + + {item.novelName} + + + + {`${item.chapterName} · ${moment( + item.updateTime + ).calendar()}`} + + + + + + ); +}; + +export default UpdateCard; + +const styles = StyleSheet.create({ + updateCard: { + flexDirection: "row", + alignItems: "center", + paddingVertical: 8, + paddingHorizontal: 12, + }, + updateIcon: { + width: 42, + height: 42, + borderRadius: 4, + }, + chapterDetails: { + flex: 1, + marginLeft: 16, + }, +}); diff --git a/src/Services/Source/source.js b/src/Services/Source/source.js index 4ebc4d5d..44c2e672 100644 --- a/src/Services/Source/source.js +++ b/src/Services/Source/source.js @@ -3,13 +3,13 @@ export const fetchSources = async () => { let res = await fetch(url); let sources = await res.json(); - // console.log(sources); + return sources; }; export const fetchNovel = async (sourceId, novelUrl) => { const url = `https://lnreader-extensions.vercel.app/api/${sourceId}/novel/${novelUrl}`; - // console.log(url); + let data = await fetch(url); let res = await data.json(); @@ -35,8 +35,6 @@ export const fetchNovel = async (sourceId, novelUrl) => { export const fetchChapter = async (sourceId, novelUrl, chapterUrl) => { const url = `https://lnreader-extensions.vercel.app/api/${sourceId}/novel/${novelUrl}${chapterUrl}`; - // console.log(url); - let res = await fetch(url); let chapter = await res.json(); diff --git a/src/Services/backup.js b/src/Services/backup.js index 5652996d..c1e0d850 100644 --- a/src/Services/backup.js +++ b/src/Services/backup.js @@ -1,6 +1,4 @@ import * as DocumentPicker from "expo-document-picker"; -import { Platform } from "react-native"; -import * as FileSystem from "expo-file-system"; import { StorageAccessFramework } from "expo-file-system"; import { getLibrary } from "../Database/queries/LibraryQueries"; diff --git a/src/Services/updates.js b/src/Services/updates.js index 1eeed336..6d2559dc 100644 --- a/src/Services/updates.js +++ b/src/Services/updates.js @@ -3,7 +3,6 @@ const db = SQLite.openDatabase("lnreader.db"); import { getLibrary } from "../Database/queries/LibraryQueries"; import { fetchChapters, fetchNovel } from "../Services/Source/source"; -import { ToastAndroid } from "react-native"; const updateNovelDetails = async (novel, novelId) => { db.transaction((tx) => { @@ -27,7 +26,7 @@ const updateNovelDetails = async (novel, novelId) => { export const updateNovel = async (extensionId, novelUrl, novelId) => { let novel = await fetchNovel(extensionId, novelUrl); - // console.log(novel); + await updateNovelDetails(novel, novelId); db.transaction((tx) => { @@ -89,14 +88,7 @@ export const updateNovelChapters = async (extensionId, novelUrl, novelId) => { tx.executeSql( "INSERT OR IGNORE INTO updates (chapterId, novelId, updateTime) values (?, ?, (datetime('now','localtime')))", [insertId, novelId], - (txObj, res) => { - // console.log( - // "Inserted Chapter Id -> " + - // insertId + - // " Novel Id " + - // novelId - // ); - }, + (txObj, res) => {}, (txObj, error) => console.log("Error ", error) ); } diff --git a/src/Theme/fonts.js b/src/Theme/fonts.js new file mode 100644 index 00000000..694626c7 --- /dev/null +++ b/src/Theme/fonts.js @@ -0,0 +1,13 @@ +/** + * Reader fonts + */ + +export const fonts = { + "pt-sans-bold": require("../../assets/fonts/PTSansNarrow-Bold.ttf"), + "arbutus-slab": require("../../assets/fonts/ArbutusSlab-Regular.ttf"), + domine: require("../../assets/fonts/Domine-VariableFont_wght.ttf"), + lato: require("../../assets/fonts/Lato-Regular.ttf"), + "noto-sans": require("../../assets/fonts/NotoSans-Regular.ttf"), + "open-sans": require("../../assets/fonts/OpenSans-Regular.ttf"), + "pt-serif": require("../../assets/fonts/PTSerif-Regular.ttf"), +}; diff --git a/src/redux/library/library.actions.js b/src/redux/library/library.actions.js index db4240e1..2594ea4f 100644 --- a/src/redux/library/library.actions.js +++ b/src/redux/library/library.actions.js @@ -6,6 +6,7 @@ import { getLibrary, searchLibrary, } from "../../Database/queries/LibraryQueries"; +import { restoreBackup } from "../../Services/backup"; export const getLibraryAction = () => async (dispatch) => { const res = await getLibrary(); @@ -24,3 +25,14 @@ export const searchLibraryAction = (searchText) => async (dispatch) => { payload: res, }); }; + +export const restoreLibraryAction = () => async (dispatch) => { + await restoreBackup(); + + const res = await getLibrary(); + + dispatch({ + type: GET_LIBRARY_NOVELS, + payload: res, + }); +};