Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 381c779c36 | |||
| 90df6b5aca | |||
| 87ff17d5e5 | |||
| 913d319222 | |||
| cca143383e | |||
| 4a874b30be | |||
| e958b0fa09 | |||
| a484d550a8 | |||
| 4acafdb741 | |||
| 1e6c4758ff |
@@ -1,19 +1,16 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import React, { useEffect } from "react";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import * as Font from "expo-font";
|
||||
import AppLoading from "expo-app-loading";
|
||||
import { useFonts } from "expo-font";
|
||||
import { Provider as PaperProvider } from "react-native-paper";
|
||||
import Main from "./src/Navigators/Main";
|
||||
|
||||
import Router from "./src/navigation/Router";
|
||||
|
||||
// Redux
|
||||
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";
|
||||
|
||||
const getFonts = () =>
|
||||
Font.loadAsync({
|
||||
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"),
|
||||
@@ -23,33 +20,23 @@ const getFonts = () =>
|
||||
"pt-serif": require("./assets/fonts/PTSerif-Regular.ttf"),
|
||||
});
|
||||
|
||||
const App = () => {
|
||||
const [fontsLoaded, setFontsLoaded] = useState(false);
|
||||
useEffect(() => createDB(), []);
|
||||
|
||||
useEffect(() => {
|
||||
createDB();
|
||||
}, []);
|
||||
|
||||
if (fontsLoaded) {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<StatusBar />
|
||||
<NavigationContainer>
|
||||
<Router />
|
||||
</NavigationContainer>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<AppLoading
|
||||
startAsync={getFonts}
|
||||
onError={() => warn(error)}
|
||||
onFinish={() => setFontsLoaded(true)}
|
||||
/>
|
||||
);
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<PaperProvider>
|
||||
<NavigationContainer>
|
||||
<Main />
|
||||
</NavigationContainer>
|
||||
</PaperProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "LNReader",
|
||||
"slug": "LNReader",
|
||||
"scheme": "lnreader",
|
||||
"version": "1.0.15",
|
||||
"version": "1.0.16",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"splash": {
|
||||
|
||||
Generated
+2369
-1795
File diff suppressed because it is too large
Load Diff
+22
-20
@@ -8,49 +8,51 @@
|
||||
"eject": "expo eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-async-storage/async-storage": "^1.13.2",
|
||||
"@react-native-async-storage/async-storage": "^1.13.0",
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-native-community/netinfo": "^5.9.7",
|
||||
"@react-native-community/slider": "^3.0.3",
|
||||
"@react-native-community/netinfo": "6.0.0",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@react-navigation/material-bottom-tabs": "^5.3.10",
|
||||
"@react-navigation/native": "^5.8.10",
|
||||
"@react-navigation/stack": "^5.12.8",
|
||||
"expo": "~40.0.0",
|
||||
"expo": "^41.0.1",
|
||||
"expo-app-loading": "^1.0.1",
|
||||
"expo-font": "^8.4.0",
|
||||
"expo-linear-gradient": "^8.4.0",
|
||||
"expo-sharing": "~8.5.0",
|
||||
"expo-sqlite": "~8.5.0",
|
||||
"expo-status-bar": "~1.0.3",
|
||||
"expo-web-browser": "~8.6.0",
|
||||
"expo-document-picker": "^9.1.2",
|
||||
"expo-file-system": "~11.0.2",
|
||||
"expo-font": "~9.1.0",
|
||||
"expo-linear-gradient": "~9.1.0",
|
||||
"expo-linking": "~2.2.3",
|
||||
"expo-media-library": "~12.0.2",
|
||||
"expo-permissions": "^12.0.1",
|
||||
"expo-sharing": "~9.1.2",
|
||||
"expo-sqlite": "~9.1.0",
|
||||
"expo-status-bar": "~1.0.4",
|
||||
"expo-web-browser": "~9.1.0",
|
||||
"moment": "^2.29.1",
|
||||
"qs": "^6.10.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
|
||||
"react-native-collapsible-header-views": "^1.1.2",
|
||||
"react-native-gesture-handler": "^1.8.0",
|
||||
"react-native-inappbrowser-reborn": "^3.5.1",
|
||||
"react-native-gesture-handler": "~1.10.2",
|
||||
"react-native-offline": "^5.7.0",
|
||||
"react-native-paper": "^4.5.0",
|
||||
"react-native-picker-scrollview": "^1.0.1",
|
||||
"react-native-reanimated": "^1.13.2",
|
||||
"react-native-safe-area-context": "3.1.9",
|
||||
"react-native-screens": "~2.15.0",
|
||||
"react-native-reanimated": "~2.1.0",
|
||||
"react-native-safe-area-context": "3.2.0",
|
||||
"react-native-screens": "~3.0.0",
|
||||
"react-native-vector-icons": "^7.1.0",
|
||||
"react-native-web": "~0.13.12",
|
||||
"react-native-webview": "11.0.0",
|
||||
"react-native-webview": "11.2.3",
|
||||
"react-redux": "^7.2.2",
|
||||
"redux": "^4.0.5",
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rn-sliding-up-panel": "^2.4.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.9.0",
|
||||
"@types/node": "^14.14.35"
|
||||
"@babel/core": "~7.9.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export const githubUpdateChecker = () => {
|
||||
const latestReleaseUrl =
|
||||
"https://api.github.com/repos/rajarsheechatterjee/lnreader/releases/latest";
|
||||
|
||||
const [checking, setChecking] = useState(true);
|
||||
const [latestRelease, setLatestRelease] = useState();
|
||||
|
||||
const checkForRelease = async () => {
|
||||
let res = await fetch(latestReleaseUrl);
|
||||
res = await res.json();
|
||||
|
||||
const release = {
|
||||
tag_name: res.tag_name,
|
||||
body: res.body,
|
||||
downloadUrl: res.assets[0].browser_download_url,
|
||||
};
|
||||
|
||||
setLatestRelease(release);
|
||||
setChecking(false);
|
||||
};
|
||||
|
||||
const isNewVersion = (versionTag) => {
|
||||
let currentVersion = "v1.0.16";
|
||||
const regex = /[^\\d.]/;
|
||||
|
||||
let newVersion = versionTag.replace(regex, "");
|
||||
currentVersion = currentVersion.replace(regex, "");
|
||||
|
||||
return newVersion !== currentVersion;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkForRelease();
|
||||
}, []);
|
||||
|
||||
if (!checking) {
|
||||
const data = {
|
||||
latestRelease,
|
||||
isNewVersion: isNewVersion(latestRelease.tag_name),
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { setStatusBarStyle as setExpoStatusBarStyle } from "expo-status-bar";
|
||||
import { useTheme } from "../Hooks/useTheme";
|
||||
|
||||
export const setStatusBarStyle = () => {
|
||||
const theme = useTheme();
|
||||
setExpoStatusBarStyle(theme.id === 1 ? "dark" : "light");
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ToastAndroid } from "react-native";
|
||||
|
||||
export const showToast = (message) => {
|
||||
ToastAndroid.show(message, ToastAndroid.SHORT);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export const useLibrary = () => {
|
||||
const library = useSelector((state) => state.libraryReducer.novels);
|
||||
|
||||
return library;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export const useReaderSettings = () => {
|
||||
const readerSettings = useSelector((state) => state.settingsReducer.reader);
|
||||
|
||||
return readerSettings;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export const useTheme = () => {
|
||||
const currentTheme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
return currentTheme;
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from "react";
|
||||
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useTheme } from "../Hooks/useTheme";
|
||||
|
||||
import Library from "../Screens/Library/Library";
|
||||
import Updates from "../Screens/Updates/Updates";
|
||||
import History from "../Screens/History/History";
|
||||
import Browse from "../Screens/Browse/Browse";
|
||||
import More from "../Screens/More/More";
|
||||
|
||||
const Tab = createMaterialBottomTabNavigator();
|
||||
|
||||
const BottomNavigator = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
shifting={false}
|
||||
barStyle={{ backgroundColor: theme.colorPrimary }}
|
||||
activeColor={theme.colorAccentDark}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Library"
|
||||
component={Library}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="book-variant-multiple"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Updates"
|
||||
component={Updates}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="alert-decagram-outline"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="History"
|
||||
component={History}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="history"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Browse"
|
||||
component={Browse}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="compass-outline"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="More"
|
||||
component={More}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="dots-horizontal"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
export default BottomNavigator;
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from "react";
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
} from "@react-navigation/stack";
|
||||
|
||||
import BottomNavigator from "./BottomNavigator";
|
||||
import Novel from "../Screens/Novel/Novel";
|
||||
import Chapter from "../Screens/Chapter/Chapter";
|
||||
import Extension from "../Screens/Extension/Extension";
|
||||
import MoreStack from "./More";
|
||||
|
||||
import { setStatusBarStyle } from "../Hooks/setStatusBarStyle";
|
||||
import { View } from "react-native";
|
||||
import { useTheme } from "../Hooks/useTheme";
|
||||
import { githubUpdateChecker } from "../Hooks/githubUpdateChecker";
|
||||
import NewUpdateDialog from "../Components/NewUpdateDialog";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
const stackNavigatorConfig = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
const MainNavigator = () => {
|
||||
const theme = useTheme();
|
||||
setStatusBarStyle();
|
||||
const { isNewVersion, latestRelease } = githubUpdateChecker() || {};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: theme.colorPrimary }}>
|
||||
{isNewVersion && <NewUpdateDialog newVersion={latestRelease} />}
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
<Stack.Screen
|
||||
name="BottomNavigator"
|
||||
component={BottomNavigator}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Novel"
|
||||
component={Novel}
|
||||
options={{
|
||||
headerTitle: "",
|
||||
headerShown: true,
|
||||
headerTransparent: true,
|
||||
headerTintColor: "white",
|
||||
...TransitionPresets.RevealFromBottomAndroid,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Chapter"
|
||||
component={Chapter}
|
||||
options={{ ...TransitionPresets.SlideFromRightIOS }}
|
||||
/>
|
||||
<Stack.Screen name="MoreStack" component={MoreStack} />
|
||||
<Stack.Screen name="Extension" component={Extension} />
|
||||
</Stack.Navigator>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainNavigator;
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import { createStackNavigator } from "@react-navigation/stack";
|
||||
|
||||
import About from "../Screens/More/About";
|
||||
import Settings from "../Screens/More/Settings";
|
||||
import Trackers from "../Screens/More/Trackers";
|
||||
import { View } from "react-native";
|
||||
import { useTheme } from "../Hooks/useTheme";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
const stackNavigatorConfig = { headerShown: false };
|
||||
|
||||
const MoreStack = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: theme.colorPrimary }}>
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
<Stack.Screen name="Settings" component={Settings} />
|
||||
<Stack.Screen name="Trackers" component={Trackers} />
|
||||
<Stack.Screen name="About" component={About} />
|
||||
</Stack.Navigator>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreStack;
|
||||
+18
-1
@@ -53,7 +53,24 @@ const Divider = ({ theme }) => (
|
||||
/>
|
||||
);
|
||||
|
||||
export { ListSection, ListSubHeader, ListItem, Divider };
|
||||
const InfoItem = ({ title, description, icon, onPress, theme, right }) => (
|
||||
<List.Item
|
||||
title={title}
|
||||
titleStyle={{ color: theme.textColorSecondary, fontSize: 14 }}
|
||||
titleNumberOfLines={5}
|
||||
left={() =>
|
||||
icon && (
|
||||
<List.Icon
|
||||
color={theme.textColorSecondary}
|
||||
icon={icon}
|
||||
style={{ marginVertical: 0 }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export { ListSection, ListSubHeader, ListItem, Divider, InfoItem };
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
listSection: {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import React, { useState } from "react";
|
||||
import { StyleSheet, View, Text } from "react-native";
|
||||
import { Portal, Modal, Button } from "react-native-paper";
|
||||
import * as Linking from "expo-linking";
|
||||
import { useTheme } from "../Hooks/useTheme";
|
||||
|
||||
const NewUpdateDialog = ({ newVersion }) => {
|
||||
const [newUpdateDialog, showNewUpdateDialog] = useState(true);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={newUpdateDialog}
|
||||
onDismiss={() => showNewUpdateDialog(false)}
|
||||
contentContainerStyle={[
|
||||
styles.containerStyle,
|
||||
{ backgroundColor: theme.colorPrimaryDark },
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.modalHeader,
|
||||
{ color: theme.textColorPrimary },
|
||||
]}
|
||||
>
|
||||
{`New update available ${newVersion.tag_name}`}
|
||||
</Text>
|
||||
<Text style={{ color: theme.textColorSecondary, fontSize: 16 }}>
|
||||
{newVersion.body}
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginTop: 30,
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
labelStyle={{
|
||||
color: theme.colorAccentDark,
|
||||
letterSpacing: 0,
|
||||
textTransform: "none",
|
||||
}}
|
||||
theme={{ colors: { primary: theme.colorAccentDark } }}
|
||||
onPress={() => showNewUpdateDialog(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
labelStyle={{
|
||||
color: theme.colorAccentDark,
|
||||
letterSpacing: 0,
|
||||
textTransform: "none",
|
||||
}}
|
||||
theme={{ colors: { primary: theme.colorAccentDark } }}
|
||||
onPress={() => Linking.openURL(newVersion.downloadUrl)}
|
||||
>
|
||||
Install
|
||||
</Button>
|
||||
</View>
|
||||
</Modal>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewUpdateDialog;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
containerStyle: {
|
||||
padding: 20,
|
||||
margin: 20,
|
||||
borderRadius: 6,
|
||||
},
|
||||
modalHeader: {
|
||||
fontSize: 20,
|
||||
marginBottom: 10,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import React, { useRef } from "react";
|
||||
import { View, TextInput, StyleSheet } from "react-native";
|
||||
import { TouchableRipple, IconButton } from "react-native-paper";
|
||||
import Constants from "expo-constants";
|
||||
|
||||
export const Searchbar = ({
|
||||
theme,
|
||||
placeholder,
|
||||
searchText,
|
||||
left,
|
||||
onPressLeft,
|
||||
right,
|
||||
onPressRight,
|
||||
clearSearchbar,
|
||||
onChangeText,
|
||||
onSubmitEditing,
|
||||
}) => {
|
||||
const searchRef = useRef(null);
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
borderless
|
||||
onPress={() => searchRef.current.focus()}
|
||||
style={[
|
||||
styles.searchAppbarContainer,
|
||||
{ backgroundColor: theme.searchBarColor },
|
||||
]}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<View style={{ flex: 1, flexDirection: "row" }}>
|
||||
{left && (
|
||||
<IconButton
|
||||
icon={left}
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginLeft: 0 }}
|
||||
size={23}
|
||||
onPress={onPressLeft}
|
||||
/>
|
||||
)}
|
||||
<TextInput
|
||||
ref={searchRef}
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: theme.textColorSecondary,
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={theme.textColorSecondary}
|
||||
onChangeText={onChangeText}
|
||||
onSubmitEditing={onSubmitEditing}
|
||||
defaultValue={searchText}
|
||||
/>
|
||||
</View>
|
||||
{searchText !== "" && (
|
||||
<IconButton
|
||||
icon="close"
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginRight: 0 }}
|
||||
size={23}
|
||||
onPress={clearSearchbar}
|
||||
/>
|
||||
)}
|
||||
{right && (
|
||||
<IconButton
|
||||
icon="earth"
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginRight: 0 }}
|
||||
size={23}
|
||||
onPress={onPressRight}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
searchAppbarContainer: {
|
||||
marginTop: Constants.statusBarHeight + 8,
|
||||
height: 48,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 12,
|
||||
borderRadius: 8,
|
||||
marginHorizontal: 12,
|
||||
elevation: 2,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
import { fetchChapters } from "../../Services/Source/source";
|
||||
import { insertChapters } from "./ChapterQueries";
|
||||
|
||||
const insertNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
export const insertNovel = async (novel) => {
|
||||
@@ -85,3 +88,38 @@ export const deleteNovelCache = () => {
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const restoreFromBackupQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre, followed, unread) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
export const restoreLibrary = async (novel) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) =>
|
||||
tx.executeSql(
|
||||
restoreFromBackupQuery,
|
||||
[
|
||||
novel.novelUrl,
|
||||
novel.sourceUrl,
|
||||
novel.sourceId,
|
||||
novel.source,
|
||||
novel.novelName,
|
||||
novel.novelCover,
|
||||
novel.novelSummary,
|
||||
novel.author,
|
||||
novel.artist,
|
||||
novel.status,
|
||||
novel.genre,
|
||||
novel.followed,
|
||||
novel.unread,
|
||||
],
|
||||
async (txObj, { insertId }) => {
|
||||
const chapters = await fetchChapters(
|
||||
novel.sourceId,
|
||||
novel.novelUrl
|
||||
);
|
||||
await insertChapters(insertId, chapters);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
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";
|
||||
|
||||
import Library from "../screens/Library/Library";
|
||||
import Updates from "../screens/Updates/Updates";
|
||||
import History from "../screens/History/History";
|
||||
import Browse from "../screens/Browse/Browse";
|
||||
|
||||
import More from "../screens/More/More";
|
||||
import About from "../screens/More/About";
|
||||
import Settings from "../screens/More/Settings";
|
||||
import Trackers from "../screens/More/Trackers";
|
||||
|
||||
import NovelItem from "../screens/Novel/Novel";
|
||||
import ChapterItem from "../screens/Chapter/Chapter";
|
||||
|
||||
import Extension from "../screens/Extension/Extension";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
const Tab = createMaterialBottomTabNavigator();
|
||||
|
||||
const stackNavigatorConfig = {
|
||||
headerShown: false,
|
||||
...TransitionPresets.RevealFromBottomAndroid,
|
||||
};
|
||||
|
||||
const SettingsStack = () => {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
<Stack.Screen name="About" component={About} />
|
||||
<Stack.Screen name="Settings" component={Settings} />
|
||||
<Stack.Screen name="Trackers" component={Trackers} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const BottomNavigator = () => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
shifting={false}
|
||||
barStyle={{ backgroundColor: theme.colorPrimary }}
|
||||
activeColor={theme.colorAccentDark}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="My Library"
|
||||
component={Library}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="book-variant-multiple"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tab.Screen
|
||||
name="Updates"
|
||||
component={Updates}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="alert-decagram-outline"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="History"
|
||||
component={History}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="history"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Browse"
|
||||
component={Browse}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="compass-outline"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="More"
|
||||
component={More}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="dots-horizontal"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const Router = () => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
useEffect(() => setStatusBarStyle(theme.id === 1 ? "dark" : "light"), []);
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
<Stack.Screen name="Router" component={BottomNavigator} />
|
||||
<Stack.Screen
|
||||
name="ChapterItem"
|
||||
component={ChapterItem}
|
||||
options={{ ...TransitionPresets.SlideFromRightIOS }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="NovelItem"
|
||||
component={NovelItem}
|
||||
options={{
|
||||
headerTitle: "",
|
||||
headerShown: true,
|
||||
headerTransparent: true,
|
||||
headerTintColor: "white",
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen name="SettingsStack" component={SettingsStack} />
|
||||
<Stack.Screen name="Extension" component={Extension} />
|
||||
{/* <Stack.Screen
|
||||
name="ExtensionStack"
|
||||
component={ExtensionStack}
|
||||
/> */}
|
||||
</Stack.Navigator>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Router;
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
CHAPTER_DELETED,
|
||||
} from "./chapter.types";
|
||||
|
||||
import { fetchChapter } from "../../source/Source";
|
||||
import { fetchChapter } from "../../Services/Source/source";
|
||||
|
||||
import {
|
||||
markChapterRead,
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
UPDATE_NOVEL,
|
||||
} from "./novel.types";
|
||||
|
||||
import { updateNovel } from "../../services/updates";
|
||||
import { fetchChapter, fetchNovel } from "../../source/Source";
|
||||
import { updateNovel } from "../../Services/updates";
|
||||
import { fetchChapter, fetchNovel } from "../../Services/Source/source";
|
||||
import {
|
||||
followNovel,
|
||||
insertNovel,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_SOURCES } from "./source.types";
|
||||
import { fetchSources } from "../../source/Source";
|
||||
import { fetchSources } from "../../Services/Source/source";
|
||||
|
||||
export const getSourcesAction = () => async (dispatch) => {
|
||||
const res = await fetchSources();
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
lightTheme,
|
||||
darkTheme,
|
||||
midnightDuskTheme,
|
||||
} from "../../theme/theme";
|
||||
} from "../../Theme/theme";
|
||||
|
||||
const themes = {
|
||||
0: amoledDarkTheme,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { findListItem, updateItem } from "../../trackers/MyAnimeList";
|
||||
import { findListItem, updateItem } from "../../Services/Trackers/myAnimeList";
|
||||
import {
|
||||
REMOVE_TRACKER,
|
||||
SET_TRACKER,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
UPDATE_CHAPTERS_READ,
|
||||
} from "./tracker.types";
|
||||
|
||||
import { updateMalChaptersRead } from "../../trackers/MyAnimeList";
|
||||
import { updateMalChaptersRead } from "../../Services/Trackers/myAnimeList";
|
||||
|
||||
export const setTracker = (res) => async (dispatch) => {
|
||||
dispatch({
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { GET_UPDATES } from "./updates.types";
|
||||
|
||||
import { getUpdates } from "../../database/queries/UpdateQueries";
|
||||
import { updateAllNovels } from "../../Services/updates";
|
||||
import { showToast } from "../../Hooks/showToast";
|
||||
|
||||
export const getUpdatesAction = () => async (dispatch) => {
|
||||
const updates = await getUpdates();
|
||||
|
||||
dispatch({ type: GET_UPDATES, payload: updates });
|
||||
};
|
||||
|
||||
export const updateLibraryAction = () => async (dispatch) => {
|
||||
showToast("Updating library");
|
||||
|
||||
await updateAllNovels();
|
||||
const updates = await getUpdates();
|
||||
|
||||
dispatch({ type: GET_UPDATES, payload: updates });
|
||||
};
|
||||
|
||||
@@ -1,17 +1,40 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
FlatList,
|
||||
ActivityIndicator,
|
||||
RefreshControl,
|
||||
} from "react-native";
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
import { connect } from "react-redux";
|
||||
import { useTheme } from "../../Hooks/useTheme";
|
||||
|
||||
import { getSourcesAction } from "../../redux/source/source.actions";
|
||||
|
||||
import ExtensionCard from "./components/ExtensionCard";
|
||||
import { showToast } from "../../Hooks/showToast";
|
||||
|
||||
const Browse = ({ extensions, loading, getSourcesAction }) => {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const Browse = ({ theme, extensions, loading, getSourcesAction }) => {
|
||||
useEffect(() => {
|
||||
getSourcesAction();
|
||||
}, [getSourcesAction]);
|
||||
|
||||
const onRefresh = () => {
|
||||
showToast("Updating extension list");
|
||||
setRefreshing(true);
|
||||
getSourcesAction();
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
const renderExtensionCard = ({ item }) => (
|
||||
<ExtensionCard item={item} theme={theme} />
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar title="Browse" />
|
||||
@@ -24,7 +47,7 @@ const Browse = ({ theme, extensions, loading, getSourcesAction }) => {
|
||||
<FlatList
|
||||
data={extensions}
|
||||
keyExtractor={(item) => item.sourceId.toString()}
|
||||
renderItem={({ item }) => <ExtensionCard item={item} />}
|
||||
renderItem={renderExtensionCard}
|
||||
ListEmptyComponent={
|
||||
loading && (
|
||||
<ActivityIndicator
|
||||
@@ -34,6 +57,14 @@ const Browse = ({ theme, extensions, loading, getSourcesAction }) => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={["white"]}
|
||||
progressBackgroundColor={theme.colorAccentDark}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
@@ -41,7 +72,6 @@ const Browse = ({ theme, extensions, loading, getSourcesAction }) => {
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
extensions: state.extensionReducer.extensions,
|
||||
loading: state.extensionReducer.loading,
|
||||
});
|
||||
@@ -49,7 +79,5 @@ const mapStateToProps = (state) => ({
|
||||
export default connect(mapStateToProps, { getSourcesAction })(Browse);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
container: { flex: 1 },
|
||||
});
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import React from "react";
|
||||
import { View, Text, Image, StyleSheet } from "react-native";
|
||||
import { TouchableRipple, Button } from "react-native-paper";
|
||||
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const ExtensionCard = ({ item }) => {
|
||||
const ExtensionCard = ({ item, theme }) => {
|
||||
const { sourceName, sourceCover, sourceLanguage, status } = item;
|
||||
|
||||
const navigation = useNavigation();
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
const navigateToExtension = () =>
|
||||
navigation.navigate("Extension", {
|
||||
sourceId: item.sourceId,
|
||||
sourceName: item.sourceName,
|
||||
sourceUrl: item.sourceUrl,
|
||||
});
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
style={styles.extensionCard}
|
||||
onPress={() =>
|
||||
navigation.navigate("Extension", {
|
||||
sourceId: item.sourceId,
|
||||
sourceName: item.sourceName,
|
||||
sourceUrl: item.sourceUrl,
|
||||
})
|
||||
}
|
||||
onPress={navigateToExtension}
|
||||
rippleColor={theme.rippleColor}
|
||||
borderless
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
@@ -50,31 +46,17 @@ const ExtensionCard = ({ item }) => {
|
||||
>
|
||||
{sourceLanguage}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: "#D63B2F",
|
||||
fontSize: 12,
|
||||
marginLeft: 5,
|
||||
}}
|
||||
>
|
||||
{status === 0 && "(Not Working)"}
|
||||
<Text style={styles.sourceStatus}>
|
||||
{status === 0 && "• Down"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View>
|
||||
<Button
|
||||
labelStyle={{
|
||||
letterSpacing: 0,
|
||||
}}
|
||||
labelStyle={{ letterSpacing: 0 }}
|
||||
uppercase={false}
|
||||
color={theme.colorAccentDark}
|
||||
onPress={() =>
|
||||
navigation.navigate("Extension", {
|
||||
sourceId: item.sourceId,
|
||||
sourceName: item.sourceName,
|
||||
sourceUrl: item.sourceUrl,
|
||||
})
|
||||
}
|
||||
onPress={navigateToExtension}
|
||||
>
|
||||
Browse
|
||||
</Button>
|
||||
@@ -107,4 +89,9 @@ const styles = StyleSheet.create({
|
||||
alignItems: "center",
|
||||
marginLeft: 16,
|
||||
},
|
||||
sourceStatus: {
|
||||
color: "#C14033",
|
||||
fontSize: 12,
|
||||
marginLeft: 5,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "../../redux/novel/novel.actions";
|
||||
|
||||
import { connect, useSelector, useDispatch } from "react-redux";
|
||||
import { parseChapterNumber } from "../../services/updates";
|
||||
import { parseChapterNumber } from "../../Services/updates";
|
||||
import { updateChaptersRead } from "../../redux/tracker/tracker.actions";
|
||||
|
||||
const ChapterItem = ({
|
||||
@@ -96,8 +96,6 @@ const ChapterItem = ({
|
||||
);
|
||||
};
|
||||
|
||||
// !loading && parseChapterNumber(chapter.chapterName);
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<CollapsibleHeaderScrollView
|
||||
@@ -126,7 +124,7 @@ const ChapterItem = ({
|
||||
size={26}
|
||||
disabled={!chapter.prevChapter}
|
||||
onPress={() => {
|
||||
navigation.replace("ChapterItem", {
|
||||
navigation.replace("Chapter", {
|
||||
chapterUrl: chapter.prevChapter,
|
||||
chapterId: chapterId - 1,
|
||||
extensionId,
|
||||
@@ -142,7 +140,7 @@ const ChapterItem = ({
|
||||
size={26}
|
||||
disabled={!chapter.nextChapter}
|
||||
onPress={() => {
|
||||
navigation.replace("ChapterItem", {
|
||||
navigation.replace("Chapter", {
|
||||
chapterUrl: chapter.nextChapter,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Animated, StyleSheet, View, Text, Dimensions } from "react-native";
|
||||
import { ToggleButton, IconButton, Chip } from "react-native-paper";
|
||||
import Slider from "@react-native-community/slider";
|
||||
import Bottomsheet from "rn-sliding-up-panel";
|
||||
import { fonts } from "../../../utils/constants";
|
||||
import { fonts } from "../../../Services/utils/constants";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
updateReaderTextSize,
|
||||
updateReaderTheme,
|
||||
} from "../../../redux/settings/settings.actions";
|
||||
import BottomSheetHandle from "../../../components/BottomSheetHandle";
|
||||
import BottomSheetHandle from "../../../Components/BottomSheetHandle";
|
||||
|
||||
const ChapterBottomSheet = ({
|
||||
bottomSheetRef,
|
||||
|
||||
@@ -3,17 +3,19 @@ import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
|
||||
import { Provider } from "react-native-paper";
|
||||
import * as WebBrowser from "expo-web-browser";
|
||||
|
||||
import NovelCover from "../../components/NovelCover";
|
||||
import EmptyView from "../../components/EmptyView";
|
||||
import { SearchAppbar } from "../../components/Appbar";
|
||||
import NovelCover from "../../Components/NovelCover";
|
||||
import EmptyView from "../../Components/EmptyView";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
import { Searchbar } from "../../Components/Searchbar";
|
||||
import { useTheme } from "../../Hooks/useTheme";
|
||||
import { useLibrary } from "../../Hooks/useLibrary";
|
||||
|
||||
const Extension = ({ navigation, route }) => {
|
||||
const { sourceId, sourceName, sourceUrl } = route.params;
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
const library = useSelector((state) => state.libraryReducer.novels);
|
||||
const theme = useTheme();
|
||||
const library = useLibrary();
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
@@ -48,7 +50,7 @@ const Extension = ({ navigation, route }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const getSearchResults = (searchText) => {
|
||||
const getSearchResults = () => {
|
||||
setLoading(true);
|
||||
fetch(getSearchUrl())
|
||||
.then((response) => response.json())
|
||||
@@ -74,15 +76,16 @@ const Extension = ({ navigation, route }) => {
|
||||
{ backgroundColor: theme.colorPrimaryDark },
|
||||
]}
|
||||
>
|
||||
<SearchAppbar
|
||||
screen="Extension"
|
||||
<Searchbar
|
||||
theme={theme}
|
||||
placeholder={`Search ${sourceName}`}
|
||||
left="arrow-left"
|
||||
onPressLeft={() => navigation.goBack()}
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
getSearchResults={getSearchResults}
|
||||
getNovels={getNovels}
|
||||
setLoading={setLoading}
|
||||
openWebView={() => WebBrowser.openBrowserAsync(sourceUrl)}
|
||||
onChangeText={(text) => setSearchText(text)}
|
||||
onSubmitEditing={getSearchResults}
|
||||
right="earth"
|
||||
onPressRight={() => WebBrowser.openBrowserAsync(sourceUrl)}
|
||||
/>
|
||||
|
||||
{loading ? (
|
||||
@@ -104,7 +107,7 @@ const Extension = ({ navigation, route }) => {
|
||||
<NovelCover
|
||||
item={item}
|
||||
onPress={() =>
|
||||
navigation.navigate("NovelItem", {
|
||||
navigation.navigate("Novel", {
|
||||
novelName: item.novelName,
|
||||
novelCover: item.novelCover,
|
||||
novelUrl: item.novelUrl,
|
||||
|
||||
@@ -3,11 +3,11 @@ import { useFocusEffect } from "@react-navigation/native";
|
||||
|
||||
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
|
||||
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
import HistoryCard from "./components/HistoryCard";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import EmptyView from "../../components/EmptyView";
|
||||
import EmptyView from "../../Components/EmptyView";
|
||||
|
||||
import {
|
||||
getHistoryAction,
|
||||
|
||||
@@ -16,11 +16,12 @@ const HistoryCard = ({ item, deleteHistoryAction, navigation }) => {
|
||||
rippleColor={theme.rippleColor}
|
||||
borderless
|
||||
onPress={() =>
|
||||
navigation.navigate("NovelItem", {
|
||||
navigation.navigate("Novel", {
|
||||
novelName: item.novelName,
|
||||
novelCover: item.novelCover,
|
||||
extensionId: item.extensionId,
|
||||
novelUrl: item.novelUrl,
|
||||
sourceId: item.sourceId,
|
||||
})
|
||||
}
|
||||
>
|
||||
@@ -74,7 +75,7 @@ const HistoryCard = ({ item, deleteHistoryAction, navigation }) => {
|
||||
color={theme.textColorPrimary}
|
||||
style={{ marginRight: 10 }}
|
||||
onPress={() =>
|
||||
navigation.navigate("ChapterItem", {
|
||||
navigation.navigate("Chapter", {
|
||||
chapterId: item.historyChapterId,
|
||||
chapterUrl: item.chapterUrl,
|
||||
extensionId: item.sourceId,
|
||||
|
||||
@@ -1,61 +1,65 @@
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import React, { useState, useCallback } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
RefreshControl,
|
||||
ActivityIndicator,
|
||||
ToastAndroid,
|
||||
} from "react-native";
|
||||
import { FlatList } from "react-native-gesture-handler";
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { connect } from "react-redux";
|
||||
import { connect, useDispatch } from "react-redux";
|
||||
|
||||
import NovelCover from "../../components/NovelCover";
|
||||
import EmptyView from "../../components/EmptyView";
|
||||
import NovelCover from "../../Components/NovelCover";
|
||||
import EmptyView from "../../Components/EmptyView";
|
||||
|
||||
import {
|
||||
getLibraryAction,
|
||||
searchLibraryAction,
|
||||
} from "../../redux/library/library.actions";
|
||||
import { SearchAppbar } from "../../components/Appbar";
|
||||
import { SearchAppbar } from "../../Components/Appbar";
|
||||
import { setNovel } from "../../redux/novel/novel.actions";
|
||||
import { updateAllNovels } from "../../services/updates";
|
||||
import { updateLibraryAction } from "../../redux/updates/updates.actions";
|
||||
import { useTheme } from "../../Hooks/useTheme";
|
||||
import { Searchbar } from "../../Components/Searchbar";
|
||||
|
||||
const LibraryScreen = ({ navigation, novels, loading, itemsPerRow }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const LibraryScreen = ({
|
||||
navigation,
|
||||
theme,
|
||||
novels,
|
||||
loading,
|
||||
getLibraryAction,
|
||||
searchLibraryAction,
|
||||
setNovel,
|
||||
itemsPerRow,
|
||||
}) => {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setSearchText("");
|
||||
getLibraryAction();
|
||||
dispatch(getLibraryAction());
|
||||
}, [getLibraryAction])
|
||||
);
|
||||
|
||||
/**
|
||||
* TODO: fix refreshing
|
||||
*/
|
||||
const onRefresh = () => {
|
||||
ToastAndroid.show("Updating library", ToastAndroid.SHORT);
|
||||
setRefreshing(true);
|
||||
updateAllNovels();
|
||||
dispatch(updateLibraryAction());
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
const redirectToNovel = (item) => {
|
||||
navigation.navigate("NovelItem", item);
|
||||
setNovel(item);
|
||||
navigation.navigate("Novel", item);
|
||||
dispatch(setNovel(item));
|
||||
};
|
||||
|
||||
const renderNovels = ({ item }) => (
|
||||
<NovelCover item={item} onPress={() => redirectToNovel(item)} />
|
||||
);
|
||||
|
||||
const clearSearchbar = () => {
|
||||
dispatch(getLibraryAction());
|
||||
setSearchText("");
|
||||
};
|
||||
|
||||
const onChangeText = (text) => {
|
||||
setSearchText(text);
|
||||
dispatch(searchLibraryAction(text));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -66,13 +70,13 @@ const LibraryScreen = ({
|
||||
{ backgroundColor: theme.colorPrimaryDark },
|
||||
]}
|
||||
>
|
||||
<SearchAppbar
|
||||
screen="Library"
|
||||
<Searchbar
|
||||
placeholder="Search Library"
|
||||
getNovels={getLibraryAction}
|
||||
getSearchResults={searchLibraryAction}
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
clearSearchbar={clearSearchbar}
|
||||
onChangeText={onChangeText}
|
||||
left="magnify"
|
||||
theme={theme}
|
||||
/>
|
||||
{loading ? (
|
||||
<ActivityIndicator
|
||||
@@ -89,12 +93,7 @@ const LibraryScreen = ({
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
renderItem={({ item }) => (
|
||||
<NovelCover
|
||||
item={item}
|
||||
onPress={() => redirectToNovel(item)}
|
||||
/>
|
||||
)}
|
||||
renderItem={renderNovels}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
@@ -128,17 +127,12 @@ const LibraryScreen = ({
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
itemsPerRow: state.settingsReducer.itemsPerRow,
|
||||
novels: state.libraryReducer.novels,
|
||||
loading: state.libraryReducer.loading,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
getLibraryAction,
|
||||
searchLibraryAction,
|
||||
setNovel,
|
||||
})(LibraryScreen);
|
||||
export default connect(mapStateToProps)(LibraryScreen);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
||||
@@ -2,10 +2,10 @@ import React from "react";
|
||||
import { View } from "react-native";
|
||||
import * as Linking from "expo-linking";
|
||||
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
import { Divider, ListItem, ListSection } from "../../components/List";
|
||||
import { Divider, ListItem, ListSection } from "../../Components/List";
|
||||
|
||||
const AboutScreen = ({ navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
@@ -17,12 +17,12 @@ const AboutScreen = ({ navigation }) => {
|
||||
<ListSection>
|
||||
<ListItem
|
||||
title="Version"
|
||||
description="Stable 1.0.15"
|
||||
description="Stable 1.0.16"
|
||||
theme={theme}
|
||||
/>
|
||||
<ListItem
|
||||
title="Build Time"
|
||||
description="30-04-21 11:00 AM"
|
||||
description="03-05-21 20:45 PM"
|
||||
theme={theme}
|
||||
/>
|
||||
<ListItem
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from "react";
|
||||
import { StyleSheet, View, Image } from "react-native";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import { ListItem, ListSection } from "../../components/List";
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
import { ListItem, ListSection } from "../../Components/List";
|
||||
|
||||
const MoreScreen = ({ navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
@@ -37,7 +37,7 @@ const MoreScreen = ({ navigation }) => {
|
||||
title="Settings"
|
||||
icon="cog-outline"
|
||||
onPress={() =>
|
||||
navigation.navigate("SettingsStack", {
|
||||
navigation.navigate("MoreStack", {
|
||||
screen: "Settings",
|
||||
})
|
||||
}
|
||||
@@ -47,7 +47,7 @@ const MoreScreen = ({ navigation }) => {
|
||||
title="Tracking"
|
||||
icon="sync"
|
||||
onPress={() =>
|
||||
navigation.navigate("SettingsStack", {
|
||||
navigation.navigate("MoreStack", {
|
||||
screen: "Trackers",
|
||||
})
|
||||
}
|
||||
@@ -57,7 +57,7 @@ const MoreScreen = ({ navigation }) => {
|
||||
title="About"
|
||||
icon="information-outline"
|
||||
onPress={() =>
|
||||
navigation.navigate("SettingsStack", {
|
||||
navigation.navigate("MoreStack", {
|
||||
screen: "About",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,21 +10,22 @@ import {
|
||||
import { deleteAllHistory } from "../../database/queries/HistoryQueries";
|
||||
import { deleteNovelCache } from "../../database/queries/NovelQueries";
|
||||
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
import {
|
||||
Divider,
|
||||
ListItem,
|
||||
ListSection,
|
||||
ListSubHeader,
|
||||
} from "../../components/List";
|
||||
InfoItem,
|
||||
} from "../../Components/List";
|
||||
import DisplayModeModal from "./components/DisplayModeModal";
|
||||
import GridSizeModal from "./components/GridSizeModal";
|
||||
import ThemeModal from "./components/ThemeModal";
|
||||
import { createBackup, restoreBackup } from "../../Services/backup";
|
||||
import { useTheme } from "../../Hooks/useTheme";
|
||||
|
||||
const SettingsScreen = ({
|
||||
navigation,
|
||||
theme,
|
||||
themeCode,
|
||||
displayMode,
|
||||
itemsPerRow,
|
||||
switchTheme,
|
||||
@@ -41,6 +42,8 @@ const SettingsScreen = ({
|
||||
return label[displayMode];
|
||||
};
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
// Display Mode Modal
|
||||
const [displayModalVisible, setDisplayModalVisible] = useState(false);
|
||||
const showDisplayModal = () => setDisplayModalVisible(true);
|
||||
@@ -102,6 +105,27 @@ const SettingsScreen = ({
|
||||
onPress={showthemeModal}
|
||||
theme={theme}
|
||||
/>
|
||||
<Divider theme={theme} />
|
||||
<ListSubHeader theme={theme}>Backup</ListSubHeader>
|
||||
<ListItem
|
||||
title="Create backup"
|
||||
description="Can be used to restore current library"
|
||||
onPress={createBackup}
|
||||
theme={theme}
|
||||
/>
|
||||
<ListItem
|
||||
title="Restore backup"
|
||||
description="Restore library from backup file"
|
||||
onPress={restoreBackup}
|
||||
theme={theme}
|
||||
/>
|
||||
<InfoItem
|
||||
title="Create backup may not work on devices with Android 9 or lower."
|
||||
icon="information-outline"
|
||||
description="Restore library from backup file"
|
||||
onPress={restoreBackup}
|
||||
theme={theme}
|
||||
/>
|
||||
</ListSection>
|
||||
<DisplayModeModal
|
||||
displayMode={displayMode}
|
||||
@@ -129,7 +153,6 @@ const SettingsScreen = ({
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
displayMode: state.settingsReducer.displayMode,
|
||||
itemsPerRow: state.settingsReducer.itemsPerRow,
|
||||
});
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
myAnimeListConfig,
|
||||
getAccessToken,
|
||||
malTokenWatcher,
|
||||
} from "../../trackers/MyAnimeList";
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
} from "../../Services/Trackers/myAnimeList";
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import {
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
ListItem,
|
||||
ListSection,
|
||||
ListSubHeader,
|
||||
} from "../../components/List";
|
||||
} from "../../Components/List";
|
||||
import { removeTracker, setTracker } from "../../redux/tracker/tracker.actions";
|
||||
|
||||
const TrackerScreen = ({ navigation }) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import { Text, StyleSheet } from "react-native";
|
||||
import { Portal, Modal } from "react-native-paper";
|
||||
import Slider from "@react-native-community/slider";
|
||||
import { RadioButton, RadioButtonGroup } from "../../../components/RadioButton";
|
||||
import { RadioButton, RadioButtonGroup } from "../../../Components/RadioButton";
|
||||
|
||||
const GridSizeModal = ({
|
||||
itemsPerRow,
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
FlatList,
|
||||
RefreshControl,
|
||||
ToastAndroid,
|
||||
} from "react-native";
|
||||
import { StyleSheet, View, FlatList, RefreshControl } from "react-native";
|
||||
import { Provider, Portal } from "react-native-paper";
|
||||
import { useTheme } from "../../Hooks/useTheme";
|
||||
|
||||
import ChapterCard from "./components/ChapterCard";
|
||||
import NovelInfoHeader from "./components/NovelHeader";
|
||||
@@ -20,10 +15,10 @@ import {
|
||||
updateNovelAction,
|
||||
} from "../../redux/novel/novel.actions";
|
||||
import TrackSheet from "./components/Tracker/TrackSheet";
|
||||
import { showToast } from "../../Hooks/showToast";
|
||||
|
||||
const Novel = ({
|
||||
route,
|
||||
theme,
|
||||
novel,
|
||||
chapters,
|
||||
loading,
|
||||
@@ -41,6 +36,8 @@ const Novel = ({
|
||||
novelId,
|
||||
} = route.params;
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
let chaptersSettingsSheetRef = useRef(null);
|
||||
let trackerSheetRef = useRef(null);
|
||||
|
||||
@@ -53,7 +50,7 @@ const Novel = ({
|
||||
|
||||
const onRefresh = async () => {
|
||||
await updateNovelAction(sourceId, novelUrl, novelId);
|
||||
ToastAndroid.show(`Updated ${novelName}`, ToastAndroid.SHORT);
|
||||
showToast(`Updated ${novelName}`);
|
||||
};
|
||||
|
||||
const renderChapterCard = ({ item }) => (
|
||||
@@ -125,7 +122,6 @@ const Novel = ({
|
||||
);
|
||||
};
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
novel: state.novelReducer.novel,
|
||||
chapters: state.novelReducer.chapters,
|
||||
loading: state.novelReducer.loading,
|
||||
|
||||
@@ -89,7 +89,7 @@ const ChapterCard = ({
|
||||
<TouchableRipple
|
||||
style={styles.chapterCardContainer}
|
||||
onPress={() =>
|
||||
navigation.navigate("ChapterItem", {
|
||||
navigation.navigate("Chapter", {
|
||||
chapterId: chapter.chapterId,
|
||||
chapterUrl: chapter.chapterUrl,
|
||||
extensionId,
|
||||
|
||||
@@ -17,7 +17,7 @@ const ReadButton = ({ navigation, novel, chapters, theme }) => {
|
||||
uppercase={false}
|
||||
labelStyle={{ letterSpacing: 0 }}
|
||||
onPress={() => {
|
||||
navigation.navigate("ChapterItem", {
|
||||
navigation.navigate("Chapter", {
|
||||
chapterId: lastRead.chapterId,
|
||||
chapterUrl: lastRead.chapterUrl,
|
||||
novelUrl: novel.novelUrl,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Modal, TextInput } from "react-native-paper";
|
||||
import {
|
||||
RadioButton,
|
||||
RadioButtonGroup,
|
||||
} from "../../../../components/RadioButton";
|
||||
} from "../../../../Components/RadioButton";
|
||||
|
||||
const SetTrackChaptersDialog = ({
|
||||
trackItem,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Modal } from "react-native-paper";
|
||||
import {
|
||||
RadioButton,
|
||||
RadioButtonGroup,
|
||||
} from "../../../../components/RadioButton";
|
||||
} from "../../../../Components/RadioButton";
|
||||
|
||||
const SetTrackScoreDialog = ({
|
||||
trackItem,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Modal } from "react-native-paper";
|
||||
import {
|
||||
RadioButton,
|
||||
RadioButtonGroup,
|
||||
} from "../../../../components/RadioButton";
|
||||
} from "../../../../Components/RadioButton";
|
||||
|
||||
const SetTrackStatusDialog = ({
|
||||
trackItem,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { StyleSheet, Text, View, Image, ActivityIndicator } from "react-native";
|
||||
import { Button, Modal, TextInput, TouchableRipple } from "react-native-paper";
|
||||
import { searchNovels } from "../../../../trackers/MyAnimeList";
|
||||
import { searchNovels } from "../../../../Services/Trackers/myAnimeList";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { trackNovel } from "../../../../redux/tracker/tracker.actions";
|
||||
|
||||
@@ -1,25 +1,42 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import EmptyView from "../../components/EmptyView";
|
||||
import { getUpdatesAction } from "../../redux/updates/updates.actions";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
FlatList,
|
||||
ActivityIndicator,
|
||||
RefreshControl,
|
||||
} from "react-native";
|
||||
import { connect, useDispatch } 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 { useTheme } from "../../Hooks/useTheme";
|
||||
|
||||
const Updates = ({ theme, updates, loading, getUpdatesAction }) => {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
getUpdatesAction();
|
||||
}, [getUpdatesAction])
|
||||
);
|
||||
const Updates = ({ updates, loading, getUpdatesAction }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
getUpdatesAction();
|
||||
}, [getUpdatesAction, updates]);
|
||||
|
||||
const onRefresh = async () => {
|
||||
setRefreshing(true);
|
||||
dispatch(updateLibraryAction());
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
const renderUpdateCard = ({ item }) => <UpdateCard item={item} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar />
|
||||
<Appbar theme={theme} />
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
@@ -47,6 +64,14 @@ const Updates = ({ theme, updates, loading, getUpdatesAction }) => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={["white"]}
|
||||
progressBackgroundColor={theme.colorAccentDark}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
@@ -54,14 +79,12 @@ const Updates = ({ theme, updates, loading, getUpdatesAction }) => {
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
updates: state.updatesReducer.updates,
|
||||
loading: state.updatesReducer.loading,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, { getUpdatesAction })(Updates);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
container: { flex: 1 },
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from "react";
|
||||
import { Appbar as MaterialAppbar } from "react-native-paper";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
import { updateAllNovels } from "../../../services/updates";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { updateLibraryAction } from "../../../redux/updates/updates.actions";
|
||||
|
||||
export const Appbar = () => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
export const Appbar = ({ theme }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<MaterialAppbar.Header style={{ backgroundColor: theme.colorPrimary }}>
|
||||
@@ -13,7 +13,10 @@ export const Appbar = () => {
|
||||
title="Updates"
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
/>
|
||||
<MaterialAppbar.Action icon="reload" onPress={updateAllNovels} />
|
||||
<MaterialAppbar.Action
|
||||
icon="reload"
|
||||
onPress={() => dispatch(updateLibraryAction())}
|
||||
/>
|
||||
</MaterialAppbar.Header>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ const UpdateCard = ({ item }) => {
|
||||
style={styles.updateCard}
|
||||
onPress={() =>
|
||||
// console.log(item);
|
||||
navigation.navigate("ChapterItem", {
|
||||
navigation.navigate("Chapter", {
|
||||
chapterId: item.chapterId,
|
||||
chapterUrl: item.chapterUrl,
|
||||
extensionId: item.sourceId,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
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";
|
||||
import { restoreLibrary } from "../database/queries/NovelQueries";
|
||||
import { showToast } from "../Hooks/showToast";
|
||||
|
||||
export const createBackup = async () => {
|
||||
const novels = await getLibrary();
|
||||
|
||||
const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
|
||||
|
||||
if (!permissions.granted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uri = permissions.directoryUri;
|
||||
|
||||
if (uri) {
|
||||
const fileUri = await StorageAccessFramework.createFileAsync(
|
||||
uri,
|
||||
"backup",
|
||||
"application/json"
|
||||
);
|
||||
|
||||
await StorageAccessFramework.writeAsStringAsync(
|
||||
fileUri,
|
||||
JSON.stringify(novels)
|
||||
);
|
||||
|
||||
showToast("Backup created");
|
||||
}
|
||||
};
|
||||
|
||||
export const restoreBackup = async () => {
|
||||
const backup = await DocumentPicker.getDocumentAsync();
|
||||
|
||||
if (backup.uri) {
|
||||
let novels = await StorageAccessFramework.readAsStringAsync(backup.uri);
|
||||
novels = await JSON.parse(novels);
|
||||
await novels.map((novel) => restoreLibrary(novel));
|
||||
showToast("Restored backup");
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
import { getLibrary } from "../database/queries/LibraryQueries";
|
||||
import { fetchChapters, fetchNovel } from "../source/Source";
|
||||
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) => {
|
||||
@@ -107,8 +108,6 @@ export const updateNovelChapters = async (extensionId, novelUrl, novelId) => {
|
||||
};
|
||||
|
||||
export const updateAllNovels = async () => {
|
||||
ToastAndroid.show("Updating library", ToastAndroid.SHORT);
|
||||
|
||||
const libraryNovels = await getLibrary();
|
||||
|
||||
libraryNovels.map((novel, index) =>
|
||||
|
||||
Reference in New Issue
Block a user