Refactor search and extension UI

This commit is contained in:
Rajarshee Chatterjee
2021-03-20 14:28:16 +05:30
committed by GitHub
30 changed files with 657 additions and 882 deletions
+126 -9
View File
@@ -1,19 +1,136 @@
import React from "react";
import { Appbar } from "react-native-paper";
import React, { useRef } from "react";
import { View, TextInput, StyleSheet } from "react-native";
import {
TouchableRipple,
IconButton,
Appbar as MaterialAppbar,
} from "react-native-paper";
import Constants from "expo-constants";
import { useNavigation } from "@react-navigation/native";
import { useSelector } from "react-redux";
export const CustomAppbar = ({ title, onBackAction }) => {
export const Appbar = ({ title, onBackAction }) => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
{onBackAction && <Appbar.BackAction onPress={onBackAction} />}
<Appbar.Content
<MaterialAppbar.Header style={{ backgroundColor: theme.colorPrimary }}>
{onBackAction && (
<MaterialAppbar.BackAction onPress={onBackAction} />
)}
<MaterialAppbar.Content
title={title}
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
/>
</Appbar.Header>
</MaterialAppbar.Header>
);
};
export const SearchAppbar = ({
placeholder,
getSearchResults,
setSearchText,
searchText,
getNovels,
setLoading,
screen,
onFilter,
}) => {
const searchRef = useRef(null);
const navigation = useNavigation();
const theme = useSelector((state) => state.themeReducer.theme);
return (
<TouchableRipple
borderless
onPress={() => searchRef.current.focus()}
style={[
styles.searchAppbarContainer,
{ backgroundColor: theme.searchBarColor },
]}
>
<View
style={{
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<View style={{ flex: 1, flexDirection: "row" }}>
<IconButton
icon={screen === "Library" ? "magnify" : "arrow-left"}
color={theme.textColorSecondary}
style={{ marginLeft: 0 }}
size={23}
onPress={() => {
if (screen === "Extension") {
navigation.goBack();
}
}}
/>
<TextInput
ref={searchRef}
style={{
fontSize: 16,
color: theme.textColorSecondary,
}}
placeholder={placeholder}
placeholderTextColor={theme.textColorSecondary}
onChangeText={(text) => {
setSearchText(text);
if (screen === "Library") {
getSearchResults(text);
}
}}
onSubmitEditing={() => {
if (screen === "Extension") {
getSearchResults(searchText);
}
}}
defaultValue={searchText}
/>
</View>
{searchText !== "" && (
<IconButton
icon="close"
color={theme.textColorSecondary}
style={{ marginRight: 0 }}
size={23}
onPress={() => {
setLoading?.(true);
getNovels();
setSearchText("");
}}
/>
)}
<IconButton
icon="filter-variant"
color={theme.textColorSecondary}
style={{ marginRight: 0 }}
size={23}
disabled={typeof onFilter === "function" ? false : true}
/**
* TODO
*/
onPress={() => onFilter?.()}
/>
</View>
</TouchableRipple>
);
};
const styles = StyleSheet.create({
searchAppbarContainer: {
marginTop: Constants.statusBarHeight + 4,
height: 48,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
marginBottom: 8,
borderRadius: 8,
marginHorizontal: 12,
elevation: 2,
},
});
@@ -2,7 +2,7 @@ import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { useSelector } from "react-redux";
const EmptyView = () => {
const EmptyView = ({ icon, description }) => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
@@ -11,21 +11,21 @@ const EmptyView = () => {
style={[
styles.emptyViewIcon,
{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
},
]}
>
(˘・_・˘)
{icon}
</Text>
<Text
style={[
styles.emptyViewText,
{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
},
]}
>
Nothing read recently.
{description}
</Text>
</View>
);
+10 -16
View File
@@ -21,20 +21,18 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
<TouchableRipple
borderless
centered
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
style={styles.opac}
onPress={onPress}
>
<>
{item.novelCover ? (
{item.novelCover && (
<ImageBackground
source={{
uri: item.novelCover,
}}
source={{ uri: item.novelCover }}
style={styles.logo}
imageStyle={[
{ borderRadius: 4 },
libraryStatus && { opacity: 0.4 },
libraryStatus && { opacity: 0.5 },
]}
progressiveRenderingEnabled={true}
>
@@ -53,7 +51,7 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
styles.title,
{
color:
theme.textColorPrimaryDark,
"rgba(255,255,255,1)",
},
]}
>
@@ -63,19 +61,16 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
</View>
)}
</ImageBackground>
) : (
<View>
<Text style={{ color: "white" }}>
Novel Cover Doesn't Exist
</Text>
</View>
)}
{displayMode === 1 && (
<Text
numberOfLines={2}
style={[
styles.title,
{ color: theme.textColorPrimaryDark },
{
color: theme.textColorPrimary,
padding: 4,
},
]}
>
{item.novelName}
@@ -91,8 +86,7 @@ export default NovelCover;
const styles = StyleSheet.create({
logo: {
height: 183,
borderRadius: 4,
height: 180,
},
titleContainer: {
flex: 1,
+4 -4
View File
@@ -13,10 +13,10 @@ export const DisplayCheckbox = ({ displayMode, onPress, value }) => {
return (
<Checkbox.Item
label={label[value]}
labelStyle={{ color: theme.textColorPrimaryDark }}
labelStyle={{ color: theme.textColorPrimary }}
status={displayMode === value ? "checked" : "unchecked"}
mode="ios"
uncheckedColor={theme.textColorSecondaryDark}
uncheckedColor={theme.textColorSecondary}
color={theme.colorAccentDark}
onPress={onPress}
/>
@@ -29,10 +29,10 @@ export const ThemeCheckbox = ({ onPress, label, checked }) => {
return (
<Checkbox.Item
label={label}
labelStyle={{ color: theme.textColorPrimaryDark }}
labelStyle={{ color: theme.textColorPrimary }}
status={checked ? "checked" : "unchecked"}
mode="ios"
uncheckedColor={theme.textColorSecondaryDark}
uncheckedColor={theme.textColorSecondary}
color={theme.colorAccentDark}
onPress={onPress}
/>
+27 -24
View File
@@ -4,6 +4,7 @@ import {
TransitionPresets,
} from "@react-navigation/stack";
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import { Provider } from "react-native-paper";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
@@ -64,7 +65,7 @@ const BottomNavigator = () => {
return (
<Tab.Navigator
shifting={false}
barStyle={{ backgroundColor: theme.colorDarkPrimary }}
barStyle={{ backgroundColor: theme.colorPrimary }}
activeColor={theme.colorAccentDark}
>
<Tab.Screen
@@ -139,29 +140,31 @@ const BottomNavigator = () => {
const Router = () => {
return (
<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="BoxNovelStack" component={BoxNovelStack} />
<Stack.Screen
name="ReadLightNovelStack"
component={ReadLightNovelStack}
/>
</Stack.Navigator>
<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="BoxNovelStack" component={BoxNovelStack} />
<Stack.Screen
name="ReadLightNovelStack"
component={ReadLightNovelStack}
/>
</Stack.Navigator>
</Provider>
);
};
+11
View File
@@ -0,0 +1,11 @@
import { GET_EXTENSIONS } from "./types";
import { fetchExtensionList } from "../../services/api";
export const getExtensions = () => async (dispatch) => {
const res = await fetchExtensionList();
dispatch({
type: GET_EXTENSIONS,
payload: res,
});
};
+2
View File
@@ -5,3 +5,5 @@ export const GET_LIBRARY_NOVELS = "GET_LIBRARY_NOVELS";
export const SET_DISPLAY_MODE = "SET_DISPLAY_MODE";
export const SET_ITEMS_PER_ROW = "SET_ITEMS_PER_ROW";
export const GET_EXTENSIONS = "GET_EXTENSIONS";
+19
View File
@@ -0,0 +1,19 @@
import { GET_EXTENSIONS } from "../actions/types";
const initialState = {
extensions: [],
loading: true,
};
const libraryReducer = (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case GET_EXTENSIONS:
return { extensions: payload, loading: false };
default:
return state;
}
};
export default libraryReducer;
+7 -1
View File
@@ -6,6 +6,7 @@ import thunk from "redux-thunk";
import themeReducer from "../reducers/theme";
import settingsReducer from "../reducers/settings";
import libraryReducer from "../reducers/library";
import extensionReducer from "../reducers/extension";
const persistConfig = {
key: "root",
@@ -14,7 +15,12 @@ const persistConfig = {
const persistedReducer = persistReducer(
persistConfig,
combineReducers({ themeReducer, settingsReducer, libraryReducer })
combineReducers({
themeReducer,
settingsReducer,
libraryReducer,
extensionReducer,
})
);
export const store = createStore(persistedReducer, applyMiddleware(thunk));
+32 -120
View File
@@ -1,144 +1,56 @@
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
import { Appbar } from "../../components/common/Appbar";
import { connect } from "react-redux";
import {
StyleSheet,
View,
Text,
FlatList,
Image,
ActivityIndicator,
} from "react-native";
import { TouchableRipple, Button } from "react-native-paper";
import { CustomAppbar } from "../../components/common/Appbar";
import { getExtensions } from "../../redux/actions/extension";
import { useSelector } from "react-redux";
const Browse = ({ navigation }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const [loading, setLoading] = useState(true);
const [extensions, setExtensions] = useState();
import ExtensionCard from "./components/ExtensionCard";
const Browse = ({ theme, extensions, loading, getExtensions }) => {
useEffect(() => {
fetch(`https://lnreader-extensions.herokuapp.com/api/`)
.then((response) => response.json())
.then((json) => {
setExtensions(json);
setLoading(false);
});
getExtensions();
}, []);
return (
<>
<CustomAppbar title="Browse" />
<Appbar title="Browse" />
<View
style={[
styles.container,
{ backgroundColor: theme.colorDarkPrimaryDark },
{ backgroundColor: theme.colorPrimaryDark },
]}
>
{!loading ? (
<FlatList
data={extensions}
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: 40,
height: 40,
borderRadius: 6,
}}
resizeMode="contain"
/>
<View
style={{
marginLeft: 15,
flex: 1,
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
}}
>
<View>
<Text
style={{
color:
theme.textColorPrimaryDark,
fontSize: 14,
}}
>
{item.sourceName}
</Text>
<Text
style={{
color:
theme.textColorSecondaryDark,
fontSize: 12,
}}
>
{item.sourceLanguage}
</Text>
</View>
<View>
<Button
labelStyle={{
letterSpacing: 0,
}}
uppercase={false}
color={theme.colorAccentDark}
onPress={() =>
navigation.navigate(
item.sourceName +
"Stack"
)
}
>
Browse
</Button>
</View>
</View>
</>
</TouchableRipple>
)}
/>
) : (
<View style={{ padding: 20 }}>
<ActivityIndicator
size="small"
color={theme.colorAccentDark}
/>
</View>
)}
<FlatList
data={extensions}
keyExtractor={(item) => item.sourceId.toString()}
renderItem={({ item }) => <ExtensionCard item={item} />}
ListEmptyComponent={
loading && (
<ActivityIndicator
size="small"
color={theme.colorAccentDark}
style={{ marginTop: 16 }}
/>
)
}
/>
</View>
</>
);
};
export default Browse;
const mapStateToProps = (state) => ({
theme: state.themeReducer.theme,
extensions: state.extensionReducer.extensions,
loading: state.extensionReducer.loading,
});
export default connect(mapStateToProps, { getExtensions })(Browse);
const styles = StyleSheet.create({
container: {
flex: 1,
},
sourceCard: {
paddingVertical: 10,
marginVertical: 5,
paddingHorizontal: 20,
borderRadius: 6,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 4,
},
});
@@ -0,0 +1,91 @@
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 { sourceName, sourceCover, sourceLanguage } = item;
const navigation = useNavigation();
const theme = useSelector((state) => state.themeReducer.theme);
return (
<TouchableRipple
style={styles.extensionCard}
onPress={() => navigation.navigate(sourceName + "Stack")}
rippleColor={theme.rippleColor}
>
<>
<Image
source={{
uri: sourceCover,
}}
style={styles.extensionIcon}
resizeMode="contain"
/>
<View style={styles.extensionDetails}>
<View>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 14,
}}
>
{sourceName}
</Text>
<Text
style={{
color: theme.textColorSecondary,
fontSize: 12,
}}
>
{sourceLanguage}
</Text>
</View>
<View>
<Button
labelStyle={{
letterSpacing: 0,
}}
uppercase={false}
color={theme.colorAccentDark}
onPress={() =>
navigation.navigate(sourceName + "Stack")
}
>
Browse
</Button>
</View>
</View>
</>
</TouchableRipple>
);
};
export default ExtensionCard;
const styles = StyleSheet.create({
extensionCard: {
flexDirection: "row",
alignItems: "center",
marginVertical: 4,
paddingVertical: 8,
paddingHorizontal: 20,
borderRadius: 6,
},
extensionIcon: {
width: 40,
height: 40,
borderRadius: 8,
},
extensionDetails: {
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginLeft: 16,
},
});
+3 -3
View File
@@ -193,7 +193,7 @@ const ChapterItem = ({ route, navigation }) => {
/>
<Appbar.Content
title={loading ? "Chapter" : chapter.chapterName}
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
/>
{!loading && (
<>
@@ -241,7 +241,7 @@ const ChapterItem = ({ route, navigation }) => {
contentContainerStyle={[
styles.container,
readerTheme === 1 && {
backgroundColor: theme.colorDarkPrimary,
backgroundColor: theme.colorPrimary,
},
readerTheme === 2 && {
backgroundColor: "white",
@@ -277,7 +277,7 @@ const ChapterItem = ({ route, navigation }) => {
fontSize: size,
},
readerTheme === 1
? { color: theme.textColorSecondaryDark }
? { color: theme.textColorSecondary }
: { color: "black" },
size === 16
? { lineHeight: 25 }
@@ -48,7 +48,7 @@ const ChapterBottomSheet = ({
<View style={{ flex: 1, alignItems: "center", paddingTop: 30 }}>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
fontWeight: "bold",
}}
>
@@ -70,7 +70,7 @@ const ChapterBottomSheet = ({
/>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
fontWeight: "bold",
}}
>
+12 -6
View File
@@ -9,13 +9,13 @@ import {
ActivityIndicator,
} from "react-native";
import { CustomAppbar } from "../../components/common/Appbar";
import { Appbar } from "../../components/common/Appbar";
import HistoryCard from "./components/HistoryCard";
import { getHistoryFromDb, deleteChapterHistory } from "../../services/db";
import { useSelector } from "react-redux";
import EmptyView from "./components/EmptyView";
import EmptyView from "../../components/common/EmptyView";
const History = ({ navigation }) => {
const [loading, setLoading] = useState(true);
@@ -49,15 +49,14 @@ const History = ({ navigation }) => {
return (
<>
<CustomAppbar title="History" />
<Appbar title="History" />
<View
style={[
styles.container,
{ backgroundColor: theme.colorDarkPrimaryDark },
{ backgroundColor: theme.colorPrimaryDark },
]}
>
<FlatList
contentContainerStyle={{ flex: 1 }}
data={history}
keyExtractor={(item) => item.historyId.toString()}
renderItem={renderHistoryCard}
@@ -69,7 +68,14 @@ const History = ({ navigation }) => {
/>
)
}
ListEmptyComponent={!loading && <EmptyView />}
ListEmptyComponent={
!loading && (
<EmptyView
icon="(˘・_・˘)"
description="Nothing read recently"
/>
)
}
/>
</View>
</>
@@ -13,7 +13,7 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
return (
<TouchableRipple
style={styles.historyCard}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
borderless
onPress={() =>
navigation.navigate("NovelItem", {
@@ -34,7 +34,7 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
<View style={{ flex: 0.9 }}>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
fontWeight: "bold",
fontSize: 16,
}}
@@ -44,7 +44,7 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
marginTop: 2,
}}
numberOfLines={1}
@@ -63,7 +63,7 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
<IconButton
icon="delete-outline"
size={22}
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
style={{
marginRight: 0,
}}
@@ -72,7 +72,7 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
<IconButton
icon="play"
size={24}
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
style={{ marginRight: 10 }}
onPress={() =>
navigation.navigate("ChapterItem", {
+50 -52
View File
@@ -11,14 +11,14 @@ import { FlatList } from "react-native-gesture-handler";
import { useFocusEffect } from "@react-navigation/native";
import { connect } from "react-redux";
import Appbar from "./components/Appbar";
import NovelCover from "../../components/common/NovelCover";
import EmptyView from "./components/EmptyView";
import EmptyView from "../../components/common/EmptyView";
import {
getLibraryNovels,
searchLibraryNovels,
} from "../../redux/actions/library";
import { SearchAppbar } from "../../components/common/Appbar";
const LibraryScreen = ({
navigation,
@@ -30,13 +30,11 @@ const LibraryScreen = ({
}) => {
const [refreshing, setRefreshing] = useState(false);
const [search, setSearch] = useState({ searching: false, searchText: "" });
const resetSearch = () => setSearch({ searching: false, searchText: "" });
const [searchText, setSearchText] = useState("");
useFocusEffect(
useCallback(() => {
resetSearch();
setSearchText("");
getLibraryNovels();
}, [])
);
@@ -59,61 +57,62 @@ const LibraryScreen = ({
return (
<>
<Appbar
search={search}
setSearch={setSearch}
getLibraryNovels={getLibraryNovels}
searchLibraryNovels={searchLibraryNovels}
/>
<View
style={[
styles.container,
{ backgroundColor: theme.colorDarkPrimaryDark },
{ backgroundColor: theme.colorPrimaryDark },
]}
>
<SearchAppbar
screen="Library"
placeholder="Search Library"
getNovels={getLibraryNovels}
getSearchResults={searchLibraryNovels}
searchText={searchText}
setSearchText={setSearchText}
/>
{loading ? (
<ActivityIndicator
size="small"
color={theme.colorAccentDark}
/>
) : (
<>
<FlatList
numColumns={3}
data={novels}
keyExtractor={(item) => item.novelUrl}
renderItem={({ item }) => (
<NovelCover
item={item}
onPress={() => redirectToNovel(item)}
<FlatList
numColumns={3}
data={novels}
keyExtractor={(item) => item.novelUrl}
renderItem={({ item }) => (
<NovelCover
item={item}
onPress={() => redirectToNovel(item)}
/>
)}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={["white"]}
progressBackgroundColor={theme.colorAccentDark}
/>
}
ListEmptyComponent={
searchText !== "" ? (
<Text
style={[
styles.emptySearch,
{ color: theme.colorAccentDark },
]}
>
{`"${searchText}" not in library`}
</Text>
) : (
<EmptyView
icon="Σ(ಠ_ಠ)"
description="Your library is empty. Add series to your library from Browse."
/>
)}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={["white"]}
progressBackgroundColor={
theme.colorAccentDark
}
/>
}
ListEmptyComponent={
search.searchText === "" ? (
<EmptyView />
) : (
<Text
style={[
styles.emptySearch,
{ color: theme.colorAccentDark },
]}
>
{`"${search.searchText}" not in library`}
</Text>
)
}
/>
</>
)
}
/>
)}
</View>
</>
@@ -137,9 +136,8 @@ const styles = StyleSheet.create({
padding: 4,
},
emptySearch: {
fontWeight: "bold",
marginTop: 10,
marginTop: 8,
textAlign: "center",
paddingHorizontal: 30,
paddingHorizontal: 16,
},
});
+7 -7
View File
@@ -15,22 +15,22 @@ const LibraryAppbar = ({
const { searching, searchText } = search;
return (
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
<Appbar.Header style={{ backgroundColor: theme.colorPrimary }}>
{!searching ? (
<>
<Appbar.Content
title="Library"
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
/>
<Appbar.Action
icon="magnify"
onPress={() =>
setSearch({ ...search, searching: true })
}
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
/>
<Appbar.Action
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
icon="filter-variant"
disabled
// onPress={() => _panel.show({ velocity: -1.5 })}
@@ -43,7 +43,7 @@ const LibraryAppbar = ({
setSearch({ searching: false, searchText: "" });
getLibraryNovels();
}}
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
size={24}
/>
<TextInput
@@ -52,7 +52,7 @@ const LibraryAppbar = ({
style={[
styles.searchBar,
{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
},
]}
autoFocus
@@ -70,7 +70,7 @@ const LibraryAppbar = ({
setSearch({ ...search, searchText: "" });
getLibraryNovels();
}}
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
/>
)}
</>
@@ -1,52 +0,0 @@
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { useSelector } from "react-redux";
const EmptyView = () => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
<View style={styles.emptyViewContainer}>
<Text
style={[
styles.emptyViewIcon,
{
color: theme.textColorSecondaryDark,
},
]}
>
Σ(ಠ_ಠ)
</Text>
<Text
style={[
styles.emptyViewText,
{
color: theme.textColorSecondaryDark,
},
]}
>
Your library is empty. Add series to your library from Browse.
</Text>
</View>
);
};
export default EmptyView;
const styles = StyleSheet.create({
emptyViewContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
emptyViewIcon: {
fontSize: 45,
fontWeight: "bold",
},
emptyViewText: {
fontWeight: "bold",
marginTop: 10,
textAlign: "center",
paddingHorizontal: 30,
},
});
+17 -20
View File
@@ -2,7 +2,7 @@ import React from "react";
import * as Linking from "expo-linking";
import { List, Divider } from "react-native-paper";
import { CustomAppbar } from "../../components/common/Appbar";
import { Appbar } from "../../components/common/Appbar";
import { useSelector } from "react-redux";
@@ -11,46 +11,43 @@ const AboutScreen = ({ navigation }) => {
return (
<>
<CustomAppbar
title="About"
onBackAction={() => navigation.goBack()}
/>
<Appbar title="About" onBackAction={() => navigation.goBack()} />
<List.Section
style={{
flex: 1,
marginTop: 0,
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
marginBottom: 0,
}}
>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
title="Version"
descriptionStyle={{ color: theme.textColorSecondaryDark }}
description="Stable 1.0.1"
descriptionStyle={{ color: theme.textColorSecondary }}
description="Stable 1.0.3"
/>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
title="Build Time"
descriptionStyle={{ color: theme.textColorSecondaryDark }}
description="15-03-21 10:30 AM"
descriptionStyle={{ color: theme.textColorSecondary }}
description="20-03-21 14:12 PM"
/>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
title="What's new"
onPress={() =>
Linking.openURL(
"https://github.com/rajarsheechatterjee/lnreader/commits/main"
)
}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
<Divider />
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
descriptionStyle={{ color: theme.textColorSecondaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
descriptionStyle={{ color: theme.textColorSecondary }}
title="Github"
description="https://github.com/rajarsheechatterjee/lnreader"
onPress={() =>
@@ -58,11 +55,11 @@ const AboutScreen = ({ navigation }) => {
"https://github.com/rajarsheechatterjee/lnreader"
)
}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
descriptionStyle={{ color: theme.textColorSecondaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
descriptionStyle={{ color: theme.textColorSecondary }}
title="Extensions"
description="https://github.com/rajarsheechatterjee/lnreader-extensions"
descriptionNumberOfLines={1}
@@ -71,7 +68,7 @@ const AboutScreen = ({ navigation }) => {
"https://github.com/rajarsheechatterjee/lnreader-extensions"
)
}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
</List.Section>
</>
+5 -5
View File
@@ -3,7 +3,7 @@ import React from "react";
import { StyleSheet } from "react-native";
import { List } from "react-native-paper";
import { CustomAppbar } from "../../components/common/Appbar";
import { Appbar } from "../../components/common/Appbar";
import { useSelector } from "react-redux";
@@ -12,17 +12,17 @@ const MoreScreen = ({ navigation }) => {
return (
<>
<CustomAppbar title="More" />
<Appbar title="More" />
<List.Section
style={{
flex: 1,
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
marginTop: 0,
marginBottom: 0,
}}
>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
title="Settings"
left={() => (
<List.Icon
@@ -33,7 +33,7 @@ const MoreScreen = ({ navigation }) => {
onPress={() => navigation.navigate("Settings")}
/>
<List.Item
titleStyle={{ color: theme.textColorPrimaryDark }}
titleStyle={{ color: theme.textColorPrimary }}
title="About"
left={() => (
<List.Icon
+56 -142
View File
@@ -1,13 +1,13 @@
import React, { useState } from "react";
import { View, Text } from "react-native";
import { View } from "react-native";
import { setStatusBarStyle } from "expo-status-bar";
import { List, Modal, Provider, Portal } from "react-native-paper";
import { List, Modal, Portal } from "react-native-paper";
import { connect } from "react-redux";
import { switchTheme } from "../../redux/actions/theme";
import { setDisplayMode, setItemsPerRow } from "../../redux/actions/settings";
import { CustomAppbar } from "../../components/common/Appbar";
import { Appbar } from "../../components/common/Appbar";
import {
DisplayCheckbox,
ThemeCheckbox,
@@ -26,54 +26,47 @@ const SettingsScreen = ({
displayMode,
switchTheme,
setDisplayMode,
setItemsPerRow,
}) => {
const label = {
const display = {
0: "Compact",
1: "Comfortable",
};
const themes = {
0: "AMOLED Dark Theme",
1: "Light Theme",
2: "Dark Theme",
3: "Midnight Dusk Theme",
};
const themes = [
{ themeCode: 0, label: "AMOLED Dark Theme", statusBar: "light" },
{ themeCode: 1, label: "Light Theme", statusBar: "dark" },
{ themeCode: 2, label: "Dark Theme", statusBar: "light" },
{ themeCode: 3, label: "Midnight Dusk Theme", statusBar: "light" },
];
const desciptionStyles = {
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
};
const titleStyles = { color: theme.textColorPrimaryDark };
const titleStyles = { color: theme.textColorPrimary };
// Display Mode Modal
/**
* Display Mode Modal
*/
const [displayModalVisible, setDisplayModalVisible] = useState(false);
const showDisplayModal = () => setDisplayModalVisible(true);
const hideDisplayModal = () => setDisplayModalVisible(false);
// Theme Modal
/**
* Change Theme Modal
*/
const [themeModalVisible, setthemeModalVisible] = useState(false);
const showthemeModal = () => setthemeModalVisible(true);
const hidethemeModal = () => setthemeModalVisible(false);
// Items Modal
const [itemsModalVisible, setitemsModalVisible] = useState(false);
const showitemsModal = () => setitemsModalVisible(true);
const hideitemsModal = () => setitemsModalVisible(false);
return (
<Provider>
<CustomAppbar
title="Settings"
onBackAction={() => navigation.goBack()}
/>
<View
style={{ flex: 1, backgroundColor: theme.colorDarkPrimaryDark }}
>
<>
<Appbar title="Settings" onBackAction={() => navigation.goBack()} />
<View style={{ flex: 1, backgroundColor: theme.colorPrimaryDark }}>
<List.Section
style={{
flex: 1,
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
}}
>
<List.Subheader
@@ -90,7 +83,7 @@ const SettingsScreen = ({
descriptionStyle={desciptionStyles}
description="Delete history for novels not in your library"
onPress={() => deleteNovelsNotInLibrary()}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
<List.Item
titleStyle={titleStyles}
@@ -98,7 +91,7 @@ const SettingsScreen = ({
descriptionStyle={desciptionStyles}
description="Delete reading history for all novels"
onPress={() => deleteHistory()}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
<List.Subheader
style={{
@@ -112,94 +105,33 @@ const SettingsScreen = ({
titleStyle={titleStyles}
title="Display Mode"
descriptionStyle={desciptionStyles}
description={label[displayMode]}
description={display[displayMode]}
onPress={showDisplayModal}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
<Portal>
<Modal
visible={displayModalVisible}
onDismiss={hideDisplayModal}
contentContainerStyle={{
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
padding: 20,
margin: 20,
borderRadius: 6,
}}
>
<View>
<DisplayCheckbox
value={1}
displayMode={displayMode}
onPress={() => setDisplayMode(1)}
/>
</View>
<View>
<DisplayCheckbox
value={0}
displayMode={displayMode}
onPress={() => setDisplayMode(0)}
/>
</View>
<DisplayCheckbox
value={1}
displayMode={displayMode}
onPress={() => setDisplayMode(1)}
/>
<DisplayCheckbox
value={0}
displayMode={displayMode}
onPress={() => setDisplayMode(0)}
/>
</Modal>
</Portal>
{/* <List.Item
titleStyle={titleStyles}
title="Items per row"
descriptionStyle={desciptionStyles}
// description={label[displayMode]}
onPress={showitemsModal}
rippleColor={theme.rippleColorDark}
/>
<Portal>
<Modal
visible={itemsModalVisible}
onDismiss={hideitemsModal}
contentContainerStyle={{
backgroundColor: theme.colorDarkPrimaryDark,
padding: 20,
margin: 20,
borderRadius: 6,
}}
>
<Text
style={{
color: theme.textColorPrimaryDark,
padding: 20,
}}
onPress={() => setItemsPerRow(1)}
>
1
</Text>
<Text
style={{
color: theme.textColorPrimaryDark,
padding: 20,
}}
onPress={() => setItemsPerRow(2)}
>
2
</Text>
<Text
style={{
color: theme.textColorPrimaryDark,
padding: 20,
}}
onPress={() => setItemsPerRow(3)}
>
3
</Text>
<Text
style={{
color: theme.textColorPrimaryDark,
padding: 20,
}}
onPress={() => setItemsPerRow(4)}
>
4
</Text>
</Modal>
</Portal> */}
{/* <List.Subheader
style={{
color: theme.colorAccentDark,
@@ -213,7 +145,7 @@ const SettingsScreen = ({
title="Delete database"
descriptionStyle={desciptionStyles}
description="Delete entire database"
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
onPress={() => deleteDatabase()}
/> */}
@@ -229,58 +161,40 @@ const SettingsScreen = ({
titleStyle={titleStyles}
title="Theme"
descriptionStyle={desciptionStyles}
description={themes[themeCode]}
description={themes[themeCode].label}
onPress={showthemeModal}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
/>
<Portal>
<Modal
visible={themeModalVisible}
onDismiss={hidethemeModal}
contentContainerStyle={{
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
padding: 20,
margin: 20,
borderRadius: 6,
}}
>
<ThemeCheckbox
label={themes[1]}
checked={themeCode === 1 ? true : false}
onPress={() => {
switchTheme(1);
setStatusBarStyle("dark");
}}
/>
<ThemeCheckbox
label={themes[2]}
checked={themeCode === 2 ? true : false}
onPress={() => {
switchTheme(2);
setStatusBarStyle("light");
}}
/>
<ThemeCheckbox
label={themes[0]}
checked={themeCode === 0 ? true : false}
onPress={() => {
switchTheme(0);
setStatusBarStyle("light");
}}
/>
<ThemeCheckbox
label={themes[3]}
checked={themeCode === 3 ? true : false}
onPress={() => {
switchTheme(3);
setStatusBarStyle("light");
}}
/>
{themes.map((item) => (
<ThemeCheckbox
label={themes[item.themeCode].label}
checked={
themeCode === item.themeCode
? true
: false
}
onPress={() => {
switchTheme(item.themeCode);
setStatusBarStyle(item.statusBar);
}}
/>
))}
</Modal>
</Portal>
</List.Section>
</View>
</Provider>
</>
);
};
+3 -3
View File
@@ -195,7 +195,7 @@ const NovelItem = ({ route, navigation }) => {
<View
style={[
styles.container,
{ backgroundColor: theme.colorDarkPrimaryDark },
{ backgroundColor: theme.colorPrimaryDark },
]}
>
<FlatList
@@ -227,8 +227,8 @@ const NovelItem = ({ route, navigation }) => {
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={[theme.textColorPrimaryDark]}
progressBackgroundColor={theme.colorDarkPrimary}
colors={[theme.textColorPrimary]}
progressBackgroundColor={theme.colorPrimary}
/>
}
/>
+11 -9
View File
@@ -39,14 +39,14 @@ export const BottomSheet = ({
style={[
styles.contentContainer,
{
backgroundColor: theme.colorDarkPrimary,
backgroundColor: theme.colorPrimary,
},
]}
>
<BottomSheetHandle />
<List.Subheader
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
fontSize: 20,
}}
>
@@ -77,7 +77,7 @@ export const BottomSheet = ({
/>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
}}
>
Newest to oldest
@@ -96,7 +96,7 @@ export const BottomSheet = ({
/>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
}}
>
Oldest to newest
@@ -108,7 +108,7 @@ export const BottomSheet = ({
<List.Subheader
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
fontSize: 20,
paddingVertical: 10,
}}
@@ -122,7 +122,7 @@ export const BottomSheet = ({
color={theme.colorAccentDark}
onPress={() => filterChapters("")}
/>
<Text style={{ color: theme.textColorPrimaryDark }}>
<Text style={{ color: theme.textColorPrimary }}>
Show all
</Text>
</View>
@@ -135,7 +135,7 @@ export const BottomSheet = ({
color={theme.colorAccentDark}
onPress={() => filterChapters("AND `read`=1")}
/>
<Text style={{ color: theme.textColorPrimaryDark }}>
<Text style={{ color: theme.textColorPrimary }}>
Show read chapters
</Text>
</View>
@@ -148,7 +148,7 @@ export const BottomSheet = ({
color={theme.colorAccentDark}
onPress={() => filterChapters("AND `read`=0")}
/>
<Text style={{ color: theme.textColorPrimaryDark }}>
<Text style={{ color: theme.textColorPrimary }}>
Show unread chapters
</Text>
</View>
@@ -163,7 +163,7 @@ export const BottomSheet = ({
color={theme.colorAccentDark}
onPress={() => filterChapters("AND downloaded=1")}
/>
<Text style={{ color: theme.textColorPrimaryDark }}>
<Text style={{ color: theme.textColorPrimary }}>
Show downloaded chapters
</Text>
</View>
@@ -175,5 +175,7 @@ export const BottomSheet = ({
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
borderTopRightRadius: 8,
borderTopLeftRadius: 8,
},
});
+4 -4
View File
@@ -36,14 +36,14 @@ const ChapterCard = ({
chapterName: chapter.chapterName,
})
}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
>
<>
<View>
<Text
style={[
{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
},
chapter.read === 1 && {
color: theme.textColorHintDark,
@@ -57,7 +57,7 @@ const ChapterCard = ({
<Text
style={[
{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
marginTop: 5,
fontSize: 12,
},
@@ -89,7 +89,7 @@ const ChapterCard = ({
animated
color={
downloaded
? theme.textColorPrimaryDark
? theme.textColorPrimary
: theme.textColorHintDark
}
size={25}
+16 -16
View File
@@ -38,7 +38,7 @@ const NovelInfoHeader = ({
{
color: theme.colorAccentDark,
borderColor: theme.colorAccentDark,
backgroundColor: theme.colorDarkPrimary,
backgroundColor: theme.colorPrimary,
},
]}
>
@@ -55,7 +55,7 @@ const NovelInfoHeader = ({
style={styles.background}
>
<LinearGradient
colors={["rgba(0,0,0,0.2)", theme.colorDarkPrimaryDark]}
colors={["rgba(0,0,0,0.2)", theme.colorPrimaryDark]}
style={styles.linearGradient}
>
<View style={styles.detailsContainer}>
@@ -73,7 +73,7 @@ const NovelInfoHeader = ({
style={[
styles.name,
{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
},
]}
>
@@ -83,7 +83,7 @@ const NovelInfoHeader = ({
<>
<Text
style={{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
marginVertical: 3,
fontSize: 14,
fontWeight: "bold",
@@ -98,7 +98,7 @@ const NovelInfoHeader = ({
<Text
style={{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
marginVertical: 3,
fontSize: 14,
}}
@@ -108,7 +108,7 @@ const NovelInfoHeader = ({
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
// marginVertical: 3,
fontSize: 14,
}}
@@ -147,10 +147,10 @@ const NovelInfoHeader = ({
onPress={() => insertNovelInLib()}
style={[
{
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
marginRight: 2,
// borderColor: "rgba(255,255,255,0.121)",
// borderWidth: 1,
borderWidth: 0,
justifyContent: "center",
height: 30,
alignItems: "center",
@@ -165,7 +165,7 @@ const NovelInfoHeader = ({
]}
textStyle={{
fontWeight: "bold",
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
}}
>
{libraryStatus ? "In Library" : "Add to library"}
@@ -202,7 +202,7 @@ const NovelInfoHeader = ({
>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
marginTop: 5,
paddingVertical: 5,
fontSize: 15,
@@ -213,7 +213,7 @@ const NovelInfoHeader = ({
</Text>
<Text
style={{
color: theme.textColorSecondaryDark,
color: theme.textColorSecondary,
lineHeight: 20,
}}
numberOfLines={more ? 100 : 2}
@@ -229,7 +229,7 @@ const NovelInfoHeader = ({
position: "absolute",
bottom: 0,
right: 15,
backgroundColor: theme.colorDarkPrimaryDark,
backgroundColor: theme.colorPrimaryDark,
paddingLeft: 5,
}}
onPress={() => setMore(!more)}
@@ -291,12 +291,12 @@ const NovelInfoHeader = ({
onPress={() =>
bottomSheetRef.current.show({ velocity: -1.5 })
}
rippleColor={theme.rippleColorDark}
rippleColor={theme.rippleColor}
>
<>
<Text
style={{
color: theme.textColorPrimaryDark,
color: theme.textColorPrimary,
paddingHorizontal: 15,
paddingVertical: 5,
fontSize: 16,
@@ -307,7 +307,7 @@ const NovelInfoHeader = ({
</Text>
<IconButton
icon="filter-variant"
color={theme.textColorPrimaryDark}
color={theme.textColorPrimary}
size={24}
/>
</>
@@ -332,7 +332,7 @@ const styles = StyleSheet.create({
},
background: {
height: 285,
// backgroundColor: theme.colorDarkPrimaryDark,
// backgroundColor: theme.colorPrimaryDark,
},
linearGradient: {
// flex: 1,
+28 -134
View File
@@ -1,70 +1,38 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, useRef } from "react";
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
import { Appbar, Provider, Portal } from "react-native-paper";
import { Provider, Portal } from "react-native-paper";
import NovelCover from "../../../components/common/NovelCover";
import SearchBar from "../../../components/common/SearchBar";
import { SearchAppbar } from "../../../components/common/Appbar";
import { BottomSheet } from "./filters/BottomSheet";
import { useSelector } from "react-redux";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const BoxNovel = ({ navigation }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const library = useSelector((state) => state.libraryReducer.novels);
const AllNovels = ({ navigation }) => {
const [loading, setLoading] = useState(true);
const [loadingMore, setLoadingMore] = useState(false);
const [novels, setNovels] = useState([]);
const [libraryNovels, setlibraryNovels] = useState([]);
const [sort, setSort] = useState("rating");
const [pageNo, setPageNo] = useState(1);
const [searchBar, setSearchBar] = useState(false);
const [searchText, setSearchText] = useState("");
const [searched, setSearched] = useState(0);
const theme = useSelector((state) => state.themeReducer.theme);
let bottomSheetRef = useRef(null);
const getNovels = () => {
fetch(
`https://lnreader-extensions.herokuapp.com/api/1/novels/${pageNo}/?o=${sort}`
`https://lnreader-extensions.herokuapp.com/api/1/novels/1/?o=${sort}`
)
.then((response) => response.json())
.then((json) => {
setPageNo(pageNo + 1);
setNovels((novels) => novels.concat(json));
setNovels(json);
setLoading(false);
});
};
const getLibraryNovels = () => {
db.transaction((tx) => {
tx.executeSql(
"SELECT novelUrl FROM LibraryTable WHERE libraryStatus = 1 AND extensionId = 1",
null,
(tx, { rows: { _array } }) => {
setlibraryNovels(_array);
},
(tx, error) => console.log(error)
);
});
};
const onEndReached = ({
layoutMeasurement,
contentOffset,
contentSize,
}) => {
const paddingToBottom = 5;
return (
layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom
);
};
const getSearchResults = (searchText) => {
setLoading(true);
fetch(
@@ -78,92 +46,33 @@ const AllNovels = ({ navigation }) => {
};
const checkIFInLibrary = (id) => {
return libraryNovels.some((obj) => obj.novelUrl === id);
return library.some((obj) => obj.novelUrl === id);
};
useEffect(() => {
getLibraryNovels();
getNovels();
}, [sort]);
const sortNovels = () => {
setLoading(true);
if (searched) {
getSearchResults();
} else {
getNovels();
}
};
return (
<Provider>
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
{!searchBar ? (
<>
<Appbar.BackAction
onPress={() => navigation.goBack()}
color={theme.textColorPrimaryDark}
/>
<Appbar.Content
title="Box Novel"
titleStyle={{ color: theme.textColorPrimaryDark }}
/>
<Appbar.Action
icon="magnify"
onPress={() => setSearchBar(true)}
color={theme.textColorPrimaryDark}
/>
<Appbar.Action
icon="filter-variant"
onPress={() => _panel.show({ velocity: -1.5 })}
color={theme.textColorPrimaryDark}
/>
</>
) : (
<>
<Appbar.BackAction
onPress={() => {
if (searched) {
setLoading(true);
setPageNo(1);
setNovels([]);
getNovels();
}
setSearchBar(false);
setSearchText("");
}}
color={theme.textColorPrimaryDark}
/>
<SearchBar
searchText={searchText}
onChangeText={(text) => setSearchText(text)}
onSubmitEditing={() => {
if (searchText !== "") {
setSort("rating");
getSearchResults(searchText);
setSearched(true);
}
}}
/>
{searchText !== "" && (
<Appbar.Action
icon="close"
onPress={() => {
setSearchText("");
}}
color={theme.textColorPrimaryDark}
/>
)}
</>
)}
</Appbar.Header>
<View
style={[
styles.container,
{ backgroundColor: theme.colorDarkPrimaryDark },
{ backgroundColor: theme.colorPrimaryDark },
]}
>
<SearchAppbar
screen="Extension"
placeholder="Search BoxNovel"
searchText={searchText}
setSearchText={setSearchText}
getSearchResults={getSearchResults}
getNovels={getNovels}
setLoading={setLoading}
onFilter={() =>
bottomSheetRef.current.show({ velocity: -1.5 })
}
/>
{loading ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator
@@ -178,15 +87,6 @@ const AllNovels = ({ navigation }) => {
data={novels}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.novelUrl}
ListFooterComponent={() =>
loadingMore && (
<ActivityIndicator
color={theme.colorAccentDark}
size="small"
style={{ marginTop: 20, marginBottom: 80 }}
/>
)
}
renderItem={({ item }) => (
<NovelCover
item={item}
@@ -203,33 +103,27 @@ const AllNovels = ({ navigation }) => {
}
/>
)}
onScroll={({ nativeEvent }) => {
if (onEndReached(nativeEvent)) {
setLoadingMore(true);
getNovels();
console.log("End Reached");
}
}}
/>
)}
</View>
<Portal>
<BottomSheet
bottomSheetRef={(c) => (_panel = c)}
bottomSheetRef={bottomSheetRef}
setSort={setSort}
sort={sort}
setLoading={setLoading}
/>
</Portal>
</Provider>
);
};
export default AllNovels;
export default BoxNovel;
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 3,
padding: 4,
},
contentContainer: {
@@ -5,14 +5,18 @@ import Bottomsheet from "rn-sliding-up-panel";
import { useSelector } from "react-redux";
export const BottomSheet = ({
bottomSheetRef,
setSort,
sort,
setRefreshing,
}) => {
export const BottomSheet = ({ bottomSheetRef, setSort, sort, setLoading }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const sorting = [
{ label: "Latest", sortFlag: "latest" },
{ label: "A-Z", sortFlag: "aplhabet" },
{ label: "Rating", sortFlag: "rating" },
{ label: "Trending", sortFlag: "trending" },
{ label: "Most Views", sortFlag: "views" },
{ label: "New", sortFlag: "new-manga" },
];
return (
<Bottomsheet
ref={bottomSheetRef}
@@ -23,7 +27,7 @@ export const BottomSheet = ({
style={[
styles.contentContainer,
{
backgroundColor: theme.colorDarkPrimary,
backgroundColor: theme.colorPrimary,
},
]}
>
@@ -49,129 +53,31 @@ export const BottomSheet = ({
}}
expanded
>
<List.Item
right={(props) =>
sort === "latest" && (
<List.Icon
{...props}
size
icon="arrow-up"
color={theme.colorAccentDark}
/>
)
}
title="Latest"
titleStyle={{
fontSize: 15,
color: theme.textColorPrimaryDark,
}}
onPress={() => setSort("latest")}
/>
<List.Item
right={(props) =>
sort === "alphabet" && (
<List.Icon
{...props}
size
icon="arrow-up"
color={theme.colorAccentDark}
/>
)
}
title="A-Z"
titleStyle={{
fontSize: 15,
color: theme.textColorPrimaryDark,
}}
onPress={() => {
setSort("alphabet");
setRefreshing(true);
}}
/>
<List.Item
right={(props) =>
sort === "rating" && (
<List.Icon
{...props}
size
icon="arrow-up"
color={theme.colorAccentDark}
/>
)
}
title="Rating"
titleStyle={{
fontSize: 15,
color: theme.textColorPrimaryDark,
}}
onPress={() => {
setSort("rating");
setRefreshing(true);
}}
/>
<List.Item
right={(props) =>
sort === "trending" && (
<List.Icon
{...props}
size
icon="arrow-up"
color={theme.colorAccentDark}
/>
)
}
title="Trending"
titleStyle={{
fontSize: 15,
color: theme.textColorPrimaryDark,
}}
onPress={() => {
setSort("trending");
setRefreshing(true);
}}
/>
<List.Item
right={(props) =>
sort === "views" && (
<List.Icon
{...props}
size
icon="arrow-up"
color={theme.colorAccentDark}
/>
)
}
title="Most Views"
titleStyle={{
fontSize: 15,
color: theme.textColorPrimaryDark,
}}
onPress={() => {
setSort("views");
setRefreshing(true);
}}
/>
<List.Item
right={(props) =>
sort === "new-manga" && (
<List.Icon
{...props}
size
icon="arrow-up"
color={theme.colorAccentDark}
/>
)
}
title="New"
titleStyle={{
fontSize: 15,
color: theme.textColorPrimaryDark,
}}
onPress={() => {
setSort("new-manga");
setRefreshing(true);
}}
/>
{sorting.map((item) => (
<List.Item
right={() =>
sort === item.sortFlag && (
<List.Icon
icon="arrow-up"
color={theme.colorAccentDark}
style={{
marginVertical: 0,
paddingVertical: 0,
}}
/>
)
}
title={item.label}
titleStyle={{
fontSize: 15,
color: theme.textColorPrimary,
}}
onPress={() => {
setLoading(true);
setSort(item.sortFlag);
}}
/>
))}
</List.Section>
</View>
</Bottomsheet>
@@ -1,28 +1,23 @@
import React, { useEffect, useState } from "react";
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
import { Appbar, Provider } from "react-native-paper";
import { Provider } from "react-native-paper";
import NovelCover from "../../../components/common/NovelCover";
import SearchBar from "../../../components/common/SearchBar";
import { SearchAppbar } from "../../../components/common/Appbar";
import { useSelector } from "react-redux";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
import EmptyView from "../../../components/common/EmptyView";
const ReadLightNovel = ({ navigation }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const library = useSelector((state) => state.libraryReducer.novels);
const [loading, setLoading] = useState(true);
const [novels, setNovels] = useState();
const [libraryNovels, setlibraryNovels] = useState([]);
const [searchBar, setSearchBar] = useState(false);
const [searchText, setSearchText] = useState("");
const [searched, setSearched] = useState(false);
const getNovels = () => {
fetch(`https://lnreader-extensions.herokuapp.com/api/2/novels/`)
.then((response) => response.json())
@@ -44,95 +39,32 @@ const ReadLightNovel = ({ navigation }) => {
});
};
const getLibraryNovels = () => {
db.transaction((tx) => {
tx.executeSql(
"SELECT novelUrl FROM LibraryTable WHERE libraryStatus = 1 AND extensionId = 2",
null,
(tx, { rows: { _array } }) => {
setlibraryNovels(_array);
},
(tx, error) => console.log(error)
);
});
};
const checkIFInLibrary = (id) => {
return libraryNovels.some((obj) => obj.novelUrl === id);
return library.some((obj) => obj.novelUrl === id);
};
useEffect(() => {
getNovels();
getLibraryNovels();
}, []);
return (
<Provider>
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
{!searchBar ? (
<>
<Appbar.BackAction
color={theme.textColorPrimaryDark}
onPress={() => navigation.goBack()}
/>
<Appbar.Content
color={theme.textColorPrimaryDark}
title="ReadLightNovel"
titleStyle={{ color: theme.textColorPrimaryDark }}
/>
<Appbar.Action
color={theme.textColorPrimaryDark}
icon="magnify"
onPress={() => setSearchBar(true)}
/>
<Appbar.Action
icon="filter-variant"
disabled
color={theme.textColorPrimaryDark}
/>
</>
) : (
<>
<Appbar.BackAction
onPress={() => {
if (searched) {
setLoading(true);
getNovels();
}
setSearchBar(false);
setSearchText("");
}}
color={theme.textColorPrimaryDark}
/>
<SearchBar
searchText={searchText}
onChangeText={(text) => setSearchText(text)}
onSubmitEditing={() => {
if (searchText !== "") {
getSearchResults(searchText);
setSearched(true);
}
}}
/>
{searchText !== "" && (
<Appbar.Action
icon="close"
onPress={() => {
setSearchText("");
}}
color={theme.textColorPrimaryDark}
/>
)}
</>
)}
</Appbar.Header>
<View
style={[
styles.container,
{ backgroundColor: theme.colorDarkPrimaryDark },
{ backgroundColor: theme.colorPrimaryDark },
]}
>
<SearchAppbar
screen="Extension"
placeholder="Search ReadLightNovel"
searchText={searchText}
setSearchText={setSearchText}
getSearchResults={getSearchResults}
getNovels={getNovels}
setLoading={setLoading}
/>
{loading ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator
@@ -142,7 +74,7 @@ const ReadLightNovel = ({ navigation }) => {
</View>
) : (
<FlatList
contentContainerStyle={styles.list}
contentContainerStyle={{ flexGrow: 1 }}
numColumns={3}
data={novels}
showsVerticalScrollIndicator={false}
@@ -163,6 +95,15 @@ const ReadLightNovel = ({ navigation }) => {
}
/>
)}
ListEmptyComponent={
searchText !== "" &&
novels.length === 0 && (
<EmptyView
icon="(; ̄Д ̄)"
description="No results found"
/>
)
}
/>
)}
</View>
+13 -3
View File
@@ -53,12 +53,22 @@ export const fetchChapterFromSource = async (
chapterUrl
) => {
const url = `https://lnreader-extensions.herokuapp.com/api/${extensionId}/novel/${novelUrl}${chapterUrl}`;
console.log(
`https://lnreader-extensions.herokuapp.com/api/${extensionId}/novel/${novelUrl}${chapterUrl}`
);
let res = await fetch(url);
let chapter = await res.json();
return chapter;
};
/**
* Fetch list of extensions
*/
export const fetchExtensionList = async () => {
const url = `https://lnreader-extensions.herokuapp.com/api/`;
let res = await fetch(url);
let extensions = await res.json();
return extensions;
};
+29 -25
View File
@@ -2,63 +2,67 @@
* Application Colors
*/
export const theme = {
colorDarkPrimary: "#000000",
colorDarkPrimaryDark: "#000000",
colorPrimary: "#000000",
colorPrimaryDark: "#000000",
colorAccentDark: "#3399FF",
textColorPrimaryDark: "#FFFFFF",
textColorSecondaryDark: "rgba(255,255,255,0.7)",
textColorPrimary: "#FFFFFF",
textColorSecondary: "rgba(255,255,255,0.7)",
textColorHintDark: "rgba(255,255,255,0.5)",
rippleColorDark: "rgba(255,255,255,0.2)",
rippleColor: "rgba(255,255,255,0.2)",
};
/**
* Light Theme
*/
export const lightTheme = {
colorDarkPrimary: "#FFFFFF",
colorDarkPrimaryDark: "#FAFAFA",
colorPrimary: "#FFFFFF",
colorPrimaryDark: "#FAFAFA",
colorAccentDark: "#2979FF",
textColorPrimaryDark: "#000000",
textColorSecondaryDark: "rgba(0,0,0,0.54)",
textColorPrimary: "#000000",
textColorSecondary: "rgba(0,0,0,0.54)",
textColorHintDark: "rgba(0,0,0,0.38)",
rippleColorDark: "#C2C2C2",
rippleColor: "#C2C2C2",
searchBarColor: "#FFFFFF",
};
/**
* Dark Theme
*/
export const darkTheme = {
colorDarkPrimary: "#242529",
colorDarkPrimaryDark: "#202125",
colorPrimary: "#242529",
colorPrimaryDark: "#202125",
colorAccentDark: "#3399FF",
textColorPrimaryDark: "#FFFFFF",
textColorSecondaryDark: "rgba(255,255,255,0.7)",
textColorPrimary: "#FFFFFF",
textColorSecondary: "rgba(255,255,255,0.7)",
textColorHintDark: "rgba(255,255,255,0.5)",
rippleColorDark: "rgba(255,255,255,0.2)",
rippleColor: "rgba(255,255,255,0.2)",
searchBarColor: "#303135",
};
/**
* Amoled Dark Theme
*/
export const amoledDarkTheme = {
colorDarkPrimary: "#000000",
colorDarkPrimaryDark: "#000000",
colorPrimary: "#000000",
colorPrimaryDark: "#000000",
colorAccentDark: "#3399FF",
textColorPrimaryDark: "#FFFFFF",
textColorSecondaryDark: "rgba(255,255,255,0.7)",
textColorPrimary: "#FFFFFF",
textColorSecondary: "rgba(255,255,255,0.7)",
textColorHintDark: "rgba(255,255,255,0.5)",
rippleColorDark: "rgba(255,255,255,0.2)",
rippleColor: "rgba(255,255,255,0.2)",
searchBarColor: "#1F1F1F",
};
/**
* Midnight Dusk Theme
*/
export const midnightDuskTheme = {
colorDarkPrimary: "#201F27",
colorDarkPrimaryDark: "#16151D",
colorPrimary: "#201F27",
colorPrimaryDark: "#16151D",
colorAccentDark: "#F02475",
textColorPrimaryDark: "#FFFFFF",
textColorSecondaryDark: "rgba(255,255,255,0.7)",
textColorPrimary: "#FFFFFF",
textColorSecondary: "rgba(255,255,255,0.7)",
textColorHintDark: "rgba(255,255,255,0.5)",
rippleColorDark: "rgba(255,255,255,0.2)",
rippleColor: "rgba(255,255,255,0.2)",
searchBarColor: "#201F27",
};