Add SQLite Storage

This commit is contained in:
Rajarshee Chatterjee
2020-12-25 21:51:06 +05:30
parent 76dd481aad
commit 09c426dab2
10 changed files with 588 additions and 70 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import Router from "./src/navigation/Router";
import { NavigationContainer } from "@react-navigation/native";
import { StatusBar } from "expo-status-bar";
+1
View File
@@ -0,0 +1 @@
[1225/135759.273:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)
+5
View File
@@ -7191,6 +7191,11 @@
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.15.0.tgz",
"integrity": "sha512-qTSQPy0WKHtlb8xt5gY0Gt6sdvfQUQAnFSqgsggW9UEvySbkHzpqOrOYNA79Ca8oXO0dCFwp6X8buIiDefa7+Q=="
},
"react-native-sqlite-storage": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-native-sqlite-storage/-/react-native-sqlite-storage-5.0.0.tgz",
"integrity": "sha512-c1Joq3/tO1nmIcP8SkRZNolPSbfvY8uZg5lXse0TmjIPC0qHVbk96IMvWGyly1TmYCIpxpuDRc0/xCffDbYIvg=="
},
"react-native-vector-icons": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-7.1.0.tgz",
+40 -38
View File
@@ -1,40 +1,42 @@
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-community/masked-view": "0.1.10",
"@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",
"cheerio-without-node-native": "^0.20.2",
"expo": "~40.0.0",
"expo-app-loading": "^1.0.1",
"expo-font": "^8.4.0",
"expo-linear-gradient": "^8.4.0",
"expo-status-bar": "~1.0.3",
"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-collapsible-header-views": "^1.1.2",
"react-native-gesture-handler": "^1.8.0",
"react-native-paper": "^4.5.0",
"react-native-reanimated": "^1.13.2",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"react-native-vector-icons": "^7.1.0",
"react-native-web": "~0.13.12",
"rn-sliding-up-panel": "^2.4.4"
},
"devDependencies": {
"@babel/core": "~7.9.0"
},
"private": true,
"proxy": "http://192.168.1.38:5000"
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-community/masked-view": "0.1.10",
"@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",
"cheerio-without-node-native": "^0.20.2",
"expo": "~40.0.0",
"expo-app-loading": "^1.0.1",
"expo-font": "^8.4.0",
"expo-linear-gradient": "^8.4.0",
"expo-status-bar": "~1.0.3",
"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-collapsible-header-views": "^1.1.2",
"react-native-gesture-handler": "^1.8.0",
"react-native-paper": "^4.5.0",
"react-native-reanimated": "^1.13.2",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"react-native-sqlite-storage": "^5.0.0",
"react-native-vector-icons": "^7.1.0",
"react-native-web": "~0.13.12",
"rn-sliding-up-panel": "^2.4.4",
"expo-sqlite": "~8.5.0"
},
"devDependencies": {
"@babel/core": "~7.9.0"
},
"private": true,
"proxy": "http://192.168.1.38:5000"
}
+5 -1
View File
@@ -13,7 +13,7 @@ import Search from "../screens/Search";
import NovelItem from "../screens/NovelItem";
import ChapterItem from "../screens/ChapterItem";
import About from "../screens/About";
import LibraryNovelItem from "../screens/LibraryNovelItem";
import { theme } from "../theming/theme";
const Stack = createStackNavigator();
@@ -182,6 +182,10 @@ const MainStack = () => {
<Stack.Screen name="MainStack" component={Router} />
<Stack.Screen name="ChapterItem" component={ChapterItem} />
<Stack.Screen name="NovelItem" component={NovelItem} />
<Stack.Screen
name="LibraryNovelItem"
component={LibraryNovelItem}
/>
<Stack.Screen
name="About"
component={About}
+11 -17
View File
@@ -7,7 +7,7 @@ import { theme } from "../theming/theme";
import { CollapsibleHeaderScrollView } from "react-native-collapsible-header-views";
const ChapterItem = ({ route, navigation }) => {
const { chapterName, chapterUrl } = route.params;
const { chapterUrl } = route.params;
const [loading, setLoading] = useState(true);
@@ -115,24 +115,18 @@ const ChapterItem = ({ route, navigation }) => {
<Text
style={[
{
// lineHeight: 0,
paddingVertical: 15,
fontSize: size,
},
readerTheme === 1 && {
color: theme.textColorSecondaryDark,
},
readerTheme === 2 && {
color: "black",
},
readerTheme === 3 && {
color: "black",
},
size === 16 && {
lineHeight: 20,
},
size === 20 && {
lineHeight: 25,
color:
readerTheme === 1
? theme.textColorSecondaryDark
: "black",
// lineHeight:
// size === 16
// ? 22
// : size === 20
// ? 25
// : size === 12 && 20,
},
]}
>
-6
View File
@@ -162,10 +162,4 @@ const styles = StyleSheet.create({
contentContainer: {
flex: 1,
},
fab: {
position: "absolute",
margin: 16,
right: 0,
bottom: 0,
},
});
+387
View File
@@ -0,0 +1,387 @@
import React, { useState, useEffect } from "react";
import {
StyleSheet,
View,
Image,
ImageBackground,
Text,
FlatList,
ScrollView,
RefreshControl,
} from "react-native";
import {
Appbar,
TouchableRipple,
IconButton,
Button,
} from "react-native-paper";
import { LinearGradient } from "expo-linear-gradient";
import { theme } from "../theming/theme";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const NovelItem = ({ route, navigation }) => {
const item = route.params;
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [novel, setNovel] = useState();
const [chapters, setChapters] = useState();
const [more, setMore] = useState(false);
const fetchNovel = () => {
db.transaction((tx) => {
tx.executeSql(
"SELECT * FROM LibraryTable WHERE novelId=?",
[item.novelId],
(txObj, { rows: { _array } }) => {
setNovel(_array);
setRefreshing(false);
},
(txObj, error) => console.log("Error ", error)
);
tx.executeSql(
"SELECT * FROM ChapterTable WHERE novelId=?",
[item.novelId],
(txObj, { rows: { _array } }) => {
setChapters(_array);
setLoading(false);
},
(txObj, error) => console.log("Error ", error)
);
});
};
useEffect(() => {
fetchNovel();
}, []);
const onRefresh = async () => {
setRefreshing(true);
fetchNovel();
};
return (
<>
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
<Appbar.BackAction
onPress={() => {
navigation.goBack();
}}
color={"white"}
size={26}
style={{ marginRight: 0 }}
/>
<Appbar.Content
title={item.novelName}
titleStyle={{ color: theme.textColorPrimaryDark }}
/>
</Appbar.Header>
<ScrollView
style={styles.container}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={["white"]}
progressBackgroundColor={theme.colorAccentDark}
/>
}
>
<ImageBackground
source={{
uri: item.novelCover,
}}
style={styles.background}
>
<LinearGradient
colors={["transparent", "#000000"]}
style={styles.linearGradient}
>
<View style={styles.detailsContainer}>
<Image
source={{
uri: item.novelCover,
}}
style={styles.logo}
/>
<View style={styles.nameContainer}>
<Text
numberOfLines={4}
style={[
styles.name,
{ color: theme.textColorPrimaryDark },
]}
>
{item.novelName}
</Text>
{!loading && (
<>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
numberOfLines={1}
>
{item.alternative.replace(
",",
", "
)}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{item.author.replace(",", ", ")}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{item.releaseDate}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{item.status + " • " + item.type}
</Text>
</>
)}
</View>
</View>
</LinearGradient>
</ImageBackground>
{!loading && (
<>
{/* <Button
color={theme.textColorPrimaryDark}
style={{
backgroundColor: theme.colorAccentDark,
marginHorizontal: 15,
}}
uppercase={false}
labelStyle={{ letterSpacing: 0 }}
onPress={() => insertToLibrary()}
>
Add to library
</Button> */}
{item.novelSummary.length > 0 && (
<View
style={{
paddingHorizontal: 15,
marginBottom: 10,
}}
>
<Text
style={{
color: theme.textColorPrimaryDark,
marginTop: 5,
paddingVertical: 5,
fontSize: 15,
}}
>
About
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
lineHeight: 20,
}}
numberOfLines={more ? 100 : 2}
onPress={() => setMore(!more)}
ellipsizeMode="clip"
>
{item.novelSummary}
</Text>
<Text
style={{
color: theme.colorAccentDark,
fontWeight: "bold",
position: "absolute",
bottom: 0,
right: 15,
}}
onPress={() => setMore(!more)}
>
{more ? "Less" : "More"}
</Text>
</View>
)}
<FlatList
contentContainerStyle={{
paddingHorizontal: 15,
marginBottom: 15,
}}
horizontal
data={item.genre.split(",")}
keyExtractor={(item) => item}
renderItem={({ item }) => (
<Text
style={[
styles.genre,
{
color: theme.colorAccentDark,
borderColor: theme.colorAccentDark,
},
]}
>
{item}
</Text>
)}
/>
<TouchableRipple
style={{
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
onPress={() =>
setChapters((chapters) => chapters.reverse())
}
rippleColor={theme.rippleColorDark}
>
<>
<Text
style={{
color: theme.textColorPrimaryDark,
paddingHorizontal: 15,
paddingVertical: 5,
fontSize: 15,
}}
>
{chapters.length + " Chapters"}
</Text>
<IconButton
icon="filter-variant"
color={theme.textColorPrimaryDark}
size={20}
onPress={() =>
setChapters((chapters) =>
chapters.reverse()
)
}
/>
</>
</TouchableRipple>
<FlatList
data={chapters}
extraData={chapters}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.chapterId}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
initialNumToRender={10}
renderItem={({ item }) => (
<TouchableRipple
style={{
paddingHorizontal: 15,
paddingVertical: 12,
}}
onPress={() =>
navigation.navigate("ChapterItem", {
chapterUrl: item.chapterId,
})
}
rippleColor={theme.rippleColorDark}
>
<>
<Text
style={{
color:
theme.textColorPrimaryDark,
}}
numberOfLines={1}
>
{item.chapterName}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginTop: 5,
fontSize: 13,
}}
numberOfLines={1}
>
{item.releaseDate}
</Text>
</>
</TouchableRipple>
)}
/>
</>
)}
</ScrollView>
</>
);
};
export default NovelItem;
const styles = StyleSheet.create({
container: {
flex: 1,
// backgroundColor: "#202125",
backgroundColor: "#000000",
},
nameContainer: {
flex: 1,
width: "100%",
marginHorizontal: 15,
// justifyContent: "center",
},
background: {
height: 240,
},
linearGradient: {
height: "100%",
backgroundColor: "rgba(256, 256, 256, 0.5)",
},
detailsContainer: {
flex: 1,
flexDirection: "row",
margin: 15,
},
logo: {
height: 180,
width: 120,
margin: 3.2,
borderRadius: 6,
},
genre: {
borderRadius: 24,
borderWidth: 1,
paddingHorizontal: 10,
marginHorizontal: 2,
fontSize: 13,
paddingVertical: 2,
justifyContent: "center",
flex: 1,
},
name: {
fontWeight: "bold",
fontSize: 20,
},
});
+67 -2
View File
@@ -9,11 +9,19 @@ import {
ScrollView,
RefreshControl,
} from "react-native";
import { Appbar, TouchableRipple, IconButton } from "react-native-paper";
import {
Appbar,
TouchableRipple,
IconButton,
Button,
} from "react-native-paper";
import { LinearGradient } from "expo-linear-gradient";
import { theme } from "../theming/theme";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const NovelItem = ({ route, navigation }) => {
const item = route.params;
@@ -38,6 +46,52 @@ const NovelItem = ({ route, navigation }) => {
});
};
const insertToLibrary = () => {
createTables();
db.transaction((tx) => {
tx.executeSql(
"INSERT INTO LibraryTable (novelId, novelName, novelCover, novelSummary, alternative, author, genre, type, releaseDate, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
[
novel.novelUrl,
novel.novelName,
novel.novelCover,
novel.novelSummary,
novel.novelDetails.Alternative,
novel.novelDetails["Author(s)"],
novel.novelDetails["Genre(s)"],
novel.novelDetails.Type,
novel.novelDetails.Release,
novel.novelDetails.Status,
]
);
console.log("Inserted Novel");
novel.novelChapters.map((chap) =>
tx.executeSql(
"INSERT INTO ChapterTable (chapterId, chapterName, releaseDate, novelId) values (?, ?, ?, ?)",
[
chap.chapterUrl,
chap.chapterName,
chap.releaseDate,
novel.novelUrl,
]
)
);
});
};
const createTables = () =>
db.transaction((tx) => {
tx.executeSql(
"CREATE TABLE IF NOT EXISTS LibraryTable (novelId VARCHAR(255) NOT NULL PRIMARY KEY, novelName VARCHAR(255), novelCover VARCHAR(255), novelSummary TEXT, alternative VARCHAR(255), author VARCHAR(255), genre VARCHAR(255), type VARCHAR(255), releaseDate VARCHAR(255), status VARCHAR(255))"
);
console.log("Library Table Created");
tx.executeSql(
"CREATE TABLE IF NOT EXISTS ChapterTable (chapterId VARCHAR(255), chapterName VARCHAR(255), releaseDate VARCHAR(255), novelId VARCHAR(255), FOREIGN KEY (novelId) REFERENCES LibraryTable(novelId) ON DELETE CASCADE)"
);
});
useEffect(() => {
getNovel();
}, []);
@@ -159,6 +213,18 @@ const NovelItem = ({ route, navigation }) => {
</ImageBackground>
{!loading && (
<>
<Button
color={theme.textColorPrimaryDark}
style={{
backgroundColor: theme.colorAccentDark,
marginHorizontal: 15,
}}
uppercase={false}
labelStyle={{ letterSpacing: 0 }}
onPress={() => insertToLibrary()}
>
Add to library
</Button>
{novel.novelSummary.length > 0 && (
<View
style={{
@@ -275,7 +341,6 @@ const NovelItem = ({ route, navigation }) => {
}}
onPress={() =>
navigation.navigate("ChapterItem", {
chapterName: item.chapterName,
chapterUrl: item.chapterUrl,
})
}
+71 -5
View File
@@ -1,12 +1,78 @@
import React from "react";
import React, { useState, useCallback } from "react";
import { StyleSheet, Text, View } from "react-native";
import { FlatList } from "react-native-gesture-handler";
import { theme } from "../theming/theme";
import { useFocusEffect } from "@react-navigation/native";
import { Button } from "react-native-paper";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const UpdatesScreen = ({ navigation }) => {
const [novels, setNovels] = useState();
const getLibraryNovels = () => {
db.transaction((tx) => {
tx.executeSql(
"SELECT * FROM LibraryTable",
null,
(txObj, { rows: { _array } }) => {
setNovels(_array);
// console.log(_array);
},
(txObj, error) => console.log("Error ", error)
);
// tx.executeSql(
// "SELECT * FROM Chaptertable",
// null,
// (txObj, { rows: { _array } }) => {
// console.log(_array);
// },
// (txObj, error) => console.log("Error ", error)
// );
});
};
// const deleted = () => {
// db.transaction((tx) => {
// tx.executeSql(
// "DROP TABLE LibraryTable",
// null,
// (txObj) => {
// console.log("Deleted");
// },
// (txObj, error) => console.log("Error ", error)
// );
// });
// };
useFocusEffect(
useCallback(() => {
getLibraryNovels();
}, [])
);
const UpdatesScreen = () => {
return (
<View style={styles.container}>
<Text style={{ color: "#FFFFFF", fontSize: 16 }}>
Updates Screen
</Text>
<FlatList
data={novels}
keyExtractor={(item) => item.novelId}
renderItem={({ item }) => (
<Text
style={{ color: theme.colorAccentDark, fontSize: 20 }}
onPress={() =>
navigation.navigate("LibraryNovelItem", item)
}
>
{item.novelId}
</Text>
)}
/>
{/* <Button mode="contained" onPress={() => deleted()}>
Delete
</Button> */}
</View>
);
};