Minor fixes

This commit is contained in:
Rajarshee Chatterjee
2021-07-04 10:16:29 +05:30
parent c3ef170385
commit 61424e533a
17 changed files with 342 additions and 365 deletions
+24
View File
@@ -0,0 +1,24 @@
name: react-native-android-build-apk
on:
push:
branches:
- main
jobs:
install-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install npm dependencies
run: |
npm install
build-android:
needs: install-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install npm dependencies
run: |
npm install
- name: Build Android Release
run: |
cd android && ./gradlew assembleRelease
+65 -27
View File
@@ -1,9 +1,13 @@
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
import BackgroundService from "react-native-background-actions";
import { fetchChapters, fetchNovel } from "../../services/Source/source";
import { insertChapters } from "./ChapterQueries";
import { showToast } from "../../hooks/showToast";
const insertNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
export const insertNovel = async (novel) => {
@@ -127,33 +131,67 @@ export const restoreLibrary = async (novel) => {
const migrateNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre, followed) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
export const migrateNovel = async (sourceId, novelUrl) => {
const novel = await fetchNovel(sourceId, novelUrl);
try {
const novel = await fetchNovel(sourceId, novelUrl);
db.transaction((tx) =>
tx.executeSql(
migrateNovelQuery,
[
novel.novelUrl,
novel.sourceUrl,
novel.sourceId,
novel.source,
novel.novelName,
novel.novelCover,
novel.novelSummary,
novel.author,
novel.artist,
novel.status,
novel.genre,
1,
],
async (txObj, { insertId }) => {
const chapters = await fetchChapters(
novel.sourceId,
novel.novelUrl
);
await insertChapters(insertId, chapters);
const options = {
taskName: "Migration",
taskTitle: `Migrating ${novel.novelName} to new source`,
taskDesc: novel.source,
taskIcon: {
name: "notification_icon",
type: "drawable",
},
(txObj, error) => console.log("Error ", error)
)
);
color: "#00adb5",
parameters: {
delay: 1000,
},
progressBar: {
max: 1,
value: 0,
indeterminate: true,
},
};
const veryIntensiveTask = async () => {
await new Promise(async (resolve) => {
db.transaction((tx) =>
tx.executeSql(
migrateNovelQuery,
[
novel.novelUrl,
novel.sourceUrl,
novel.sourceId,
novel.source,
novel.novelName,
novel.novelCover,
novel.novelSummary,
novel.author,
novel.artist,
novel.status,
novel.genre,
1,
],
async (txObj, { insertId }) => {
const chapters = await fetchChapters(
novel.sourceId,
novel.novelUrl
);
await insertChapters(insertId, chapters);
resolve();
},
(txObj, error) => console.log("Error ", error)
)
);
});
};
await BackgroundService.start(veryIntensiveTask, options);
await BackgroundService.updateNotification({
progressBar: { value: 1, indeterminate: false },
});
await BackgroundService.stop();
} catch (error) {
showToast(error.message);
}
};
+24 -8
View File
@@ -18,15 +18,13 @@ import {
import SourceCard from "./components/SourceCard";
import { showToast } from "../../hooks/showToast";
import { Searchbar } from "../../components/Searchbar";
import MalCard from "./components/MalCard";
import NovelUpdatesCard from "./components/NovelUpdatesCard";
import EmptyView from "../../components/EmptyView";
import DiscoverCard from "./discover/DiscoverCard";
const Browse = ({ navigation }) => {
const [refreshing, setRefreshing] = useState(false);
const [searchText, setSearchText] = useState("");
// const [globalSearch, setGlobalSearch] = useState(false);
// const [searchResult, setSearchResults] = useState();
let {
sources,
search,
@@ -64,12 +62,12 @@ const Browse = ({ navigation }) => {
dispatch(searchSourcesAction(text));
};
// const onSubmitEditing = () => {};
const renderItem = ({ item }) => (
<SourceCard
item={item}
isPinned={isPinned(item.sourceId)}
dispatch={dispatch}
navigation={navigation}
theme={theme}
/>
);
@@ -124,9 +122,27 @@ const Browse = ({ navigation }) => {
Discover
</Text>
{showNovelUpdates && (
<NovelUpdatesCard theme={theme} />
<DiscoverCard
label="Novel Updates"
onPress={() =>
navigation.navigate(
"BrowseNovelUpdates"
)
}
icon={require("../../../assets/novelupdates.png")}
theme={theme}
/>
)}
{showMyAnimeList && (
<DiscoverCard
label="MyAnimeList"
onPress={() =>
navigation.navigate("BrowseMal")
}
icon={require("../../../assets/mal.png")}
theme={theme}
/>
)}
{showMyAnimeList && <MalCard theme={theme} />}
</>
)}
{pinned.length > 0 && (
-19
View File
@@ -53,25 +53,6 @@ const SourceNovels = ({ navigation, route }) => {
</Text>
}
/>
{/* <Portal>
<MigrationDialog
migrationDialog={migrationDialog}
hideMigrationDialog={hideMigrationDIalog}
theme={theme}
selectedNovel={selectedNovel}
loading={loading}
migrationNovel={migrationNovel}
/>
<SourceToMigrateDialog
selectedNovel={selectedNovel}
sourceToMigrateDialog={sourceToMigrateDialog}
hideSourceToMigrateDialog={hideSourceToMigrateDialog}
sources={sources}
theme={theme}
showMigrationDialog={showMigrationDialog}
selectSourceToMigrate={selectSourceToMigrate}
/>
</Portal> */}
</View>
);
};
-80
View File
@@ -1,80 +0,0 @@
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";
const MalCard = ({ theme }) => {
const navigation = useNavigation();
const navigateToExtension = () => navigation.navigate("BrowseMal");
return (
<TouchableRipple
style={styles.extensionCard}
onPress={navigateToExtension}
rippleColor={theme.rippleColor}
>
<>
<Image
source={require("../../../../assets/mal.png")}
style={styles.extensionIcon}
resizeMode="contain"
/>
<View style={styles.extensionDetails}>
<View>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 14,
}}
>
MyAnimeList
</Text>
</View>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
<Button
labelStyle={{ letterSpacing: 0 }}
uppercase={false}
color={theme.colorAccent}
onPress={navigateToExtension}
>
Browse
</Button>
</View>
</View>
</>
</TouchableRipple>
);
};
export default MalCard;
const styles = StyleSheet.create({
extensionCard: {
flexDirection: "row",
alignItems: "center",
marginVertical: 4,
paddingVertical: 8,
paddingHorizontal: 20,
},
extensionIcon: {
width: 40,
height: 40,
borderRadius: 8,
},
extensionDetails: {
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginLeft: 16,
},
sourceStatus: {
color: "#C14033",
fontSize: 12,
marginLeft: 5,
},
});
@@ -1,80 +0,0 @@
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";
const NovelUpdatesCard = ({ theme }) => {
const navigation = useNavigation();
const navigateToExtension = () => navigation.navigate("BrowseNovelUpdates");
return (
<TouchableRipple
style={styles.extensionCard}
onPress={navigateToExtension}
rippleColor={theme.rippleColor}
>
<>
<Image
source={require("../../../../assets/novelupdates.png")}
style={styles.extensionIcon}
resizeMode="contain"
/>
<View style={styles.extensionDetails}>
<View>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 14,
}}
>
Novel Updates
</Text>
</View>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
<Button
labelStyle={{ letterSpacing: 0 }}
uppercase={false}
color={theme.colorAccent}
onPress={navigateToExtension}
>
Browse
</Button>
</View>
</View>
</>
</TouchableRipple>
);
};
export default NovelUpdatesCard;
const styles = StyleSheet.create({
extensionCard: {
flexDirection: "row",
alignItems: "center",
marginVertical: 4,
paddingVertical: 8,
paddingHorizontal: 20,
},
extensionIcon: {
width: 40,
height: 40,
borderRadius: 8,
},
extensionDetails: {
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginLeft: 16,
},
sourceStatus: {
color: "#C14033",
fontSize: 12,
marginLeft: 5,
},
});
+54 -62
View File
@@ -1,17 +1,13 @@
import React from "react";
import { View, Text, Image, StyleSheet } from "react-native";
import { TouchableRipple, Button, IconButton } from "react-native-paper";
import { useNavigation } from "@react-navigation/native";
import { useDispatch } from "react-redux";
import { View, Text, Image, StyleSheet, Pressable } from "react-native";
import { Button, IconButton } from "react-native-paper";
import { pinSourceAction } from "../../../redux/source/source.actions";
import FastImage from "react-native-fast-image";
const ExtensionCard = ({ item, theme, isPinned }) => {
const ExtensionCard = ({ item, theme, isPinned, dispatch, navigation }) => {
const { sourceId, sourceName, icon, lang } = item;
const navigation = useNavigation();
const dispatch = useDispatch();
const navigateToExtension = () =>
navigation.navigate("Extension", {
sourceId: item.sourceId,
@@ -20,63 +16,59 @@ const ExtensionCard = ({ item, theme, isPinned }) => {
});
return (
<TouchableRipple
<Pressable
style={styles.extensionCard}
onPress={navigateToExtension}
rippleColor={theme.rippleColor}
android_ripple={{ color: theme.rippleColor }}
>
<>
<Image
source={{ uri: icon }}
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,
}}
>
{lang}
</Text>
</View>
<View
style={{ flexDirection: "row", alignItems: "center" }}
<Image
source={{ uri: icon }}
style={styles.extensionIcon}
// resizeMode="contain"
/>
<View style={styles.extensionDetails}>
<View>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 14,
}}
>
<Button
labelStyle={{ letterSpacing: 0 }}
uppercase={false}
color={theme.colorAccent}
onPress={navigateToExtension}
>
Browse
</Button>
<IconButton
icon={isPinned ? "pin" : "pin-outline"}
animated
size={21}
onPress={() => dispatch(pinSourceAction(sourceId))}
color={
isPinned
? theme.colorAccent
: theme.textColorSecondary
}
style={{ margin: 2 }}
/>
</View>
{sourceName}
</Text>
<Text
style={{
color: theme.textColorSecondary,
fontSize: 12,
}}
>
{lang}
</Text>
</View>
</>
</TouchableRipple>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Button
labelStyle={{ letterSpacing: 0 }}
uppercase={false}
color={theme.colorAccent}
onPress={navigateToExtension}
>
Browse
</Button>
<IconButton
icon={isPinned ? "pin" : "pin-outline"}
animated
size={21}
onPress={() => dispatch(pinSourceAction(sourceId))}
color={
isPinned
? theme.colorAccent
: theme.textColorSecondary
}
style={{ margin: 2 }}
/>
</View>
</View>
</Pressable>
);
};
@@ -93,7 +85,7 @@ const styles = StyleSheet.create({
extensionIcon: {
width: 40,
height: 40,
borderRadius: 8,
borderRadius: 4,
},
extensionDetails: {
flex: 1,
+50 -42
View File
@@ -67,49 +67,57 @@ const BrowseMalCard = ({ novel, theme, onPress }) => {
{novel.novelName}
</Text>
<GenreChip theme={theme}>{novel.score}</GenreChip>
<View style={{ flexDirection: "row" }}>
<Text
style={[
styles.genreChip,
{ color: theme.textColorSecondary },
]}
>
<Text
style={[
styles.genreChip,
{
color: theme.textColorPrimary,
fontSize: 14,
},
]}
{novel.info && (
<>
<View style={{ flexDirection: "row" }}>
<Text
style={[
styles.genreChip,
{ color: theme.textColorSecondary },
]}
>
<Text
style={[
styles.genreChip,
{
color: theme.textColorPrimary,
fontSize: 14,
},
]}
>
{`${"Type "}`}
</Text>
{novel.info[1]}
</Text>
</View>
<View
style={{
flexDirection: "row",
marginBottom: 4,
}}
>
{`${"Type "}`}
</Text>
{novel.info[1]}
</Text>
</View>
<View style={{ flexDirection: "row", marginBottom: 4 }}>
<Text
style={[
styles.genreChip,
{ color: theme.textColorSecondary },
]}
>
<Text
style={[
styles.genreChip,
{
color: theme.textColorPrimary,
fontSize: 14,
},
]}
>
{`${"Published "}`}
</Text>
{novel.info[2]}
</Text>
</View>
<Text
style={[
styles.genreChip,
{ color: theme.textColorSecondary },
]}
>
<Text
style={[
styles.genreChip,
{
color: theme.textColorPrimary,
fontSize: 14,
},
]}
>
{`${"Published "}`}
</Text>
{novel.info[2]}
</Text>
</View>
</>
)}
</View>
</Pressable>
</View>
@@ -0,0 +1,58 @@
import React from "react";
import { View, Text, Image, StyleSheet, Pressable } from "react-native";
import { Button } from "react-native-paper";
const DiscoverCard = ({ icon, label, onPress, theme }) => {
return (
<Pressable
style={styles.discoverCard}
onPress={onPress}
android_ripple={{ color: theme.rippleColor }}
>
<Image source={icon} style={styles.discoverIcon} />
<View style={styles.label}>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 14,
}}
>
{label}
</Text>
<Button
labelStyle={{ letterSpacing: 0 }}
uppercase={false}
color={theme.colorAccent}
onPress={onPress}
>
Browse
</Button>
</View>
</Pressable>
);
};
export default DiscoverCard;
const styles = StyleSheet.create({
discoverCard: {
flexDirection: "row",
alignItems: "center",
marginVertical: 4,
paddingVertical: 8,
paddingHorizontal: 20,
},
discoverIcon: {
width: 40,
height: 40,
borderRadius: 4,
},
label: {
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginLeft: 16,
},
});
+1 -11
View File
@@ -1,15 +1,8 @@
import React, { useEffect, useState } from "react";
import {
StyleSheet,
View,
ActivityIndicator,
FlatList,
Button,
} from "react-native";
import { StyleSheet, View, ActivityIndicator, FlatList } from "react-native";
import * as WebBrowser from "expo-web-browser";
import NovelCover from "../../../components/NovelCover";
import { Searchbar } from "../../../components/Searchbar";
import ErrorView from "../../../components/ErrorView";
@@ -29,7 +22,6 @@ const BrowseMalScreen = ({ navigation, route }) => {
const [searchText, setSearchText] = useState("");
const baseUrl = "https://api.jikan.moe/v3/";
const malUrl = "https://myanimelist.net/topmanga.php?type=lightnovels";
const getNovels = async (limit) => {
@@ -155,8 +147,6 @@ const BrowseMalScreen = ({ navigation, route }) => {
) : (
<FlatList
contentContainerStyle={styles.novelsContainer}
// numColumns={getNovelsPerRow()}
// key={getNovelsPerRow()}
data={novels}
keyExtractor={(item) => item.novelName}
renderItem={renderItem}
@@ -63,7 +63,9 @@ const scrapeSearchResults = async (searchTerm) => {
novelId[8].split(".")[0] +
".jpg";
const novel = { novelName, novelCover };
const score = $(this).find("td:nth-child(5)").text().trim();
const novel = { novelName, novelCover, score };
novels.push(novel);
}
@@ -101,7 +101,25 @@ const scrapeSearchResults = async (searchTerm) => {
const novelSummary =
$(this).find(".search_genre")[0].nextSibling.nodeValue;
const novel = { novelName, novelCover, genres, novelSummary };
let chapterCount = "";
$(this)
.find(".search_stats > span")
.each(function () {
let detail = $(this).text();
if (detail.includes("Chapters")) {
chapterCount = detail.replace(" Chapters", "");
}
});
const novel = {
novelName,
novelCover,
genres,
novelSummary,
chapterCount,
};
novels.push(novel);
});
@@ -111,12 +111,12 @@ const MigrationNovelList = ({ data, theme, library, navigation }) => {
textTransform: "none",
}}
theme={{ colors: { primary: theme.colorAccent } }}
onPress={() => {
migrateNovel(
onPress={async () => {
hideMigrateNovelDialog();
await migrateNovel(
selectedNovel.sourceId,
selectedNovel.novelUrl
);
hideMigrateNovelDialog();
showToast(
`${selectedNovel.novelName} migrated to new source.`
);
+38 -15
View File
@@ -1,5 +1,5 @@
import React, { useState } from "react";
import { StyleSheet, View, Text, Dimensions } from "react-native";
import { StyleSheet, View, Text, Dimensions, Pressable } from "react-native";
import { ToggleButton, IconButton, Menu, Switch } from "react-native-paper";
import Slider from "@react-native-community/slider";
import Bottomsheet from "rn-sliding-up-panel";
@@ -68,6 +68,7 @@ const ReaderSheet = ({
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
paddingVertical: 8,
}}
>
<ReaderSettingTitle title="Reader Theme" />
@@ -145,7 +146,7 @@ const ReaderSheet = ({
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
marginTop: 16,
paddingVertical: 8,
}}
>
<ReaderSettingTitle title="Text Align" />
@@ -216,7 +217,7 @@ const ReaderSheet = ({
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
marginTop: 16,
paddingVertical: 8,
}}
>
<ReaderSettingTitle title="Padding" />
@@ -266,7 +267,7 @@ const ReaderSheet = ({
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
marginTop: 16,
paddingVertical: 8,
}}
>
<ReaderSettingTitle title="Line Height" />
@@ -313,13 +314,17 @@ const ReaderSheet = ({
/>
</Row>
</Row>
<Row
<Pressable
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
marginTop: 16,
paddingVertical: 8,
}}
android_ripple={{ color: theme.colorAccent }}
onPress={openFontMenu}
>
<ReaderSettingTitle title="Font Style" />
<Menu
@@ -328,12 +333,11 @@ const ReaderSheet = ({
contentStyle={{ backgroundColor: theme.menuColor }}
anchor={
<Text
onPress={openFontMenu}
style={{
color: "white",
paddingHorizontal: 16,
fontSize: 16,
fontFamily: reader.fontFamily,
paddingHorizontal: 16,
}}
>
{fonts.find(
@@ -367,14 +371,25 @@ const ReaderSheet = ({
/>
))}
</Menu>
</Row>
<Row
</Pressable>
<Pressable
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
marginTop: 16,
paddingVertical: 8,
}}
android_ripple={{ color: theme.colorAccent }}
onPress={() =>
dispatch(
setAppSettings(
"showScrollPercentage",
!showScrollPercentage
)
)
}
>
<ReaderSettingTitle title="Show Progress Percentage" />
<Switch
@@ -389,14 +404,22 @@ const ReaderSheet = ({
}
color={theme.colorAccent}
/>
</Row>
<Row
</Pressable>
<Pressable
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
paddingHorizontal: 16,
marginTop: 16,
paddingVertical: 8,
}}
android_ripple={{ color: theme.colorAccent }}
onPress={() =>
dispatch(
setAppSettings("textSelectable", !selectText)
)
}
>
<View>
<ReaderSettingTitle title="Select Text " />
@@ -421,7 +444,7 @@ const ReaderSheet = ({
}
color={theme.colorAccent}
/>
</Row>
</Pressable>
</View>
</View>
</Bottomsheet>
@@ -24,7 +24,7 @@ const JumpToChapterModal = ({
const onSubmit = () => {
if (!mode) {
if (text <= chapters.length) {
if (text > 0 && text <= chapters.length) {
navigation.navigate("Chapter", {
sourceId: novel.sourceId,
novelUrl: novel.novelUrl,
@@ -113,6 +113,7 @@ const JumpToChapterModal = ({
style={{
flexDirection: "row",
justifyContent: "space-between",
marginTop: 4,
}}
>
<Text style={{ color: theme.textColorPrimary }}>
+1 -1
View File
@@ -119,7 +119,7 @@ const parseNovelAndChapters = async (novelUrl) => {
const chapterUrl = $(this)
.find("a")
.attr("href")
.replace(`/novel/${novelUrl}/`, "");
.replace(`/novel/${novelUrl}`, "");
const chapter = { chapterName, releaseDate, chapterUrl };
-14
View File
@@ -16,9 +16,6 @@ const htmlToText = (html) => {
// Remove content in comments.
.replace(/<!--.*?-->/gim, "")
// Format anchor tags properly.
// e.g.
// input - <a class='ahref' href='http://pinetechlabs.com/' title='asdfqwer\"><b>asdf</b></a>
// output - asdf (http://pinetechlabs.com/)
.replace(
/<\s*a[^>]*href=['"](.*?)['"][^>]*>([\s\S]*?)<\/\s*a\s*>/gi,
"$2"
@@ -41,29 +38,18 @@ const htmlToText = (html) => {
};
const decodeHtmlEntity = (m, n) => {
// Determine the character code of the entity. Range is 0 to 65535
// (characters in JavaScript are Unicode, and entities can represent
// Unicode characters).
let code;
// Try to parse as numeric entity. This is done before named entities for
// speed because associative array lookup in many JavaScript implementations
// is a linear search.
if (n.substr(0, 1) == "#") {
// Try to parse as numeric entity
if (n.substr(1, 1) == "x") {
// Try to parse as hexadecimal
code = parseInt(n.substr(2), 16);
} else {
// Try to parse as decimal
code = parseInt(n.substr(1), 10);
}
} else {
// Try to parse as named entity
code = ENTITIES_MAP[n];
}
// If still nothing, pass entity through
return code === undefined || code === NaN
? "&" + n + ";"
: String.fromCharCode(code);