Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 419420efd1 | |||
| 871fc905ce | |||
| 5a8c652f68 | |||
| 97f2f84fa5 | |||
| 65a3e8fd2d |
@@ -7,7 +7,6 @@ import AppLoading from "expo-app-loading";
|
||||
import Router from "./src/navigation/Router";
|
||||
|
||||
import { createTables, createIndexes } from "./src/services/db";
|
||||
import { setStatusBar } from "./src/services/asyncStorage";
|
||||
|
||||
// Redux
|
||||
import { Provider } from "react-redux";
|
||||
@@ -22,8 +21,6 @@ const getFonts = () =>
|
||||
const App = () => {
|
||||
const [fontsLoaded, setFontsLoaded] = useState(false);
|
||||
|
||||
setStatusBar();
|
||||
|
||||
const createDb = () => {
|
||||
createTables();
|
||||
createIndexes();
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 24 KiB |
Generated
+8
@@ -7120,6 +7120,14 @@
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-input-spinner": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-input-spinner/-/react-native-input-spinner-1.7.7.tgz",
|
||||
"integrity": "sha512-6zYFU2uhChJ+JC/5yujlZu+r5PYQSYIXluUqTNR3jOCKai0dlCkWiz7d3K2rM7Q2SAIiw4bSBR6mWZIuhL+DEg==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-iphone-x-helper": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
|
||||
"react-native-collapsible-header-views": "^1.1.2",
|
||||
"react-native-gesture-handler": "^1.8.0",
|
||||
"react-native-input-spinner": "^1.7.7",
|
||||
"react-native-offline": "^5.7.0",
|
||||
"react-native-paper": "^4.5.0",
|
||||
"react-native-reanimated": "^1.13.2",
|
||||
|
||||
@@ -59,17 +59,22 @@ export const SearchAppbar = ({
|
||||
}}
|
||||
>
|
||||
<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();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{screen === "Library" ? (
|
||||
<IconButton
|
||||
icon="magnify"
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginLeft: 0 }}
|
||||
size={23}
|
||||
/>
|
||||
) : (
|
||||
<IconButton
|
||||
icon="arrow-left"
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginLeft: 0 }}
|
||||
size={23}
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
)}
|
||||
<TextInput
|
||||
ref={searchRef}
|
||||
style={{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View, Text, ImageBackground } from "react-native";
|
||||
import { StyleSheet, View, Text, ImageBackground, Image } from "react-native";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { TouchableRipple } from "react-native-paper";
|
||||
|
||||
@@ -12,12 +12,25 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
// const itemsPerRow = useSelector(
|
||||
// (state) => state.settingsReducer.itemsPerRow
|
||||
// );
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 / 3 }}>
|
||||
const height = {
|
||||
1: 550,
|
||||
2: 280,
|
||||
3: 180,
|
||||
4: 140,
|
||||
5: 100,
|
||||
6: 80,
|
||||
7: 70,
|
||||
8: 58,
|
||||
9: 55,
|
||||
10: 50,
|
||||
};
|
||||
|
||||
return displayMode !== 2 ? (
|
||||
<View style={{ flex: 1 / itemsPerRow }}>
|
||||
<TouchableRipple
|
||||
borderless
|
||||
centered
|
||||
@@ -29,7 +42,7 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
|
||||
{item.novelCover && (
|
||||
<ImageBackground
|
||||
source={{ uri: item.novelCover }}
|
||||
style={styles.logo}
|
||||
style={{ height: height[itemsPerRow] }}
|
||||
imageStyle={[
|
||||
{ borderRadius: 4 },
|
||||
libraryStatus && { opacity: 0.5 },
|
||||
@@ -79,15 +92,42 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
|
||||
</>
|
||||
</TouchableRipple>
|
||||
</View>
|
||||
) : (
|
||||
<TouchableRipple
|
||||
borderless
|
||||
centered
|
||||
rippleColor={theme.rippleColor}
|
||||
style={styles.listView}
|
||||
onPress={onPress}
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
source={{ uri: item.novelCover }}
|
||||
style={styles.extensionIcon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
flex: 1,
|
||||
color: theme.textColorPrimary,
|
||||
marginLeft: 16,
|
||||
fontSize: 15,
|
||||
paddingRight: 8,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.novelName}
|
||||
</Text>
|
||||
</>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
export default NovelCover;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
logo: {
|
||||
height: 180,
|
||||
},
|
||||
// logo: {
|
||||
// height: 180,
|
||||
// },
|
||||
titleContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
@@ -108,4 +148,17 @@ const styles = StyleSheet.create({
|
||||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
},
|
||||
extensionIcon: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
borderRadius: 4,
|
||||
},
|
||||
listView: {
|
||||
flex: 1,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 12,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,8 +6,9 @@ export const DisplayCheckbox = ({ displayMode, onPress, value }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
const label = {
|
||||
0: "Compact",
|
||||
1: "Comfortable",
|
||||
0: "Compact Grid",
|
||||
1: "Comfortable Grid",
|
||||
2: "List",
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
} from "@react-navigation/stack";
|
||||
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
|
||||
import { Provider } from "react-native-paper";
|
||||
import { setStatusBarStyle } from "expo-status-bar";
|
||||
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
|
||||
@@ -62,6 +63,7 @@ import { useSelector } from "react-redux";
|
||||
|
||||
const BottomNavigator = () => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
shifting={false}
|
||||
@@ -139,6 +141,10 @@ const BottomNavigator = () => {
|
||||
};
|
||||
|
||||
const Router = () => {
|
||||
const themeCode = useSelector((state) => state.themeReducer.themeCode);
|
||||
|
||||
useEffect(() => setStatusBarStyle(themeCode === 1 ? "dark" : "light"), []);
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
|
||||
+36
-40
@@ -63,18 +63,14 @@ export const getNovel = (inLibrary, extensionId, novelUrl) => async (
|
||||
});
|
||||
} else {
|
||||
const novel = await fetchNovelFromSource(extensionId, novelUrl);
|
||||
const chapters = await fetchChaptersFromSource(
|
||||
extensionId,
|
||||
novelUrl
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: GET_NOVEL,
|
||||
payload: { novel, chapters },
|
||||
payload: { novel, chapters: novel.novelChapters },
|
||||
});
|
||||
|
||||
insertNovelInfoInDb(novel);
|
||||
insertChaptersInDb(novelUrl, chapters);
|
||||
insertChaptersInDb(novelUrl, novel.novelChapters);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -130,43 +126,43 @@ export const updateChapterRead = (chapterUrl, novelUrl) => async (dispatch) => {
|
||||
dispatch({ type: CHAPTER_READ, payload: { chapterUrl, novelUrl } });
|
||||
};
|
||||
|
||||
// export const downloadChapter = (
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// chapterName
|
||||
// ) => async (dispatch) => {
|
||||
// dispatch({
|
||||
// type: CHAPTER_DOWNLOADING,
|
||||
// payload: {
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// },
|
||||
// });
|
||||
export const downloadChapter = (
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName
|
||||
) => async (dispatch) => {
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADING,
|
||||
payload: {
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
},
|
||||
});
|
||||
|
||||
// await downloadChapterFromSource(extensionId, novelUrl, chapterUrl);
|
||||
await downloadChapterFromSource(extensionId, novelUrl, chapterUrl);
|
||||
|
||||
// dispatch({
|
||||
// type: CHAPTER_DOWNLOADED,
|
||||
// payload: { extensionId, novelUrl, chapterUrl },
|
||||
// });
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADED,
|
||||
payload: { extensionId, novelUrl, chapterUrl },
|
||||
});
|
||||
|
||||
// ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
|
||||
// };
|
||||
ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
|
||||
};
|
||||
|
||||
// export const deleteChapter = (
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// chapterName
|
||||
// ) => (dispatch) => {
|
||||
// deleteChapterFromDb(extensionId, novelUrl, chapterUrl);
|
||||
export const deleteChapter = (
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName
|
||||
) => async (dispatch) => {
|
||||
await deleteChapterFromDb(novelUrl, chapterUrl);
|
||||
|
||||
// dispatch({
|
||||
// type: CHAPTER_DELETED,
|
||||
// payload: { extensionId, novelUrl, chapterUrl },
|
||||
// });
|
||||
dispatch({
|
||||
type: CHAPTER_DELETED,
|
||||
payload: { extensionId, novelUrl, chapterUrl },
|
||||
});
|
||||
|
||||
// ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
|
||||
// };
|
||||
ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ export const setDisplayMode = (val) => (dispatch) => {
|
||||
};
|
||||
|
||||
export const setItemsPerRow = (val) => (dispatch) => {
|
||||
console.log("called" + val);
|
||||
dispatch({
|
||||
type: SET_ITEMS_PER_ROW,
|
||||
payload: val,
|
||||
|
||||
+31
-30
@@ -17,6 +17,7 @@ const initialState = {
|
||||
novel: null,
|
||||
chapters: [],
|
||||
chapter: null,
|
||||
downloading: [],
|
||||
chapterLoading: true,
|
||||
loading: true,
|
||||
fetching: false,
|
||||
@@ -70,36 +71,36 @@ const novelReducer = (state = initialState, action) => {
|
||||
};
|
||||
case GET_CHAPTER:
|
||||
return { ...state, chapter: payload, chapterLoading: false };
|
||||
// case CHAPTER_DOWNLOADING:
|
||||
// return {
|
||||
// ...state,
|
||||
// chapters: [
|
||||
// state.chapters.map((chap) =>
|
||||
// chap.novelUrl === payload.novelUrl &&
|
||||
// chap.chapterUrl === payload.novelUrl
|
||||
// ? { ...chap, downloading: true, downloaded: false }
|
||||
// : chap
|
||||
// ),
|
||||
// ],
|
||||
// };
|
||||
// case CHAPTER_DOWNLOADED:
|
||||
// return {
|
||||
// ...state,
|
||||
// downloads: state.downloads.map((chap) =>
|
||||
// chap.chapterUrl === payload.chapterUrl &&
|
||||
// chap.novelUrl === payload.novelUrl
|
||||
// ? { ...chap, downloading: false, downloaded: true }
|
||||
// : chap
|
||||
// ),
|
||||
// };
|
||||
// case CHAPTER_DELETED:
|
||||
// return {
|
||||
// downloads: state.downloads.filter(
|
||||
// (chapter) =>
|
||||
// chapter.chapterUrl === payload.chapterUrl &&
|
||||
// chapter.novelUrl === payload.novelUrl
|
||||
// ),
|
||||
// };
|
||||
case CHAPTER_DOWNLOADING:
|
||||
return {
|
||||
...state,
|
||||
downloading: [...state.downloading, payload],
|
||||
};
|
||||
case CHAPTER_DOWNLOADED:
|
||||
return {
|
||||
...state,
|
||||
downloading: state.downloading.filter(
|
||||
(chapter) =>
|
||||
chapter.chapterUrl === payload.chapterUrl &&
|
||||
chapter.novelUrl === payload.novelUrl
|
||||
),
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterUrl === payload.chapterUrl &&
|
||||
chapter.novelUrl === payload.novelUrl
|
||||
? { ...chapter, downloaded: 1 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
case CHAPTER_DELETED:
|
||||
return {
|
||||
...state,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterUrl === payload.chapterUrl &&
|
||||
chapter.novelUrl === payload.novelUrl
|
||||
? { ...chapter, downloaded: 0 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -34,9 +34,16 @@ const settingsReducer = (state = initialState, action) => {
|
||||
|
||||
switch (type) {
|
||||
case SET_DISPLAY_MODE:
|
||||
return { ...state, displayMode: payload };
|
||||
return {
|
||||
...state,
|
||||
displayMode: payload,
|
||||
itemsPerRow: payload === 2 ? 1 : state.itemsPerRow,
|
||||
};
|
||||
case SET_ITEMS_PER_ROW:
|
||||
return { ...state, itemsPerRow: payload };
|
||||
return {
|
||||
...state,
|
||||
itemsPerRow: state.displayMode !== 2 ? payload : 1,
|
||||
};
|
||||
case UPDATE_READER_THEME:
|
||||
return { ...state, reader: { ...state.reader, theme: payload } };
|
||||
case UPDATE_READER_TEXT_SIZE:
|
||||
|
||||
@@ -17,6 +17,7 @@ const ExtensionCard = ({ item }) => {
|
||||
style={styles.extensionCard}
|
||||
onPress={() => navigation.navigate(sourceName + "Stack")}
|
||||
rippleColor={theme.rippleColor}
|
||||
borderless
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
@@ -74,7 +75,7 @@ const styles = StyleSheet.create({
|
||||
marginVertical: 4,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 20,
|
||||
borderRadius: 6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
extensionIcon: {
|
||||
width: 40,
|
||||
|
||||
@@ -42,7 +42,7 @@ const ChapterItem = ({
|
||||
};
|
||||
|
||||
const readerTextColor = (val) => {
|
||||
const textColor = val === 1 ? theme.textColorSecondary : "#000000";
|
||||
const textColor = val === 1 ? "rgba(255,255,255,0.7)" : "#000000";
|
||||
|
||||
return textColor;
|
||||
};
|
||||
@@ -71,7 +71,7 @@ const ChapterItem = ({
|
||||
return (
|
||||
<Provider>
|
||||
<CollapsibleHeaderScrollView
|
||||
headerContainerBackgroundColor={"rgba(0,0,0,0.4)"}
|
||||
headerContainerBackgroundColor="rgba(0,0,0,0.4)"
|
||||
CollapsibleHeaderComponent={
|
||||
<Appbar.Header
|
||||
style={{
|
||||
|
||||
@@ -28,12 +28,7 @@ const ChapterBottomSheet = ({
|
||||
snappingPoints={[0, 200]}
|
||||
showBackdrop={false}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.contentContainer,
|
||||
{ backgroundColor: "rgba(0,0,0,0.4)" },
|
||||
]}
|
||||
>
|
||||
<View style={styles.contentContainer}>
|
||||
<BottomSheetHandle />
|
||||
<View style={styles.readerSettingsContainer}>
|
||||
<Text
|
||||
@@ -118,6 +113,9 @@ export default connect(mapStateToProps, {
|
||||
const styles = StyleSheet.create({
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
backgroundColor: "rgba(0,0,0,0.4)",
|
||||
},
|
||||
readerSettingsContainer: { flex: 1, alignItems: "center", paddingTop: 30 },
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
|
||||
novelCover: item.novelCover,
|
||||
extensionId: item.extensionId,
|
||||
novelUrl: item.novelUrl,
|
||||
libraryStatus: item.libraryStatus,
|
||||
})
|
||||
}
|
||||
>
|
||||
|
||||
@@ -29,6 +29,7 @@ const LibraryScreen = ({
|
||||
getLibraryNovels,
|
||||
searchLibraryNovels,
|
||||
setNovel,
|
||||
itemsPerRow,
|
||||
}) => {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
@@ -83,7 +84,8 @@ const LibraryScreen = ({
|
||||
flexGrow: 1,
|
||||
paddingBottom: 8,
|
||||
}}
|
||||
numColumns={3}
|
||||
numColumns={itemsPerRow}
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
renderItem={({ item }) => (
|
||||
@@ -126,6 +128,7 @@ const LibraryScreen = ({
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
itemsPerRow: state.settingsReducer.itemsPerRow,
|
||||
novels: state.libraryReducer.novels,
|
||||
loading: state.libraryReducer.loading,
|
||||
});
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, TextInput } from "react-native";
|
||||
import { Appbar } from "react-native-paper";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const LibraryAppbar = ({
|
||||
search,
|
||||
setSearch,
|
||||
getLibraryNovels,
|
||||
searchLibraryNovels,
|
||||
}) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
const { searching, searchText } = search;
|
||||
|
||||
return (
|
||||
<Appbar.Header style={{ backgroundColor: theme.colorPrimary }}>
|
||||
{!searching ? (
|
||||
<>
|
||||
<Appbar.Content
|
||||
title="Library"
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="magnify"
|
||||
onPress={() =>
|
||||
setSearch({ ...search, searching: true })
|
||||
}
|
||||
color={theme.textColorPrimary}
|
||||
/>
|
||||
<Appbar.Action
|
||||
color={theme.textColorPrimary}
|
||||
icon="filter-variant"
|
||||
disabled
|
||||
// onPress={() => _panel.show({ velocity: -1.5 })}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Appbar.BackAction
|
||||
onPress={() => {
|
||||
setSearch({ searching: false, searchText: "" });
|
||||
getLibraryNovels();
|
||||
}}
|
||||
color={theme.textColorPrimary}
|
||||
size={24}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Search..."
|
||||
defaultValue={searchText}
|
||||
style={[
|
||||
styles.searchBar,
|
||||
{
|
||||
color: theme.textColorPrimary,
|
||||
},
|
||||
]}
|
||||
autoFocus
|
||||
placeholderTextColor={theme.textColorHintDark}
|
||||
blurOnSubmit={true}
|
||||
onChangeText={(text) => {
|
||||
setSearch({ ...search, searchText: text });
|
||||
searchLibraryNovels(text);
|
||||
}}
|
||||
/>
|
||||
{searchText !== "" && (
|
||||
<Appbar.Action
|
||||
icon="close"
|
||||
onPress={() => {
|
||||
setSearch({ ...search, searchText: "" });
|
||||
getLibraryNovels();
|
||||
}}
|
||||
color={theme.textColorPrimary}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Appbar.Header>
|
||||
);
|
||||
};
|
||||
|
||||
export default LibraryAppbar;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
searchBar: { fontSize: 18, flex: 1, marginLeft: 15 },
|
||||
});
|
||||
@@ -24,7 +24,7 @@ const AboutScreen = ({ navigation }) => {
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
title="Version"
|
||||
descriptionStyle={{ color: theme.textColorSecondary }}
|
||||
description="Stable 1.0.6"
|
||||
description="Stable 1.0.9"
|
||||
/>
|
||||
<List.Item
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { View, Text } from "react-native";
|
||||
import { setStatusBarStyle } from "expo-status-bar";
|
||||
import { List, Modal, Portal } from "react-native-paper";
|
||||
import InputSpinner from "react-native-input-spinner";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { switchTheme } from "../../redux/actions/theme";
|
||||
@@ -24,12 +25,15 @@ const SettingsScreen = ({
|
||||
theme,
|
||||
themeCode,
|
||||
displayMode,
|
||||
itemsPerRow,
|
||||
switchTheme,
|
||||
setDisplayMode,
|
||||
setItemsPerRow,
|
||||
}) => {
|
||||
const display = {
|
||||
0: "Compact",
|
||||
1: "Comfortable",
|
||||
0: "Compact Grid",
|
||||
1: "Comfortable Grid",
|
||||
2: "List",
|
||||
};
|
||||
|
||||
const themes = [
|
||||
@@ -59,6 +63,10 @@ const SettingsScreen = ({
|
||||
const showthemeModal = () => setthemeModalVisible(true);
|
||||
const hidethemeModal = () => setthemeModalVisible(false);
|
||||
|
||||
const [itemsModalVisible, setitemsModalVisible] = useState(false);
|
||||
const showitemsModal = () => setitemsModalVisible(true);
|
||||
const hideitemsModal = () => setitemsModalVisible(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar title="Settings" onBackAction={() => navigation.goBack()} />
|
||||
@@ -130,8 +138,66 @@ const SettingsScreen = ({
|
||||
displayMode={displayMode}
|
||||
onPress={() => setDisplayMode(0)}
|
||||
/>
|
||||
<DisplayCheckbox
|
||||
value={2}
|
||||
displayMode={displayMode}
|
||||
onPress={() => setDisplayMode(2)}
|
||||
/>
|
||||
</Modal>
|
||||
</Portal>
|
||||
<List.Item
|
||||
titleStyle={titleStyles}
|
||||
title="Items per row in library"
|
||||
descriptionStyle={desciptionStyles}
|
||||
description={itemsPerRow}
|
||||
onPress={showitemsModal}
|
||||
rippleColor={theme.rippleColor}
|
||||
/>
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={itemsModalVisible}
|
||||
onDismiss={hideitemsModal}
|
||||
contentContainerStyle={{
|
||||
backgroundColor: theme.colorPrimaryDark,
|
||||
padding: 20,
|
||||
margin: 20,
|
||||
borderRadius: 6,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
fontSize: 18,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
Items per row
|
||||
</Text>
|
||||
<InputSpinner
|
||||
style={{
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
elevation: 0,
|
||||
}}
|
||||
max={10}
|
||||
min={1}
|
||||
skin="paper"
|
||||
background={theme.colorPrimaryDark}
|
||||
colorMax={theme.colorAccentDark}
|
||||
colorLeft={theme.colorAccentDark}
|
||||
colorRight={theme.colorAccentDark}
|
||||
buttonTextColor={theme.textColorPrimary}
|
||||
colorPress={"white"}
|
||||
colorMin={theme.colorAccentDark}
|
||||
value={itemsPerRow}
|
||||
onChange={(num) => setItemsPerRow(num)}
|
||||
inputStyle={{
|
||||
color: theme.textColorPrimary,
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</Portal>
|
||||
|
||||
{/* <List.Subheader
|
||||
style={{
|
||||
color: theme.colorAccentDark,
|
||||
@@ -148,7 +214,6 @@ const SettingsScreen = ({
|
||||
rippleColor={theme.rippleColor}
|
||||
onPress={() => deleteDatabase()}
|
||||
/> */}
|
||||
|
||||
<List.Subheader
|
||||
style={{
|
||||
color: theme.colorAccentDark,
|
||||
@@ -203,6 +268,7 @@ const mapStateToProps = (state) => ({
|
||||
themeCode: state.themeReducer.themeCode,
|
||||
theme: state.themeReducer.theme,
|
||||
displayMode: state.settingsReducer.displayMode,
|
||||
itemsPerRow: state.settingsReducer.itemsPerRow,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
|
||||
+29
-29
@@ -41,10 +41,10 @@ const Novel = ({
|
||||
libraryStatus,
|
||||
} = route.params;
|
||||
|
||||
const [downloading, setDownloading] = useState({
|
||||
downloading: false,
|
||||
chapterUrl: "",
|
||||
});
|
||||
// const [downloading, setDownloading] = useState({
|
||||
// downloading: false,
|
||||
// chapterUrl: "",
|
||||
// });
|
||||
|
||||
let _panel = useRef(null); // Bottomsheet ref
|
||||
|
||||
@@ -54,29 +54,29 @@ const Novel = ({
|
||||
* If downloaded then delete from db
|
||||
*/
|
||||
|
||||
const downloadChapter = (
|
||||
downloadStatus,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName
|
||||
) => {
|
||||
setDownloading({ downloading: true, chapterUrl: chapterUrl });
|
||||
downloadOrDeleteChapter(
|
||||
downloadStatus,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl
|
||||
).then((res) => {
|
||||
setDownloading({ downloading: false });
|
||||
ToastAndroid.show(
|
||||
!downloadStatus
|
||||
? `Downloaded ${chapterName}`
|
||||
: `Deleted ${chapterName}`,
|
||||
ToastAndroid.SHORT
|
||||
);
|
||||
});
|
||||
};
|
||||
// const downloadChapter = (
|
||||
// downloadStatus,
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// chapterName
|
||||
// ) => {
|
||||
// setDownloading({ downloading: true, chapterUrl: chapterUrl });
|
||||
// downloadOrDeleteChapter(
|
||||
// downloadStatus,
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl
|
||||
// ).then((res) => {
|
||||
// setDownloading({ downloading: false });
|
||||
// ToastAndroid.show(
|
||||
// !downloadStatus
|
||||
// ? `Downloaded ${chapterName}`
|
||||
// : `Deleted ${chapterName}`,
|
||||
// ToastAndroid.SHORT
|
||||
// );
|
||||
// });
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
getNovel(libraryStatus, extensionId, novelUrl);
|
||||
@@ -87,8 +87,8 @@ const Novel = ({
|
||||
novelUrl={novelUrl}
|
||||
extensionId={extensionId}
|
||||
chapter={item}
|
||||
downloadChapter={downloadChapter}
|
||||
downloading={downloading}
|
||||
// downloadChapter={downloadChapter}
|
||||
// downloading={downloading}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@ export const BottomSheet = ({
|
||||
|
||||
const sortChapters = (sort) => {
|
||||
setChapterFlags({ ...chapterFlags, sort: sort });
|
||||
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
|
||||
sortAndFilterChapters(novelUrl, chapterFlags.filter, sort);
|
||||
};
|
||||
|
||||
const filterChapters = (filter) => {
|
||||
setChapterFlags({ ...chapterFlags, filter: filter });
|
||||
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
|
||||
sortAndFilterChapters(novelUrl, filter, chapterFlags.sort);
|
||||
};
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
@@ -53,7 +53,6 @@ export const BottomSheet = ({
|
||||
<RadioButton.Group
|
||||
onValueChange={(newValue) => sortChapters(newValue)}
|
||||
value={chapterFlags.sort}
|
||||
c
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
|
||||
@@ -3,21 +3,27 @@ import { View, Text, ActivityIndicator, StyleSheet } from "react-native";
|
||||
import { TouchableRipple, IconButton } from "react-native-paper";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
import { deleteChapter, downloadChapter } from "../../../redux/actions/novel";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
const ChapterCard = ({
|
||||
novelUrl,
|
||||
chapter,
|
||||
downloadChapter,
|
||||
extensionId,
|
||||
theme,
|
||||
downloading,
|
||||
downloadChapter,
|
||||
deleteChapter,
|
||||
}) => {
|
||||
const [downloaded, setDownloaded] = useState(chapter.downloaded);
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
const navigation = useNavigation();
|
||||
|
||||
const isDownloading = () =>
|
||||
downloading.some(
|
||||
(obj) =>
|
||||
obj.chapterUrl === chapter.chapterUrl &&
|
||||
obj.novelUrl === novelUrl
|
||||
);
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
style={styles.chapterCardContainer}
|
||||
@@ -65,8 +71,7 @@ const ChapterCard = ({
|
||||
)}
|
||||
</View>
|
||||
<View>
|
||||
{downloading.downloading &&
|
||||
downloading.chapterUrl === chapter.chapterUrl ? (
|
||||
{isDownloading() && !chapter.downloaded ? (
|
||||
<ActivityIndicator
|
||||
color={theme.textColorHintDark}
|
||||
size={25}
|
||||
@@ -75,26 +80,33 @@ const ChapterCard = ({
|
||||
) : (
|
||||
<IconButton
|
||||
icon={
|
||||
downloaded
|
||||
chapter.downloaded
|
||||
? "check-circle"
|
||||
: "arrow-down-circle-outline"
|
||||
}
|
||||
animated
|
||||
color={
|
||||
downloaded
|
||||
chapter.downloaded
|
||||
? theme.textColorPrimary
|
||||
: theme.textColorHintDark
|
||||
}
|
||||
size={25}
|
||||
onPress={() => {
|
||||
downloadChapter(
|
||||
downloaded ? 1 : 0,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName
|
||||
);
|
||||
setDownloaded(!downloaded);
|
||||
if (!chapter.downloaded) {
|
||||
downloadChapter(
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName
|
||||
);
|
||||
} else {
|
||||
deleteChapter(
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName
|
||||
);
|
||||
}
|
||||
}}
|
||||
style={{ margin: 2 }}
|
||||
/>
|
||||
@@ -105,7 +117,14 @@ const ChapterCard = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default ChapterCard;
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
downloading: state.novelReducer.downloading,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, { downloadChapter, deleteChapter })(
|
||||
ChapterCard
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
chapterCardContainer: {
|
||||
|
||||
@@ -81,6 +81,7 @@ const NovelInfoHeader = ({
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{novel["Author(s)"].replace(",", ", ")}
|
||||
</Text>
|
||||
|
||||
@@ -10,6 +10,9 @@ import { useSelector } from "react-redux";
|
||||
|
||||
const BoxNovel = ({ navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
|
||||
const library = useSelector((state) => state.libraryReducer.novels);
|
||||
|
||||
@@ -83,7 +86,8 @@ const BoxNovel = ({ navigation }) => {
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={styles.list}
|
||||
numColumns={3}
|
||||
numColumns={itemsPerRow}
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
|
||||
@@ -11,6 +11,9 @@ import EmptyView from "../../../components/common/EmptyView";
|
||||
const ReadLightNovel = ({ navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
const library = useSelector((state) => state.libraryReducer.novels);
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
@@ -75,7 +78,8 @@ const ReadLightNovel = ({ navigation }) => {
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
numColumns={3}
|
||||
numColumns={itemsPerRow}
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
|
||||
@@ -24,6 +24,7 @@ export const fetchNovelFromSource = async (extensionId, novelUrl) => {
|
||||
lastRead: res.novelChapters[0].chapterUrl,
|
||||
lastReadName: res.novelChapters[0].chapterName,
|
||||
extensionId: res.extensionId,
|
||||
novelChapters: res.novelChapters,
|
||||
};
|
||||
|
||||
return novel;
|
||||
|
||||
+10
-9
@@ -395,19 +395,20 @@ export const downloadChapterFromSource = async (
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteChapterFromDb = (extensionId, novelUrl, chapterUrl) => {
|
||||
const updateIsDownloadedQuery = `UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ? AND novelUrl = ? AND extensionId = ?`;
|
||||
const deleteChapterQuery = `DELETE FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ? AND extensionId = ?`;
|
||||
export const deleteChapterFromDb = async (novelUrl, chapterUrl) => {
|
||||
const updateIsDownloadedQuery = `UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ? AND novelUrl = ?`;
|
||||
const deleteChapterQuery = `DELETE FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ?`;
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(updateIsDownloadedQuery, [
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
extensionId,
|
||||
]);
|
||||
tx.executeSql(
|
||||
updateIsDownloadedQuery,
|
||||
[chapterUrl, novelUrl],
|
||||
(tx, res) => console.log(`Chapter deleted`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
tx.executeSql(
|
||||
deleteChapterQuery,
|
||||
[chapterUrl, novelUrl, extensionId],
|
||||
[chapterUrl, novelUrl],
|
||||
(tx, res) => console.log(`Chapter deleted`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ export const lightTheme = {
|
||||
textColorPrimary: "#000000",
|
||||
textColorSecondary: "rgba(0,0,0,0.54)",
|
||||
textColorHintDark: "rgba(0,0,0,0.38)",
|
||||
rippleColor: "#C2C2C2",
|
||||
rippleColor: "rgba(0,0,0,0.12)",
|
||||
searchBarColor: "#FFFFFF",
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user