Break into components

This commit is contained in:
Rajarshee Chatterjee
2020-12-30 12:43:58 +05:30
parent 10c7ac5217
commit 40dfebe964
15 changed files with 950 additions and 751 deletions
+2 -2
View File
@@ -32,14 +32,14 @@ export default function App() {
(tx, error) => console.log(error)
);
tx.executeSql(
"CREATE TABLE IF NOT EXISTS HistoryTable (historyId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255) UNIQUE, novelUrl VARCHAR(255), chapterName VARCHAR(255), lastRead DATETIME DEFAULT CURRENT_TIMESTAMP)",
"CREATE TABLE IF NOT EXISTS HistoryTable (historyId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255) UNIQUE, novelUrl VARCHAR(255) UNIQUE, chapterName VARCHAR(255), lastRead DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (novelUrl) REFERENCES LibraryTable(novelUrl) ON DELETE CASCADE)",
null,
(tx, results) => console.log("History Table Created"),
(tx, error) => console.log(error)
);
tx.executeSql(
"CREATE TABLE IF NOT EXISTS DownloadsTable (downloadId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255) UNIQUE, novelUrl VARCHAR(255), chapterName VARCHAR(255), chapterText VARCHAR(255), nextChapter VARCHAR(255), prevChapter VARCHAR(255))",
"CREATE TABLE IF NOT EXISTS DownloadsTable (downloadId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255) UNIQUE, novelUrl VARCHAR(255), chapterName VARCHAR(255), chapterText VARCHAR(255), nextChapter VARCHAR(255), prevChapter VARCHAR(255), FOREIGN KEY (novelUrl) REFERENCES LibraryTable(novelUrl) ON DELETE CASCADE)",
null,
(tx, results) => console.log("Downloads Table Created"),
(tx, error) => console.log(error)
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+13
View File
@@ -0,0 +1,13 @@
import React from "react";
import { Appbar } from "react-native-paper";
import { theme } from "../theming/theme";
export const CustomAppbar = ({ title, onBackAction }) => (
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
{onBackAction && <Appbar.BackAction onPress={onBackAction} />}
<Appbar.Content
title={title}
titleStyle={{ color: theme.textColorPrimaryDark }}
/>
</Appbar.Header>
);
+88
View File
@@ -0,0 +1,88 @@
import React from "react";
import { View, Text } from "react-native";
import { TouchableRipple, IconButton } from "react-native-paper";
import { theme } from "../theming/theme";
const ChapterCard = ({
navigation,
novelUrl,
chapter,
downloadChapter,
extensionId,
}) => (
<TouchableRipple
style={{
paddingHorizontal: 15,
paddingVertical: 12,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
onPress={() =>
navigation.navigate("ChapterItem", {
chapterUrl: chapter.chapterUrl,
extensionId,
novelUrl: novelUrl,
chapterName: chapter.chapterName,
})
}
rippleColor={theme.rippleColorDark}
>
<>
<View>
<Text
style={[
{
color: theme.textColorPrimaryDark,
},
chapter.read === 1 && {
color: theme.textColorHintDark,
},
]}
numberOfLines={1}
>
{chapter.chapterName.substring(0, 50) + "..."}
</Text>
<Text
style={[
{
color: theme.textColorSecondaryDark,
marginTop: 5,
fontSize: 13,
},
chapter.read === 1 && {
color: theme.textColorHintDark,
},
]}
numberOfLines={1}
>
{chapter.releaseDate ? chapter.releaseDate : "release-date"}
</Text>
</View>
<View>
<IconButton
icon={
chapter.downloaded
? "check-circle"
: "arrow-down-circle-outline"
}
animated
color={
chapter.downloaded
? "#47a84a"
: theme.textColorSecondaryDark
}
size={24}
onPress={() => {
downloadChapter(
chapter.downloaded ? chapter.downloaded : 0,
chapter.chapterUrl
);
}}
/>
</View>
</>
</TouchableRipple>
);
export default ChapterCard;
+118
View File
@@ -0,0 +1,118 @@
import React from "react";
import { theme } from "../theming/theme";
import moment from "moment";
import { StyleSheet, View, Text, FlatList, Image } from "react-native";
import { TouchableRipple, IconButton, Appbar } from "react-native-paper";
const HistoryCard = ({ item, deleteHistory, navigation }) => (
<TouchableRipple
style={styles.historyCard}
rippleColor={theme.rippleColorDark}
borderless
onPress={() =>
navigation.navigate("NovelItem", {
novelName: item.novelName,
novelCover: item.novelCover,
extensionId: item.extensionId,
novelUrl: item.novelUrl,
})
}
>
<>
<Image
source={{ uri: item.novelCover }}
style={{
height: 80,
width: 57,
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
}}
/>
<View
style={{
marginLeft: 15,
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<View>
<Text
style={{
color: "white",
fontWeight: "bold",
fontSize: 16,
}}
>
{item.novelName}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginTop: 2,
}}
numberOfLines={1}
>
{`Ch. ${item.chapterName
.replace(/^\D+/g, "")
.substring(0, 4)} - ${moment(
item.lastRead
).calendar()}`}
</Text>
</View>
<View
style={{
flexDirection: "row",
}}
>
<IconButton
icon="delete-outline"
size={24}
color={theme.textColorPrimaryDark}
style={{
marginRight: 0,
}}
onPress={() => deleteHistory(item.novelUrl)}
/>
<IconButton
icon="play"
size={24}
color={theme.textColorPrimaryDark}
style={{ marginRight: 10 }}
onPress={() =>
navigation.navigate("ChapterItem", {
chapterUrl: item.chapterUrl,
extensionId: item.extensionId,
novelUrl: item.novelUrl,
novelName: item.novelName,
novelCover: item.novelCover,
chapterName: item.chapterName,
})
}
/>
</View>
</View>
</>
</TouchableRipple>
);
export default HistoryCard;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#202125",
backgroundColor: "#000000",
padding: 10,
},
historyCard: {
// backgroundColor: "pink",
// paddingVertical: 10,
// marginVertical: 5,
// paddingHorizontal: 20,
marginTop: 10,
borderRadius: 4,
flexDirection: "row",
alignItems: "center",
},
});
+423
View File
@@ -0,0 +1,423 @@
import React, { useState } from "react";
import {
View,
Text,
ImageBackground,
StyleSheet,
Image,
FlatList,
} from "react-native";
import { TouchableRipple, IconButton, Button } from "react-native-paper";
import { LinearGradient } from "expo-linear-gradient";
import { theme } from "../theming/theme";
const NovelInfoHeader = ({
item,
novel,
loading,
libraryStatus,
noOfChapters,
insertToLibrary,
sortChapters,
}) => {
const [more, setMore] = useState(false);
const renderGenreChip = ({ item }) => (
<Text
style={[
styles.genre,
{
color: theme.colorAccentDark,
borderColor: theme.colorAccentDark,
},
]}
>
{item}
</Text>
);
return (
<>
<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}
>
{novel.Alternative &&
novel.Alternative.replace(
",",
", "
)}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{novel["Author(s)"].replace(",", ", ")}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{novel["Release"]}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{novel["Status"] +
" • " +
novel["Type"]}
</Text>
</>
) : (
<>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
numberOfLines={1}
>
{item.Alternative &&
item.Alternative.replace(",", ", ")}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{item["Author(s)"] &&
item["Author(s)"].replace(
",",
", "
)}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{item["Release"] && item["Release"]}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{item["Status"] &&
item["Type"] &&
item["Status"] +
" • " +
item["Type"]}
</Text>
</>
)}
</View>
</View>
</LinearGradient>
</ImageBackground>
{!loading ? (
<>
<Button
color={theme.textColorPrimaryDark}
style={[
{
backgroundColor: theme.colorAccentDark,
marginHorizontal: 15,
},
novel.novelSummary.length === 0 && {
marginBottom: 20,
},
]}
icon={
libraryStatus === 0
? "bookmark-outline"
: "bookmark"
}
uppercase={false}
labelStyle={{ letterSpacing: 0 }}
onPress={() => insertToLibrary()}
>
{libraryStatus === 0 ? "Add to library" : "In Library"}
</Button>
{novel.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"
>
{novel.novelSummary}
</Text>
<Text
style={{
color: theme.colorAccentDark,
fontWeight: "bold",
position: "absolute",
bottom: 0,
right: 15,
backgroundColor: "black",
paddingLeft: 5,
}}
onPress={() => setMore(!more)}
>
{more ? "Less" : "More"}
</Text>
</View>
)}
<FlatList
contentContainerStyle={{
paddingHorizontal: 15,
marginBottom: 15,
}}
horizontal
data={novel["Genre(s)"].split(",")}
keyExtractor={(item) => item}
renderItem={renderGenreChip}
/>
<TouchableRipple
style={{
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingRight: 15,
}}
onPress={() => sortChapters()}
rippleColor={theme.rippleColorDark}
>
<>
<Text
style={{
color: theme.textColorPrimaryDark,
paddingHorizontal: 15,
paddingVertical: 5,
fontSize: 15,
}}
>
{noOfChapters + " Chapters"}
</Text>
<IconButton
icon="filter-variant"
color={theme.textColorPrimaryDark}
size={24}
onPress={() => sortChapters()}
/>
</>
</TouchableRipple>
</>
) : (
<>
{item.libraryStatus && (
<Button
color={theme.textColorPrimaryDark}
style={[
{
backgroundColor: theme.colorAccentDark,
marginHorizontal: 15,
},
item.novelSummary &&
item.novelSummary.length === 0 && {
marginBottom: 20,
},
]}
icon={
item.libraryStatus === 0
? "bookmark-outline"
: "bookmark"
}
uppercase={false}
labelStyle={{ letterSpacing: 0 }}
onPress={() => insertToLibrary()}
>
{item.libraryStatus === 0
? "Add to library"
: "In Library"}
</Button>
)}
{item.novelSummary && 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,
backgroundColor: "black",
paddingLeft: 5,
}}
onPress={() => setMore(!more)}
>
{more ? "Less" : "More"}
</Text>
</View>
)}
<FlatList
contentContainerStyle={{
paddingHorizontal: 15,
marginBottom: 15,
}}
horizontal
data={item["Genre(s)"] && item["Genre(s)"].split(",")}
keyExtractor={(item) => item}
renderItem={({ item }) => (
<Text
style={[
styles.genre,
{
color: theme.colorAccentDark,
borderColor: theme.colorAccentDark,
},
]}
>
{item}
</Text>
)}
/>
</>
)}
</>
);
};
export default NovelInfoHeader;
const styles = StyleSheet.create({
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,
},
});
+31 -105
View File
@@ -1,108 +1,53 @@
import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import {
createStackNavigator,
TransitionPresets,
} from "@react-navigation/stack";
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import { TransitionPresets } from "@react-navigation/stack";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import Library from "../screens/Library";
import History from "../screens/History/History";
import Browse from "../screens/Browse";
import Latest from "../screens/Latest";
import More from "../screens/More";
import BoxNovelSearch from "../screens/Sources/BoxNovel/BoxNovelSearch";
import More from "../screens/More/More";
import About from "../screens/More/About";
import NovelItem from "../screens/NovelItem";
import ChapterItem from "../screens/ChapterItem";
import About from "../screens/About";
import HistoryScreen from "../screens/History/History";
import Updates from "../screens/Updates/Updates";
import BoxNovel from "../screens/Sources/BoxNovel/BoxNovel";
import { theme } from "../theming/theme";
// Sources
import BoxNovel from "../screens/Sources/BoxNovel/BoxNovel";
import BoxNovelSearch from "../screens/Sources/BoxNovel/BoxNovelSearch";
import ReadLightNovel from "../screens/Sources/ReadLightNovel/ReadLightNovel";
import ReadLightNovelSearch from "../screens/Sources/ReadLightNovel/ReadLightNovelSearch";
import { theme } from "../theming/theme";
const Stack = createStackNavigator();
const Tab = createMaterialBottomTabNavigator();
const LibraryStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerTintColor: theme.textColorPrimaryDark,
headerStyle: { backgroundColor: theme.colorDarkPrimary },
}}
>
<Stack.Screen name="Library" component={Library} />
</Stack.Navigator>
);
const stackNavigatorConfig = {
headerShown: false,
...TransitionPresets.RevealFromBottomAndroid,
};
// const UpdatesStack = () => {
// return (
// <Stack.Navigator
// screenOptions={{
// headerTintColor: theme.textColorPrimaryDark,
// headerStyle: { backgroundColor: theme.colorDarkPrimary },
// }}
// >
// <Stack.Screen name="Updates" component={Updates} />
// </Stack.Navigator>
// );
// };
const SearchStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Search"
component={Search}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
);
};
const BrowseStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerTintColor: theme.textColorPrimaryDark,
headerStyle: { backgroundColor: theme.colorDarkPrimary },
}}
>
<Stack.Screen
name="Browse"
component={Browse}
// options={{ headerShown: false }}
/>
</Stack.Navigator>
);
};
const MoreStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerTintColor: theme.textColorPrimaryDark,
headerStyle: { backgroundColor: theme.colorDarkPrimary },
}}
>
<Stack.Navigator screenOptions={stackNavigatorConfig}>
<Stack.Screen name="More" component={More} />
<Stack.Screen name="About" component={About} />
</Stack.Navigator>
);
};
const BoxNovelStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
...TransitionPresets.RevealFromBottomAndroid,
}}
>
<Stack.Navigator screenOptions={stackNavigatorConfig}>
<Stack.Screen name="BoxNovel" component={BoxNovel} />
<Stack.Screen name="BoxNovelSearch" component={BoxNovelSearch} />
</Stack.Navigator>
@@ -111,12 +56,7 @@ const BoxNovelStack = () => {
const ReadLightNovelStack = () => {
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
...TransitionPresets.RevealFromBottomAndroid,
}}
>
<Stack.Navigator screenOptions={stackNavigatorConfig}>
<Stack.Screen name="ReadLightNovel" component={ReadLightNovel} />
<Stack.Screen
name="ReadLightNovelSearch"
@@ -126,16 +66,16 @@ const ReadLightNovelStack = () => {
);
};
const Router = () => {
const BottomNavigator = () => {
return (
<Tab.Navigator
shifting={false}
barStyle={{ backgroundColor: theme.colorDarkPrimary }}
activeColor="#3399FF"
activeColor={theme.colorAccentDark}
>
<Tab.Screen
name="My Library"
component={LibraryStack}
component={Library}
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
@@ -162,7 +102,7 @@ const Router = () => {
/> */}
<Tab.Screen
name="History"
component={HistoryScreen}
component={History}
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
@@ -175,7 +115,7 @@ const Router = () => {
/>
<Tab.Screen
name="Browse"
component={BrowseStack}
component={Browse}
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
@@ -203,26 +143,12 @@ const Router = () => {
);
};
const MainStack = () => {
const Router = () => {
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
...TransitionPresets.RevealFromBottomAndroid,
}}
>
<Stack.Screen name="MainStack" component={Router} />
<Stack.Navigator screenOptions={stackNavigatorConfig}>
<Stack.Screen name="Router" component={BottomNavigator} />
<Stack.Screen name="ChapterItem" component={ChapterItem} />
<Stack.Screen name="NovelItem" component={NovelItem} />
<Stack.Screen
name="About"
component={About}
options={{
headerShown: true,
headerTintColor: theme.textColorPrimaryDark,
headerStyle: { backgroundColor: theme.colorDarkPrimary },
}}
/>
<Stack.Screen name="BoxNovelStack" component={BoxNovelStack} />
<Stack.Screen
name="ReadLightNovelStack"
@@ -232,4 +158,4 @@ const MainStack = () => {
);
};
export default MainStack;
export default Router;
+78 -72
View File
@@ -1,84 +1,90 @@
import React, { useEffect, useState } from "react";
import { StyleSheet, View, Text, FlatList, Image } from "react-native";
import { TouchableRipple, IconButton } from "react-native-paper";
import { CustomAppbar } from "../components/Appbar";
import { theme } from "../theming/theme";
import { sources } from "../utils/sources";
const Browse = ({ navigation }) => {
return (
<View style={styles.container}>
<FlatList
data={sources}
keyExtractor={(item) => item.sourceId.toString()}
renderItem={({ item }) => (
<TouchableRipple
style={styles.sourceCard}
onPress={() =>
navigation.navigate(item.sourceName + "Stack")
}
rippleColor={theme.rippleColorDark}
>
<>
<Image
source={{
uri: item.sourceCover,
}}
style={{
width: 60,
height: 60,
borderRadius: 6,
}}
resizeMode="contain"
/>
<View
style={{
marginLeft: 15,
flex: 1,
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
}}
>
<View>
<Text
style={{
color: theme.textColorPrimaryDark,
fontSize: 16,
}}
>
{item.sourceName}
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
}}
>
{item.sourceLanguage}
</Text>
<>
<CustomAppbar title="Browse" />
<View style={styles.container}>
<FlatList
data={sources}
keyExtractor={(item) => item.sourceId.toString()}
renderItem={({ item }) => (
<TouchableRipple
style={styles.sourceCard}
onPress={() =>
navigation.navigate(item.sourceName + "Stack")
}
rippleColor={theme.rippleColorDark}
>
<>
<Image
source={{
uri: item.sourceCover,
}}
style={{
width: 60,
height: 60,
borderRadius: 6,
}}
resizeMode="contain"
/>
<View
style={{
marginLeft: 15,
flex: 1,
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
}}
>
<View>
<Text
style={{
color:
theme.textColorPrimaryDark,
fontSize: 16,
}}
>
{item.sourceName}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
}}
>
{item.sourceLanguage}
</Text>
</View>
<View>
<IconButton
icon="magnify"
color={theme.textColorSecondaryDark}
size={24}
onPress={() =>
navigation.navigate(
item.sourceName + "Stack",
{
screen:
item.sourceName +
"Search",
}
)
}
/>
</View>
</View>
<View>
<IconButton
icon="magnify"
color={theme.textColorSecondaryDark}
size={24}
onPress={() =>
navigation.navigate(
item.sourceName + "Stack",
{
screen:
item.sourceName +
"Search",
}
)
}
/>
</View>
</View>
</>
</TouchableRipple>
)}
/>
</View>
</>
</TouchableRipple>
)}
/>
</View>
</>
);
};
+17 -119
View File
@@ -1,16 +1,16 @@
import React, { useEffect, useState, useCallback } from "react";
import { StyleSheet, View, Text, FlatList, Image } from "react-native";
import { TouchableRipple, IconButton, Appbar } from "react-native-paper";
import React, { useState, useCallback, useEffect } from "react";
import { StyleSheet, View, Text, FlatList } from "react-native";
import { theme } from "../../theming/theme";
import moment from "moment";
import { useFocusEffect } from "@react-navigation/native";
import { CustomAppbar } from "../../components/Appbar";
import HistoryCard from "../../components/HistoryCard";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const History = ({ navigation }) => {
const [loading, setLoading] = useState(true);
const [novels, setNovels] = useState();
const [history, setHistory] = useState();
const getHistory = () => {
db.transaction((tx) => {
@@ -18,21 +18,19 @@ const History = ({ navigation }) => {
"SELECT HistoryTable.chapterUrl, HistoryTable.historyId, HistoryTable.chapterName, HistoryTable.lastRead, LibraryTable.novelName, LibraryTable.novelCover, LibraryTable.novelUrl, LibraryTable.extensionId FROM HistoryTable INNER JOIN LibraryTable ON HistoryTable.novelUrl = LibraryTable.novelUrl ORDER BY HistoryTable.lastRead DESC",
null,
(txObj, { rows: { _array } }) => {
setNovels(_array);
// console.log(_array);
setLoading(false);
setHistory(_array);
},
(txObj, error) => console.log("Error ", error)
);
});
};
const deleteHistory = (name) => {
const deleteHistory = (novelId) => {
db.transaction((tx) => {
tx.executeSql(
"DELETE FROM HistoryTable WHERE novelUrl = ?",
[name],
(txObj, { rows: { _array } }) => {
[novelId],
(txObj, res) => {
getHistory();
},
(txObj, error) => console.log("Error ", error)
@@ -48,118 +46,18 @@ const History = ({ navigation }) => {
return (
<>
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
<Appbar.Content
title="History"
titleStyle={{ color: theme.textColorPrimaryDark }}
/>
</Appbar.Header>
<CustomAppbar title="History" />
<View style={styles.container}>
<FlatList
contentContainerStyle={{ flex: 1 }}
data={novels}
data={history}
keyExtractor={(item) => item.historyId.toString()}
renderItem={({ item }) => (
<TouchableRipple
style={styles.historyCard}
rippleColor={theme.rippleColorDark}
borderless
onPress={() =>
navigation.navigate("NovelItem", {
novelName: item.novelName,
novelCover: item.novelCover,
extensionId: item.extensionId,
novelUrl: item.novelUrl,
})
}
>
<>
<Image
source={{ uri: item.novelCover }}
style={{
height: 80,
width: 57,
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
}}
/>
<View
style={{
marginLeft: 15,
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<View>
<Text
style={{
color: "white",
fontWeight: "bold",
fontSize: 16,
}}
>
{item.novelName}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginTop: 2,
}}
numberOfLines={1}
>
{`Ch. ${item.chapterName
.replace(/^\D+/g, "")
.substring(0, 4)} - ${moment(
item.lastRead
).calendar()}`}
</Text>
</View>
<View
style={{
flexDirection: "row",
}}
>
<IconButton
icon="delete-outline"
size={24}
color={theme.textColorPrimaryDark}
style={{
marginRight: 0,
}}
onPress={() =>
deleteHistory(item.novelUrl)
}
/>
<IconButton
icon="play"
size={24}
color={theme.textColorPrimaryDark}
style={{ marginRight: 10 }}
onPress={() =>
navigation.navigate(
"ChapterItem",
{
chapterUrl:
item.chapterUrl,
extensionId:
item.extensionId,
novelUrl: item.novelUrl,
novelName:
item.novelName,
novelCover:
item.novelCover,
chapterName:
item.chapterName,
}
)
}
/>
</View>
</View>
</>
</TouchableRipple>
<HistoryCard
item={item}
deleteHistory={deleteHistory}
navigation={navigation}
/>
)}
ListEmptyComponent={
<View
+63 -55
View File
@@ -10,9 +10,11 @@ import { FlatList } from "react-native-gesture-handler";
import { theme } from "../theming/theme";
import { useFocusEffect } from "@react-navigation/native";
import { TouchableRipple, Button } from "react-native-paper";
import { Button } from "react-native-paper";
import NovelCover from "../components/NovelCover";
import { CustomAppbar } from "../components/Appbar";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
@@ -88,63 +90,69 @@ const UpdatesScreen = ({ navigation }) => {
};
return (
<View style={styles.container}>
<FlatList
contentContainerStyle={{ flex: 1 }}
numColumns={3}
data={novels}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.novelUrl}
renderItem={({ item }) => (
<NovelCover
item={item}
onPress={() => navigation.navigate("NovelItem", item)}
/>
)}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={["white"]}
progressBackgroundColor={theme.colorAccentDark}
/>
}
ListEmptyComponent={
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text
<>
<CustomAppbar title="Library" />
<View style={styles.container}>
<FlatList
contentContainerStyle={{ flex: 1 }}
numColumns={3}
data={novels}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.novelUrl}
renderItem={({ item }) => (
<NovelCover
item={item}
onPress={() =>
navigation.navigate("NovelItem", item)
}
/>
)}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={["white"]}
progressBackgroundColor={theme.colorAccentDark}
/>
}
ListEmptyComponent={
<View
style={{
color: theme.textColorSecondaryDark,
fontSize: 45,
fontWeight: "bold",
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
Σ(ಠ_ಠ)
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
fontWeight: "bold",
marginTop: 10,
textAlign: "center",
paddingHorizontal: 30,
}}
>
Your library is empty. Add series to your library
from Browse.
</Text>
</View>
}
/>
{/* <Button mode="contained" onPress={() => deleted()}>
Delete Library Table
</Button> */}
</View>
<Text
style={{
color: theme.textColorSecondaryDark,
fontSize: 45,
fontWeight: "bold",
}}
>
Σ(ಠ_ಠ)
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
fontWeight: "bold",
marginTop: 10,
textAlign: "center",
paddingHorizontal: 30,
}}
>
Your library is empty. Add series to your
library from Browse.
</Text>
</View>
}
/>
{/* <Button mode="contained" onPress={() => deleted()}>
Delete Library Table
</Button> */}
</View>
</>
);
};
-39
View File
@@ -1,39 +0,0 @@
import React from "react";
import { StyleSheet } from "react-native";
import { List } from "react-native-paper";
import { theme } from "../theming/theme";
const MoreScreen = ({ navigation }) => {
return (
<List.Section
style={{
flex: 1,
backgroundColor: theme.colorDarkPrimaryDark,
marginTop: 0,
marginBottom: 0,
}}
>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
title="About"
left={() => (
<List.Icon
color={theme.colorAccentDark}
icon="information-outline"
/>
)}
onPress={() => navigation.navigate("About")}
/>
</List.Section>
);
};
export default MoreScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
// backgroundColor: "#202125",
backgroundColor: "#000000",
},
});
@@ -1,11 +1,16 @@
import React from "react";
import { List, Divider } from "react-native-paper";
import * as Linking from "expo-linking";
import { theme } from "../theming/theme";
import { theme } from "../../theming/theme";
import { CustomAppbar } from "../../components/Appbar";
const AboutScreen = ({ navigation }) => {
return (
<>
<CustomAppbar
title="About"
onBackAction={() => navigation.goBack()}
/>
<List.Section
style={{
flex: 1,
@@ -35,6 +40,7 @@ const AboutScreen = ({ navigation }) => {
"https://github.com/rajarsheechatterjee/LNReader/commits/master"
)
}
rippleColor={theme.rippleColorDark}
/>
<Divider />
@@ -48,6 +54,7 @@ const AboutScreen = ({ navigation }) => {
"https://github.com/rajarsheechatterjee/lnreader"
)
}
rippleColor={theme.rippleColorDark}
/>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
@@ -60,6 +67,7 @@ const AboutScreen = ({ navigation }) => {
"https://github.com/rajarsheechatterjee/lnreader-extensions"
)
}
rippleColor={theme.rippleColorDark}
/>
</List.Section>
</>
+54
View File
@@ -0,0 +1,54 @@
import React from "react";
import { StyleSheet } from "react-native";
import { List } from "react-native-paper";
import { CustomAppbar } from "../../components/Appbar";
import { theme } from "../../theming/theme";
const MoreScreen = ({ navigation }) => {
return (
<>
<CustomAppbar title="More" />
<List.Section
style={{
flex: 1,
backgroundColor: theme.colorDarkPrimaryDark,
marginTop: 0,
marginBottom: 0,
}}
>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
title="Settings"
left={() => (
<List.Icon
color={theme.colorAccentDark}
icon="code-tags"
/>
)}
onPress={() => navigation.navigate("About")}
/>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
title="About"
left={() => (
<List.Icon
color={theme.colorAccentDark}
icon="information-outline"
/>
)}
onPress={() => navigation.navigate("About")}
/>
</List.Section>
</>
);
};
export default MoreScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
// backgroundColor: "#202125",
backgroundColor: "#000000",
},
});
+54 -358
View File
@@ -1,27 +1,21 @@
import React, { useState, useCallback, useEffect } from "react";
import React, { useState, useCallback } from "react";
import { useFocusEffect } from "@react-navigation/native";
import {
StyleSheet,
View,
Image,
ImageBackground,
Text,
FlatList,
RefreshControl,
ToastAndroid,
ActivityIndicator,
} from "react-native";
import {
Appbar,
TouchableRipple,
IconButton,
Button,
FAB,
ProgressBar,
} from "react-native-paper";
import { LinearGradient } from "expo-linear-gradient";
import { useFocusEffect } from "@react-navigation/native";
import { Appbar, FAB, ProgressBar } from "react-native-paper";
import { theme } from "../theming/theme";
import ChapterCard from "../components/ChapterCard";
import NovelInfoHeader from "../components/NovelInfoHeader";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
@@ -35,9 +29,8 @@ const NovelItem = ({ route, navigation }) => {
const [novel, setNovel] = useState(item);
const [chapters, setChapters] = useState();
const [more, setMore] = useState(false);
const [libraryStatus, setlibraryStatus] = useState(0);
const [libraryStatus, setlibraryStatus] = useState(item.libraryStatus);
const [sort, setSort] = useState("ASC");
const [readingStatus, setReadingStatus] = useState();
@@ -63,21 +56,8 @@ const NovelItem = ({ route, navigation }) => {
});
};
const getNovelFromDb = () => {
const getChaptersFromDb = () => {
db.transaction((tx) => {
tx.executeSql(
"SELECT * FROM LibraryTable WHERE novelUrl=?",
[item.novelUrl],
(txObj, results) => {
let len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
setNovel(row);
setlibraryStatus(row.libraryStatus);
}
},
(txObj, error) => console.log("Error ", error)
);
tx.executeSql(
`SELECT * FROM ChapterTable WHERE novelUrl=? ORDER BY chapterId ${sort}`,
[item.novelUrl],
@@ -109,14 +89,12 @@ const NovelItem = ({ route, navigation }) => {
(txObj, results) => {
let len = results.rows.length;
if (len > 0) {
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
setReadingStatus({
chapterUrl: row.chapterUrl,
chapterName: row.chapterName,
fabLabel: "Resume",
});
}
let row = results.rows.item(0);
setReadingStatus({
chapterUrl: row.chapterUrl,
chapterName: row.chapterName,
fabLabel: "Resume",
});
} else {
setReadingStatus({
chapterUrl: chapUrl,
@@ -149,7 +127,7 @@ const NovelItem = ({ route, navigation }) => {
0,
],
(tx, res) => {
console.log("Inserted into DB");
// console.log("Inserted into DB");
},
(txObj, error) => console.log("Error ", error)
);
@@ -181,9 +159,9 @@ const NovelItem = ({ route, navigation }) => {
`UPDATE ChapterTable SET downloaded = 1 WHERE chapterUrl = ?`,
[cdUrl],
(tx, res) => {
console.log(
"Updated Download Status to downloaded"
);
// console.log(
// "Updated Download Status to downloaded"
// );
},
(txObj, error) => console.log("Error ", error)
);
@@ -202,7 +180,7 @@ const NovelItem = ({ route, navigation }) => {
`Downloaded ${json.chapterName}`,
ToastAndroid.SHORT
);
console.log("Inserted into Downloads Table");
// console.log("Inserted into Downloads Table");
},
(txObj, error) => console.log("Error ", error)
);
@@ -218,7 +196,7 @@ const NovelItem = ({ route, navigation }) => {
`UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ?`,
[cdUrl],
(tx, res) => {
console.log("Updated Download Status to downloaded");
// console.log("Updated Download Status to downloaded");
},
(txObj, error) => console.log("Error ", error)
);
@@ -226,7 +204,7 @@ const NovelItem = ({ route, navigation }) => {
`DELETE FROM DownloadsTable WHERE chapterUrl = ?`,
[cdUrl],
(tx, res) => {
console.log("Deleted Download");
// console.log("Deleted Download");
checkIfExistsInDB();
ToastAndroid.show(
`Chapter deleted`,
@@ -250,7 +228,7 @@ const NovelItem = ({ route, navigation }) => {
"Added to library",
ToastAndroid.SHORT
);
console.log("Inserted into Library");
// console.log("Inserted into Library");
setlibraryStatus(1);
},
@@ -267,7 +245,7 @@ const NovelItem = ({ route, navigation }) => {
"Removed from library",
ToastAndroid.SHORT
);
console.log("Removed From Library");
// console.log("Removed From Library");
setlibraryStatus(0);
},
(txObj, error) => console.log("Error ", error)
@@ -284,12 +262,15 @@ const NovelItem = ({ route, navigation }) => {
(txObj, res) => {
if (res.rows.length === 0) {
setRefreshing(true);
console.log("Not In Database");
setlibraryStatus(0);
// console.log("Not In Database");
getNovel();
} else {
// setRefreshing(true);
console.log("In Database");
getNovelFromDb();
// console.log("In Database");
setlibraryStatus(res.rows.item(0).libraryStatus);
setNovel(res.rows.item(0));
getChaptersFromDb();
}
},
(txObj, error) => console.log("Error ", error)
@@ -313,6 +294,16 @@ const NovelItem = ({ route, navigation }) => {
checkIfExistsInDB();
};
const renderChapterCard = ({ item }) => (
<ChapterCard
navigation={navigation}
novelUrl={novelUrl}
extensionId={extensionId}
chapter={item}
downloadChapter={downloadChapter}
/>
);
return (
<>
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
@@ -324,10 +315,10 @@ const NovelItem = ({ route, navigation }) => {
size={26}
style={{ marginRight: 0 }}
/>
{/* <Appbar.Content
<Appbar.Content
title={item.novelName}
titleStyle={{ color: theme.textColorPrimaryDark }}
/> */}
/>
</Appbar.Header>
<View style={styles.container}>
@@ -344,312 +335,18 @@ const NovelItem = ({ route, navigation }) => {
removeClippedSubviews={true}
maxToRenderPerBatch={10}
initialNumToRender={10}
renderItem={({ item }) => (
<TouchableRipple
style={{
paddingHorizontal: 15,
paddingVertical: 12,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
onPress={() =>
navigation.navigate("ChapterItem", {
chapterUrl: item.chapterUrl,
extensionId,
novelUrl: novelUrl,
chapterName: item.chapterName,
})
}
rippleColor={theme.rippleColorDark}
>
<>
<View>
<Text
style={[
{
color:
theme.textColorPrimaryDark,
},
item.read === 1 && {
color: theme.textColorHintDark,
},
]}
numberOfLines={1}
>
{item.chapterName}
</Text>
<Text
style={[
{
color:
theme.textColorSecondaryDark,
marginTop: 5,
fontSize: 13,
},
item.read === 1 && {
color: theme.textColorHintDark,
},
]}
numberOfLines={1}
>
{item.releaseDate
? item.releaseDate
: "release-date"}
</Text>
</View>
<View>
<IconButton
icon={
item.downloaded
? "check-circle"
: "arrow-down-circle-outline"
}
animated
color={
item.downloaded
? "#47a84a"
: theme.textColorSecondaryDark
}
size={24}
onPress={() => {
downloadChapter(
item.downloaded
? item.downloaded
: 0,
item.chapterUrl
);
}}
/>
</View>
</>
</TouchableRipple>
renderItem={renderChapterCard}
ListHeaderComponent={() => (
<NovelInfoHeader
item={item}
novel={novel}
noOfChapters={!loading && chapters.length}
loading={loading}
libraryStatus={libraryStatus}
insertToLibrary={insertToLibrary}
sortChapters={sortChapters}
/>
)}
ListHeaderComponent={
<>
<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}
>
{novel.Alternative &&
novel.Alternative.replace(
",",
", "
)}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{novel[
"Author(s)"
].replace(",", ", ")}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{novel["Release"]}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
marginVertical: 3,
fontSize: 15,
}}
>
{novel["Status"] +
" • " +
novel["Type"]}
</Text>
</>
)}
</View>
</View>
</LinearGradient>
</ImageBackground>
{!loading && (
<>
<Button
color={theme.textColorPrimaryDark}
style={[
{
backgroundColor:
theme.colorAccentDark,
marginHorizontal: 15,
},
novel.novelSummary.length === 0 && {
marginBottom: 20,
},
]}
icon={
libraryStatus === 0
? "bookmark-outline"
: "bookmark"
}
uppercase={false}
labelStyle={{ letterSpacing: 0 }}
onPress={() => insertToLibrary()}
>
{libraryStatus === 0
? "Add to library"
: "In Library"}
</Button>
{novel.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"
>
{novel.novelSummary}
</Text>
<Text
style={{
color:
theme.colorAccentDark,
fontWeight: "bold",
position: "absolute",
bottom: 0,
right: 15,
backgroundColor: "black",
paddingLeft: 5,
}}
onPress={() => setMore(!more)}
>
{more ? "Less" : "More"}
</Text>
</View>
)}
<FlatList
contentContainerStyle={{
paddingHorizontal: 15,
marginBottom: 15,
}}
horizontal
data={novel["Genre(s)"].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",
paddingRight: 15,
}}
onPress={() => sortChapters()}
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={24}
onPress={() => sortChapters()}
/>
</>
</TouchableRipple>
</>
)}
</>
}
refreshControl={
<RefreshControl
refreshing={refreshing}
@@ -667,7 +364,6 @@ const NovelItem = ({ route, navigation }) => {
label={readingStatus.fabLabel}
color={theme.textColorPrimaryDark}
onPress={() => {
// console.log(readingStatus);
navigation.navigate("ChapterItem", {
chapterUrl: readingStatus.chapterUrl,
extensionId,