Add screens, custom font, navigation
This commit is contained in:
@@ -1,11 +1,34 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import Router from "./src/navigation/Router";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import * as Font from "expo-font";
|
||||
import AppLoading from "expo-app-loading";
|
||||
|
||||
const getFonts = () =>
|
||||
Font.loadAsync({
|
||||
"pt-sans-bold": require("./assets/fonts/PTSansNarrow-Bold.ttf"),
|
||||
});
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<Router />
|
||||
</NavigationContainer>
|
||||
);
|
||||
const [fontsLoaded, setFontsLoaded] = useState(false);
|
||||
|
||||
if (fontsLoaded) {
|
||||
return (
|
||||
<>
|
||||
<StatusBar style="light" />
|
||||
<NavigationContainer>
|
||||
<Router />
|
||||
</NavigationContainer>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<AppLoading
|
||||
startAsync={getFonts}
|
||||
onError={() => warn(error)}
|
||||
onFinish={() => setFontsLoaded(true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Generated
+1042
File diff suppressed because it is too large
Load Diff
+35
-30
@@ -1,32 +1,37 @@
|
||||
{
|
||||
"main": "node_modules/expo/AppEntry.js",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"eject": "expo eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-navigation/material-bottom-tabs": "^5.3.10",
|
||||
"@react-navigation/native": "^5.8.10",
|
||||
"@react-navigation/stack": "^5.12.8",
|
||||
"expo": "~40.0.0",
|
||||
"expo-status-bar": "~1.0.3",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
|
||||
"react-native-gesture-handler": "~1.8.0",
|
||||
"react-native-paper": "^4.5.0",
|
||||
"react-native-reanimated": "~1.13.0",
|
||||
"react-native-safe-area-context": "3.1.9",
|
||||
"react-native-screens": "~2.15.0",
|
||||
"react-native-vector-icons": "^7.1.0",
|
||||
"react-native-web": "~0.13.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.9.0"
|
||||
},
|
||||
"private": true
|
||||
"main": "node_modules/expo/AppEntry.js",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"eject": "expo eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-navigation/material-bottom-tabs": "^5.3.10",
|
||||
"@react-navigation/native": "^5.8.10",
|
||||
"@react-navigation/stack": "^5.12.8",
|
||||
"cheerio-without-node-native": "^0.20.2",
|
||||
"expo": "~40.0.0",
|
||||
"expo-app-loading": "^1.0.1",
|
||||
"expo-font": "^8.4.0",
|
||||
"expo-linear-gradient": "^8.4.0",
|
||||
"expo-status-bar": "~1.0.3",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
|
||||
"react-native-gesture-handler": "~1.8.0",
|
||||
"react-native-paper": "^4.5.0",
|
||||
"react-native-reanimated": "~1.13.0",
|
||||
"react-native-safe-area-context": "3.1.9",
|
||||
"react-native-screens": "~2.15.0",
|
||||
"react-native-vector-icons": "^7.1.0",
|
||||
"react-native-web": "~0.13.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.9.0"
|
||||
},
|
||||
"private": true,
|
||||
"proxy": "http://192.168.1.38:5000"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View, Text, ImageBackground } from "react-native";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
|
||||
const NovelCover = ({ item }) => {
|
||||
return (
|
||||
<ImageBackground
|
||||
source={{
|
||||
uri: item.novelCover,
|
||||
}}
|
||||
style={styles.logo}
|
||||
imageStyle={{ borderRadius: 6 }}
|
||||
>
|
||||
<View style={styles.titleContainer}>
|
||||
<LinearGradient
|
||||
colors={["transparent", "black"]}
|
||||
style={styles.linearGradient}
|
||||
>
|
||||
<Text numberOfLines={2} style={styles.title}>
|
||||
{item.novelName}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
export default NovelCover;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
logo: {
|
||||
height: "100%",
|
||||
borderRadius: 6,
|
||||
},
|
||||
titleContainer: {
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
borderRadius: 6,
|
||||
},
|
||||
title: {
|
||||
fontFamily: "pt-sans-bold",
|
||||
fontSize: 15,
|
||||
color: "white",
|
||||
padding: 5,
|
||||
width: "100%",
|
||||
},
|
||||
linearGradient: {
|
||||
borderRadius: 6,
|
||||
},
|
||||
});
|
||||
+108
-4
@@ -1,15 +1,106 @@
|
||||
import React from "react";
|
||||
import { createStackNavigator } from "@react-navigation/stack";
|
||||
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
|
||||
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
|
||||
import Library from "../screens/Library";
|
||||
import Updates from "../screens/Updates";
|
||||
import Browse from "../screens/Browse";
|
||||
import More from "../screens/More";
|
||||
import Search from "../screens/Search";
|
||||
import NovelItem from "../screens/NovelItem";
|
||||
|
||||
import { CardStyleInterpolators } from "@react-navigation/stack";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
const Tab = createMaterialBottomTabNavigator();
|
||||
|
||||
const LibraryStack = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerTintColor: "white",
|
||||
headerStyle: { backgroundColor: "#242529" },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Library" component={Library} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const BrowseStack = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerTintColor: "white",
|
||||
headerStyle: { backgroundColor: "#242529" },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Browse" component={Browse} />
|
||||
<Stack.Screen
|
||||
name="NovelItem"
|
||||
component={NovelItem}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const SearchStack = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerTintColor: "white",
|
||||
headerStyle: { backgroundColor: "#242529" },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="Search"
|
||||
component={Search}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="NovelItem"
|
||||
component={NovelItem}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const UpdatesStack = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerTintColor: "white",
|
||||
headerStyle: { backgroundColor: "#242529" },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Updates" component={Updates} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
const MoreStack = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerTintColor: "white",
|
||||
headerStyle: { backgroundColor: "#242529" },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="More" component={More} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const Router = () => {
|
||||
return (
|
||||
<Tab.Navigator
|
||||
@@ -19,7 +110,7 @@ const Router = () => {
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Library"
|
||||
component={Library}
|
||||
component={LibraryStack}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
@@ -32,7 +123,7 @@ const Router = () => {
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Updates"
|
||||
component={Updates}
|
||||
component={UpdatesStack}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
@@ -43,9 +134,22 @@ const Router = () => {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Search"
|
||||
component={SearchStack}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
name="magnify"
|
||||
color={color}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Browse"
|
||||
component={Browse}
|
||||
component={BrowseStack}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
@@ -58,7 +162,7 @@ const Router = () => {
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="More"
|
||||
component={More}
|
||||
component={MoreStack}
|
||||
options={{
|
||||
tabBarIcon: ({ color }) => (
|
||||
<MaterialCommunityIcons
|
||||
|
||||
+60
-5
@@ -1,8 +1,63 @@
|
||||
import React from "react";
|
||||
import { Text } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
|
||||
import { TouchableRipple } from "react-native-paper";
|
||||
import NovelCover from "../components/NovelCover";
|
||||
|
||||
const MoreScreen = () => {
|
||||
return <Text>Browse</Text>;
|
||||
const BrowseScreen = ({ navigation }) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [novels, setNovels] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
fetch("http://192.168.1.38:5000/api/latest")
|
||||
.then((response) => response.json())
|
||||
.then((json) => setNovels(json))
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{loading ? (
|
||||
<View style={{ flex: 1, justifyContent: "center" }}>
|
||||
<ActivityIndicator size="large" color="white" />
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={styles.list}
|
||||
numColumns={3}
|
||||
data={novels}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableRipple
|
||||
borderless
|
||||
centered
|
||||
rippleColor="rgba(256,256,256,0.3)"
|
||||
style={styles.opac}
|
||||
onPress={() =>
|
||||
navigation.navigate("NovelItem", item)
|
||||
}
|
||||
>
|
||||
<NovelCover item={item} />
|
||||
</TouchableRipple>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreScreen;
|
||||
export default BrowseScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
},
|
||||
opac: {
|
||||
height: 190,
|
||||
flex: 1 / 3,
|
||||
margin: 3.2,
|
||||
},
|
||||
});
|
||||
|
||||
+13
-2
@@ -1,8 +1,19 @@
|
||||
import React from "react";
|
||||
import { Text } from "react-native";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
|
||||
const MoreScreen = () => {
|
||||
return <Text>More</Text>;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={{ color: "#FFFFFF" }}>More</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Image,
|
||||
ActivityIndicator,
|
||||
ImageBackground,
|
||||
} from "react-native";
|
||||
import { Appbar } from "react-native-paper";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
|
||||
const NovelItem = ({ route, navigation }) => {
|
||||
const item = route.params;
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [novel, setNovel] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
fetch("http://192.168.1.38:5000/api/" + item.novelUrl)
|
||||
.then((response) => response.json())
|
||||
.then((json) => setNovel(json))
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<Appbar.Header style={{ backgroundColor: "#242529" }}>
|
||||
<Appbar.BackAction
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
}}
|
||||
color={"white"}
|
||||
size={26}
|
||||
style={{ marginRight: 0 }}
|
||||
/>
|
||||
<Appbar.Content
|
||||
title={item.novelName}
|
||||
titleStyle={{ color: "white" }}
|
||||
/>
|
||||
</Appbar.Header>
|
||||
<View style={styles.container}>
|
||||
{loading ? (
|
||||
<View style={{ flex: 1, justifyContent: "center" }}>
|
||||
<ActivityIndicator size="large" color="white" />
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<ImageBackground
|
||||
source={{
|
||||
uri: item.novelCover,
|
||||
}}
|
||||
style={styles.background}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={["transparent", "#202125"]}
|
||||
style={styles.linearGradient}
|
||||
>
|
||||
<View style={styles.detailsContainer}>
|
||||
<Image
|
||||
source={{
|
||||
uri: item.novelCover,
|
||||
}}
|
||||
style={styles.logo}
|
||||
/>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
</ImageBackground>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NovelItem;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
},
|
||||
background: {
|
||||
height: 240,
|
||||
},
|
||||
linearGradient: {
|
||||
height: "100%",
|
||||
backgroundColor: "rgba(256, 256, 256, 0.5)",
|
||||
},
|
||||
detailsContainer: {
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
margin: 17,
|
||||
},
|
||||
logo: {
|
||||
height: 180,
|
||||
width: 120,
|
||||
margin: 3.2,
|
||||
borderRadius: 6,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,114 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
TextInput,
|
||||
FlatList,
|
||||
ActivityIndicator,
|
||||
} from "react-native";
|
||||
import { Appbar, TouchableRipple } from "react-native-paper";
|
||||
import NovelCover from "../components/NovelCover";
|
||||
|
||||
const SearchScreen = ({ navigation }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [novels, setNovels] = useState([]);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const getNovels = (searchText) => {
|
||||
setLoading(true);
|
||||
setNovels([]);
|
||||
fetch(`http://192.168.1.38:5000/api/search/?s=${searchText}&?o=rating`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => setNovels(json))
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Appbar.Header style={{ backgroundColor: "#202125" }}>
|
||||
<Appbar.BackAction
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
setNovels([]);
|
||||
}}
|
||||
color={"white"}
|
||||
size={26}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Search Light Novels..."
|
||||
defaultValue={searchText}
|
||||
style={{ fontSize: 17, flex: 1, color: "white" }}
|
||||
placeholderTextColor="#e0e0e0"
|
||||
blurOnSubmit={true}
|
||||
onChangeText={(text) => {
|
||||
setSearchText(text);
|
||||
getNovels(searchText);
|
||||
}}
|
||||
/>
|
||||
{searchText !== "" && (
|
||||
<Appbar.Action
|
||||
icon="close"
|
||||
onPress={() => {
|
||||
setSearchText("");
|
||||
}}
|
||||
color={"white"}
|
||||
/>
|
||||
)}
|
||||
</Appbar.Header>
|
||||
<View style={styles.container}>
|
||||
{loading ? (
|
||||
<View style={{ flex: 1, justifyContent: "center" }}>
|
||||
<ActivityIndicator size="large" color="white" />
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={styles.list}
|
||||
numColumns={3}
|
||||
data={novels}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableRipple
|
||||
borderless
|
||||
centered
|
||||
rippleColor="rgba(256,256,256,0.3)"
|
||||
style={styles.opac}
|
||||
onPress={() =>
|
||||
navigation.navigate("NovelItem", item)
|
||||
}
|
||||
>
|
||||
<NovelCover item={item} />
|
||||
</TouchableRipple>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
},
|
||||
textInput: {
|
||||
alignItems: "center",
|
||||
backgroundColor: "#E6E8E9",
|
||||
borderRadius: 10,
|
||||
color: "#8E8E93",
|
||||
flexDirection: "row",
|
||||
fontSize: 17,
|
||||
height: 43,
|
||||
margin: 8,
|
||||
marginVertical: 10,
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
opac: {
|
||||
height: 190,
|
||||
flex: 1 / 3,
|
||||
margin: 3.2,
|
||||
},
|
||||
});
|
||||
+15
-4
@@ -1,8 +1,19 @@
|
||||
import React from "react";
|
||||
import { Text } from "react-native";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
|
||||
const MoreScreen = () => {
|
||||
return <Text>Updates</Text>;
|
||||
const UpdatesScreen = () => {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={{ color: "#FFFFFF" }}>Update</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreScreen;
|
||||
export default UpdatesScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import cheerio from "cheerio-without-node-native";
|
||||
|
||||
const baseUrl = "https://boxnovel.com/";
|
||||
|
||||
export const getLatest = async () => {
|
||||
const response = await fetch(baseUrl); // fetch page
|
||||
const htmlString = await response.text(); // get response text
|
||||
const $ = cheerio.load(htmlString); // parse HTML string
|
||||
let title = $("title");
|
||||
|
||||
console.log(title.text());
|
||||
};
|
||||
Reference in New Issue
Block a user