diff --git a/package-lock.json b/package-lock.json
index a708cca7..ab1b3f44 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "lnreader",
- "version": "1.0.0",
+ "version": "1.1.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "lnreader",
- "version": "1.0.0",
+ "version": "1.1.5",
"dependencies": {
"@react-native-async-storage/async-storage": "^1.13.0",
"@react-native-community/masked-view": "0.1.10",
@@ -28,6 +28,7 @@
"expo-updates": "~0.5.4",
"expo-web-browser": "~9.1.0",
"moment": "^2.29.1",
+ "prop-types": "^15.7.2",
"qs": "^6.10.1",
"react": "16.13.1",
"react-dom": "16.13.1",
diff --git a/package.json b/package.json
index 1b386340..b48b6665 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"expo-updates": "~0.5.4",
"expo-web-browser": "~9.1.0",
"moment": "^2.29.1",
+ "prop-types": "^15.7.2",
"qs": "^6.10.1",
"react": "16.13.1",
"react-dom": "16.13.1",
diff --git a/src/components/Appbar.js b/src/components/Appbar.js
index 7b825877..b4993b00 100644
--- a/src/components/Appbar.js
+++ b/src/components/Appbar.js
@@ -1,8 +1,10 @@
import React from 'react';
+import PropTypes from 'prop-types';
import {Appbar as MaterialAppbar} from 'react-native-paper';
import {useTheme} from '../hooks/reduxHooks';
+import {ViewPropTypes} from 'react-native';
export const Appbar = ({title, onBackAction, style, children}) => {
const theme = useTheme();
@@ -20,3 +22,9 @@ export const Appbar = ({title, onBackAction, style, children}) => {
);
};
+
+Appbar.propTypes = {
+ title: PropTypes.string,
+ onBackAction: PropTypes.func,
+ style: ViewPropTypes.style,
+};
diff --git a/src/components/Chip/Chip.js b/src/components/Chip/Chip.js
new file mode 100644
index 00000000..be57cc9b
--- /dev/null
+++ b/src/components/Chip/Chip.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import {View, Text, Pressable, StyleSheet} from 'react-native';
+
+import PropTypes from 'prop-types';
+
+export const Chip = ({label, onPress, theme}) => {
+ return (
+
+
+
+ {label}
+
+
+
+ );
+};
+
+Chip.propTypes = {
+ label: PropTypes.string,
+ onPress: PropTypes.func,
+ theme: PropTypes.object.isRequired,
+};
+
+const styles = StyleSheet.create({
+ container: {
+ borderRadius: 8,
+ overflow: 'hidden',
+ alignSelf: 'flex-start',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: 32,
+ marginHorizontal: 4,
+ elevation: 1,
+ },
+ content: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingHorizontal: 16,
+ },
+ label: {
+ fontSize: 14,
+ },
+});
diff --git a/src/components/NovelCover.js b/src/components/NovelCover.js
index 1efca1eb..568e9b8a 100644
--- a/src/components/NovelCover.js
+++ b/src/components/NovelCover.js
@@ -1,4 +1,4 @@
-import React, {useCallback} from 'react';
+import React, {memo, useCallback} from 'react';
import {
StyleSheet,
View,
@@ -139,7 +139,7 @@ const NovelCover = ({
);
};
-export default NovelCover;
+export default memo(NovelCover);
const ComfortableTitle = ({theme, novelName}) => (
searchRef.current.focus()}
- style={[
- styles.searchbarContainer,
- {backgroundColor: theme.searchBarColor},
- ]}
- rippleColor={theme.rippleColor}
- >
-
+
+ searchRef.current.focus()}
+ style={[styles.pressable, {backgroundColor: theme.searchBarColor}]}
+ android_ripple={{color: theme.rippleColor}}
+ >
{backAction && (
))}
-
-
+
+
);
};
const styles = StyleSheet.create({
- searchbarContainer: {
+ searchBar: {
marginTop: StatusBar.currentHeight + 8,
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 12,
marginBottom: 8,
- borderRadius: 50,
marginHorizontal: 16,
+ borderRadius: 50,
+ overflow: 'hidden',
elevation: 1,
},
- container: {
- flex: 1,
+ pressable: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
+ paddingHorizontal: 16,
},
});
diff --git a/src/components/Typography/Typography.js b/src/components/Typography/Typography.js
new file mode 100644
index 00000000..b9d66569
--- /dev/null
+++ b/src/components/Typography/Typography.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {Text as NativeText, TextPropTypes} from 'react-native';
+
+const Text = ({type, children, style, theme}) => {
+ const color =
+ type === 'primary'
+ ? theme.textColorPrimary
+ : type === 'secondary'
+ ? theme.textColorSecondary
+ : theme.textColorHint;
+
+ return {children};
+};
+
+Text.propType = {
+ style: TextPropTypes.style,
+ type: PropTypes.oneOf(['primary', 'secondary', 'hint']),
+ theme: PropTypes.object.isRequired,
+};
diff --git a/src/database/DBHelper.js b/src/database/DBHelper.js
index 85c58983..7bf92e82 100644
--- a/src/database/DBHelper.js
+++ b/src/database/DBHelper.js
@@ -35,7 +35,9 @@ export const createDB = () => {
tx.executeSql(createDownloadTableQuery);
tx.executeSql(createUpdatesTableQuery);
- //Db indexes
+ /**
+ * Indexes
+ */
tx.executeSql(createUrlIndexQuery);
tx.executeSql(createLibraryIndexQuery);
tx.executeSql(createNovelIdIndexQuery);
diff --git a/src/database/queries/HistoryQueries.js b/src/database/queries/HistoryQueries.js
index 7c37a99f..6f55750d 100644
--- a/src/database/queries/HistoryQueries.js
+++ b/src/database/queries/HistoryQueries.js
@@ -1,6 +1,8 @@
import * as SQLite from 'expo-sqlite';
const db = SQLite.openDatabase('lnreader.db');
+import {showToast} from '../../hooks/showToast';
+
const getHistoryQuery = `
SELECT history.*, chapters.*, novels.*
FROM history
@@ -57,4 +59,5 @@ export const deleteAllHistory = async () => {
db.transaction(tx => {
tx.executeSql('DELETE FROM history; VACCUM;');
});
+ showToast('History deleted.');
};
diff --git a/src/database/queries/UpdateQueries.js b/src/database/queries/UpdateQueries.js
index 296d239d..e814d8f0 100644
--- a/src/database/queries/UpdateQueries.js
+++ b/src/database/queries/UpdateQueries.js
@@ -10,6 +10,7 @@ const getUpdatesQuery = `
ON updates.novelId = novels.novelId
WHERE date(updates.updateTime) > date('now','-3 months')
ORDER BY updates.updateTime DESC
+ LIMIT 100
`;
export const getUpdates = async () => {
diff --git a/src/database/tables/ChapterTable.js b/src/database/tables/ChapterTable.js
index 14edcbf9..76305f8c 100644
--- a/src/database/tables/ChapterTable.js
+++ b/src/database/tables/ChapterTable.js
@@ -1,4 +1,4 @@
-export const createChapterTableQuery = `
+const createChapterTableQuery = `
CREATE TABLE IF NOT EXISTS chapters(
chapterId INTEGER PRIMARY KEY AUTOINCREMENT,
chapterUrl TEXT,
@@ -13,6 +13,18 @@ export const createChapterTableQuery = `
ON DELETE CASCADE
)`;
-export const createNovelIdIndexQuery = `CREATE INDEX IF NOT EXISTS chapterNovelIdIndex ON chapters(novelId)`;
+const createNovelIdIndexQuery = `
+ CREATE INDEX
+ IF NOT EXISTS
+ chapterNovelIdIndex ON chapters(novelId)`;
-export const createUnreadChaptersIndexQuery = `CREATE INDEX IF NOT EXISTS chapterUnreadByNovelIndex ON chapters(novelId, \`read\`) WHERE \`read\`=0`;
+const createUnreadChaptersIndexQuery = `
+ CREATE INDEX
+ IF NOT EXISTS
+ chapterUnreadByNovelIndex ON chapters(novelId, \`read\`) WHERE \`read\`=0`;
+
+export {
+ createChapterTableQuery,
+ createNovelIdIndexQuery,
+ createUnreadChaptersIndexQuery,
+};
diff --git a/src/hooks/useModal.js b/src/hooks/useModal.js
new file mode 100644
index 00000000..e50124b7
--- /dev/null
+++ b/src/hooks/useModal.js
@@ -0,0 +1,10 @@
+import {useState} from 'react';
+
+export const useModal = () => {
+ const [visible, setVisible] = useState(false);
+
+ const showModal = () => setVisible(true);
+ const hideModal = () => setVisible(false);
+
+ return {visible, showModal, hideModal};
+};
diff --git a/src/navigators/BottomNavigator.js b/src/navigators/BottomNavigator.js
index 1c9b9eb6..2d06840f 100644
--- a/src/navigators/BottomNavigator.js
+++ b/src/navigators/BottomNavigator.js
@@ -4,11 +4,11 @@ import {createMaterialBottomTabNavigator} from '@react-navigation/material-botto
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
-import Library from '../screens/Library/Library';
-import Updates from '../screens/Updates/Updates';
-import History from '../screens/History/History';
-import Browse from '../screens/Browse/Browse';
-import More from '../screens/More/More';
+import Library from '../screens/library/Library';
+import Updates from '../screens/updates/Updates';
+import History from '../screens/history/History';
+import Browse from '../screens/browse/Browse';
+import More from '../screens/more/More';
import {useSettings, useTheme} from '../hooks/reduxHooks';
diff --git a/src/navigators/Main.js b/src/navigators/Main.js
index 3dbc451d..48952084 100644
--- a/src/navigators/Main.js
+++ b/src/navigators/Main.js
@@ -9,21 +9,26 @@ import {useTheme} from '../hooks/reduxHooks';
import {githubUpdateChecker} from '../hooks/githubUpdateChecker';
import * as SplashScreen from 'expo-splash-screen';
-// Navigators
+/**
+ * Navigators
+ */
import BottomNavigator from './BottomNavigator';
-
-// Screens
-import Novel from '../screens/Novel/Novel';
-import Chapter from '../screens/Chapter/Chapter';
-import Extension from '../screens/Extension/Extension';
-import GlobalSearch from '../screens/Browse/globalsearch/GlobalSearch';
-import Migration from '../screens/Browse/migration/Migration';
-import SourceNovels from '../screens/Browse/SourceNovels';
-import MigrateNovel from '../screens/Browse/migration/MigrationNovels';
import MoreStack from './More';
-import MalTopNovels from '../screens/Browse/discover/MalTopNovels';
+
+/**
+ * Screens
+ */
+import Novel from '../screens/novel/Novel';
+import Reader from '../screens/reader/Reader';
+import Extension from '../screens/source/Source';
+import GlobalSearch from '../screens/browse/globalsearch/GlobalSearch';
+import Migration from '../screens/browse/migration/Migration';
+import SourceNovels from '../screens/browse/SourceNovels';
+import MigrateNovel from '../screens/browse/migration/MigrationNovels';
+
+import MalTopNovels from '../screens/browse/discover/MalTopNovels';
import NewUpdateDialog from '../components/NewUpdateDialog';
-import BrowseSettings from '../screens/Browse/BrowseSettings';
+import BrowseSettings from '../screens/browse/BrowseSettings';
const Stack = createStackNavigator();
@@ -46,7 +51,7 @@ const MainNavigator = () => {
-
+
diff --git a/src/navigators/More.js b/src/navigators/More.js
index c9cc0437..00a337e4 100644
--- a/src/navigators/More.js
+++ b/src/navigators/More.js
@@ -3,16 +3,16 @@ import React from 'react';
import {createStackNavigator} from '@react-navigation/stack';
// Screens
-import About from '../screens/More/About';
-import Settings from '../screens/More/Settings';
-import TrackerSettings from '../screens/More/TrackerSettings';
-import ReaderSettings from '../screens/More/ReaderSettings';
-import BackupSettings from '../screens/More/BackupSettings';
-import AdvancedSettings from '../screens/More/AdvancedSettings';
-import GeneralSettings from '../screens/More/GeneralSettings';
-import DownloadQueue from '../screens/More/DownloadQueue';
-import Downloads from '../screens/More/Downloads';
-import AppearanceSettings from '../screens/More/AppearanceSettings';
+import About from '../screens/more/About';
+import Settings from '../screens/setting/Settings';
+import TrackerSettings from '../screens/setting/SettingsTracker';
+import ReaderSettings from '../screens/setting/SettingsReader';
+import BackupSettings from '../screens/setting/SettingsBackup';
+import AdvancedSettings from '../screens/setting/SettingsAdvanced';
+import GeneralSettings from '../screens/setting/SettingsGeneral';
+import DownloadQueue from '../screens/more/DownloadQueue';
+import Downloads from '../screens/more/Downloads';
+import AppearanceSettings from '../screens/setting/SettingsAppearance';
const Stack = createStackNavigator();
diff --git a/src/screens/Browse/Browse.js b/src/screens/Browse/Browse.js
index c02f8f63..7f7b26e6 100644
--- a/src/screens/Browse/Browse.js
+++ b/src/screens/Browse/Browse.js
@@ -18,7 +18,7 @@ import {useTheme} from '../../hooks/reduxHooks';
import {showToast} from '../../hooks/showToast';
import {Searchbar} from '../../components/Searchbar/Searchbar';
-import SourceCard from './components/SourceCard';
+import SourceItem from './components/SourceItem';
import EmptyView from '../../components/EmptyView';
import DiscoverCard from './discover/DiscoverCard';
@@ -65,7 +65,7 @@ const Browse = ({navigation}) => {
};
const renderItem = ({item}) => (
- {
+const SourceItem = ({item, theme, isPinned, dispatch, navigation}) => {
const {sourceId, sourceName, icon, lang} = item;
- const navigateToExtension = () =>
- navigation.navigate('Extension', {
- sourceId: item.sourceId,
- sourceName: item.sourceName,
- url: item.url,
- });
+ const navigateToSource = useCallback(
+ () =>
+ navigation.navigate('Extension', {
+ sourceId: item.sourceId,
+ sourceName: item.sourceName,
+ url: item.url,
+ }),
+ [],
+ );
return (
-
+
{
labelStyle={{letterSpacing: 0}}
uppercase={false}
color={theme.colorAccent}
- onPress={navigateToExtension}
+ onPress={navigateToSource}
>
Browse
@@ -64,17 +67,17 @@ const ExtensionCard = ({item, theme, isPinned, dispatch, navigation}) => {
);
};
-export default ExtensionCard;
+export default SourceItem;
const styles = StyleSheet.create({
- extensionCard: {
+ container: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: 4,
paddingVertical: 8,
paddingHorizontal: 20,
},
- extensionIcon: {
+ icon: {
width: 40,
height: 40,
borderRadius: 4,
diff --git a/src/screens/Browse/migration/Migration.js b/src/screens/Browse/migration/Migration.js
index 2fdfb99e..d407badd 100644
--- a/src/screens/Browse/migration/Migration.js
+++ b/src/screens/Browse/migration/Migration.js
@@ -4,7 +4,7 @@ import {StyleSheet, View, FlatList, Text} from 'react-native';
import {useSelector} from 'react-redux';
import {Appbar} from '../../../components/Appbar';
-import MigrationSourceCard from './MigrationSourceCard';
+import MigrationSourceItem from './MigrationSourceItem';
import {useLibrary, useTheme} from '../../../hooks/reduxHooks';
@@ -23,11 +23,10 @@ const GlobalSearch = ({navigation}) => {
);
const renderItem = ({item}) => (
- navigation.navigate('SourceNovels', item.sourceId)}
/>
);
@@ -43,6 +42,7 @@ const GlobalSearch = ({navigation}) => {
item.sourceId.toString()}
renderItem={renderItem}
ListHeaderComponent={ListHeaderComponent}
@@ -57,11 +57,7 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
- containerStyle: {
- padding: 20,
- margin: 20,
- borderRadius: 6,
- },
+
listHeader: {
padding: 20,
paddingBottom: 10,
diff --git a/src/screens/Browse/migration/MigrationNovelList.js b/src/screens/Browse/migration/MigrationNovelList.js
index 3c984f80..527ff376 100644
--- a/src/screens/Browse/migration/MigrationNovelList.js
+++ b/src/screens/Browse/migration/MigrationNovelList.js
@@ -15,11 +15,8 @@ const MigrationNovelList = ({data, theme, library, navigation}) => {
const showMigrateNovelDialog = () => setMigrateNovelDialog(true);
const hideMigrateNovelDialog = () => setMigrateNovelDialog(false);
- const inLibrary = (sourceId, novelUrl) => {
- return library.some(
- obj => obj.novelUrl === novelUrl && obj.sourceId === sourceId,
- );
- };
+ const inLibrary = (sourceId, novelUrl) =>
+ library.some(obj => obj.novelUrl === novelUrl && obj.sourceId === sourceId);
const renderItem = ({item}) => (
{
- const onSlidingComplete = value => {
- setLoading(true);
- scrollViewRef.current.scrollTo({
- x: 0,
- y: Math.round((value * contentSize) / 100),
- animated: false,
- });
- setLoading(false);
- };
-
- if (hide) {
- return null;
- } else if (getDeviceOrientation() === 'potrait') {
- return (
-
-
- {scrollPercentage}
-
-
-
- 100
-
-
- );
- } else {
- return (
-
-
- {scrollPercentage}
-
-
-
- 100
-
-
- );
- }
-};
-
-export default ReaderSeekbar;
-
-const styles = StyleSheet.create({
- verticalSliderContainer: {
- position: 'absolute',
- zIndex: 2,
- right: -(Dimensions.get('window').width / 2) + 40,
- bottom: 300,
- backgroundColor: 'rgba(0,0,0,0.7)',
- flexDirection: 'row',
- justifyContent: 'flex-end',
- alignItems: 'center',
- borderRadius: 50,
- marginHorizontal: 8,
- transform: [{rotate: '90deg'}],
- },
- horizontalSliderContainer: {
- position: 'absolute',
- zIndex: 2,
- bottom: 100,
- backgroundColor: 'rgba(0,0,0,0.7)',
- flexDirection: 'row',
- justifyContent: 'flex-end',
- alignItems: 'center',
- borderRadius: 50,
- marginHorizontal: 8,
- },
-});
diff --git a/src/screens/Chapter/components/WebViewReader.js b/src/screens/Chapter/components/WebViewReader.js
deleted file mode 100644
index 704d7250..00000000
--- a/src/screens/Chapter/components/WebViewReader.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react';
-import WebView from 'react-native-webview';
-
-export const WebViewReader = ({style, css, text, onScroll}) => (
-
-
-
-
-
-
- ${text}
-
-