Refactor search and extension UI
This commit is contained in:
@@ -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>
|
||||
);
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
@@ -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
@@ -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,
|
||||
},
|
||||
});
|
||||
@@ -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",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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", {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user