Add library sort/filter sheet and notification icon

This commit is contained in:
Rajarshee Chatterjee
2021-05-14 18:30:15 +05:30
parent 3a156f32a0
commit 67096eb1ff
13 changed files with 352 additions and 27 deletions
+5 -1
View File
@@ -9,12 +9,16 @@
"splash": {
"image": "./assets/splash.png",
"resizeMode": "cover",
"backgroundColor": "#000000"
"backgroundColor": "#222831"
},
"updates": {
"fallbackToCacheTimeout": 0,
"enabled": false
},
"notification": {
"icon": "./assets/notification.png",
"color": "#00adb5"
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 36 KiB

+11 -2
View File
@@ -14,10 +14,19 @@ const ListSubHeader = ({ children, theme }) => (
</List.Subheader>
);
const ListItem = ({ title, description, icon, onPress, theme, right }) => (
const ListItem = ({
title,
description,
icon,
onPress,
theme,
right,
titleStyle,
}) => (
<List.Item
title={title}
titleStyle={{ color: theme.textColorPrimary }}
style={[!right && { paddingVertical: 12 }]}
titleStyle={[{ color: theme.textColorPrimary }, titleStyle]}
description={description}
descriptionStyle={{ color: theme.textColorSecondary }}
descriptionNumberOfLines={1}
+5 -5
View File
@@ -1,7 +1,7 @@
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const getLibraryQuery = `
const getLibraryQuery = (sort, filter) => `
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded
FROM novels
LEFT JOIN (
@@ -18,15 +18,15 @@ const getLibraryQuery = `
GROUP BY chapters.novelId
) AS D
ON novels.novelId = D.novelId
WHERE novels.followed = 1
GROUP BY novels.novelId
WHERE novels.followed = 1 ${filter ? "AND " + filter : ""}
${sort ? "ORDER BY " + sort : ""}
`;
export const getLibrary = () => {
export const getLibrary = (sort, filter) => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
getLibraryQuery,
getLibraryQuery(sort, filter),
null,
(txObj, { rows: { _array } }) => {
// console.log(_array);
+7
View File
@@ -95,6 +95,12 @@ const useTrackingStatus = () => {
return tracker;
};
const useLibraryFilters = () => {
const filters = useSelector((state) => state.libraryReducer.filters);
return filters;
};
export {
useTheme,
useReaderSettings,
@@ -106,4 +112,5 @@ export {
useNovel,
useChapter,
useSavedSettings,
useLibraryFilters,
};
+38 -11
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useCallback, useRef } from "react";
import {
StyleSheet,
Text,
@@ -21,12 +21,22 @@ import {
import { updateLibraryAction } from "../../redux/updates/updates.actions";
import { useTheme } from "../../Hooks/reduxHooks";
import { setNovel } from "../../redux/novel/novel.actions";
import { Portal } from "react-native-paper";
import LibraryFilterSheet from "./components/LibraryFilterSheet";
const LibraryScreen = ({ navigation }) => {
const theme = useTheme();
const dispatch = useDispatch();
const { loading, novels } = useSelector((state) => state.libraryReducer);
const libraryFilterSheetRef = useRef(null);
const {
loading,
novels,
filters: { sort, filter },
} = useSelector((state) => state.libraryReducer);
console.log("1.", sort, filter);
const [refreshing, setRefreshing] = useState(false);
const [searchText, setSearchText] = useState("");
@@ -34,7 +44,7 @@ const LibraryScreen = ({ navigation }) => {
useFocusEffect(
useCallback(() => {
setSearchText("");
dispatch(getLibraryAction());
dispatch(getLibraryAction(sort, filter));
}, [getLibraryAction])
);
@@ -45,13 +55,13 @@ const LibraryScreen = ({ navigation }) => {
};
const clearSearchbar = () => {
dispatch(getLibraryAction());
dispatch(getLibraryAction(sort, filter));
setSearchText("");
};
const onChangeText = (text) => {
setSearchText(text);
dispatch(searchLibraryAction(text));
dispatch(searchLibraryAction(text, sort, filter));
};
const renderItem = ({ item }) => (
@@ -73,6 +83,10 @@ const LibraryScreen = ({ navigation }) => {
/>
);
// if (!loading) {
// console.log(novels);
// }
const listEmptyComponent = () =>
searchText !== "" ? (
<Text style={[styles.emptySearch, { color: theme.colorAccent }]}>
@@ -99,17 +113,30 @@ const LibraryScreen = ({ navigation }) => {
clearSearchbar={clearSearchbar}
onChangeText={onChangeText}
left="magnify"
right="dots-vertical"
onPressRight={() => libraryFilterSheetRef.current.show()}
theme={theme}
/>
{loading ? (
<ActivityIndicator size="small" color={theme.colorAccent} />
) : (
<NovelList
data={novels}
renderItem={renderItem}
refreshControl={refreshControl()}
ListEmptyComponent={listEmptyComponent}
/>
<>
<NovelList
data={novels}
renderItem={renderItem}
refreshControl={refreshControl()}
ListEmptyComponent={listEmptyComponent}
/>
<Portal>
<LibraryFilterSheet
bottomSheetRef={libraryFilterSheetRef}
theme={theme}
dispatch={dispatch}
sort={sort}
filter={filter}
/>
</Portal>
</>
)}
</View>
</>
@@ -0,0 +1,254 @@
import React, { useState } from "react";
import { StyleSheet, View, Text, Animated } from "react-native";
import { List, RadioButton, Checkbox } from "react-native-paper";
import Bottomsheet from "rn-sliding-up-panel";
import BottomSheetHandle from "../../../Components/BottomSheetHandle";
import { ListItem } from "../../../Components/List";
import { filterLibrary } from "../../../redux/library/library.actions";
const LibraryFilterSheet = ({
bottomSheetRef,
dispatch,
sort,
filter,
theme,
}) => {
const [animatedValue] = useState(new Animated.Value(0));
return (
<Bottomsheet
animatedValue={animatedValue}
ref={bottomSheetRef}
draggableRange={{ top: 540, bottom: 0 }}
snappingPoints={[0, 540]}
>
<View
style={[
styles.contentContainer,
{ backgroundColor: theme.colorPrimary },
]}
>
<BottomSheetHandle theme={theme} />
<View style={{ marginTop: 24 }}>
<Text
style={{
color: theme.colorAccent,
paddingHorizontal: 16,
paddingBottom: 8,
textTransform: "uppercase",
}}
>
SORT
</Text>
<ListItem
title="Date Added"
theme={theme}
right={
sort === "novels.novelId ASC"
? "arrow-up"
: sort === "novels.novelId DESC"
? "arrow-down"
: null
}
onPress={() =>
sort === "novels.novelId ASC"
? dispatch(
filterLibrary(
"novels.novelId DESC",
filter
)
)
: dispatch(
filterLibrary(
"novels.novelId ASC",
filter
)
)
}
/>
<ListItem
title="Alphabetically"
theme={theme}
right={
sort === "novels.novelName ASC"
? "arrow-up"
: sort === "novels.novelName DESC"
? "arrow-down"
: null
}
onPress={() =>
sort === "novels.novelName ASC"
? dispatch(
filterLibrary(
"novels.novelName DESC",
filter
)
)
: dispatch(
filterLibrary(
"novels.novelName ASC",
filter
)
)
}
/>
<ListItem
title="Unread"
theme={theme}
right={
sort === "novels.unread ASC"
? "arrow-up"
: sort === "novels.unread DESC"
? "arrow-down"
: null
}
onPress={() =>
sort === "novels.unread ASC"
? dispatch(
filterLibrary(
"novels.unread DESC",
filter
)
)
: dispatch(
filterLibrary("novels.unread ASC", filter)
)
}
/>
<ListItem
title="Downloaded"
theme={theme}
right={
sort === "chaptersDownloaded ASC"
? "arrow-up"
: sort === "chaptersDownloaded DESC"
? "arrow-down"
: null
}
onPress={() =>
sort === "chaptersDownloaded ASC"
? dispatch(
filterLibrary(
"chaptersDownloaded DESC",
filter
)
)
: dispatch(
filterLibrary(
"chaptersDownloaded ASC",
filter
)
)
}
/>
<ListItem
title="Total Chapters"
theme={theme}
right={
sort === "chaptersUnread ASC"
? "arrow-up"
: sort === "chaptersUnread DESC"
? "arrow-down"
: null
}
onPress={() =>
sort === "chaptersUnread ASC"
? dispatch(
filterLibrary(
"chaptersUnread DESC",
filter
)
)
: dispatch(
filterLibrary(
"chaptersUnread ASC",
filter
)
)
}
/>
</View>
<View>
<Text
style={{
color: theme.colorAccent,
paddingHorizontal: 16,
paddingVertical: 8,
textTransform: "uppercase",
}}
>
Filter
</Text>
<Checkbox.Item
label="Downloaded"
labelStyle={{ color: theme.textColorPrimary }}
color={theme.colorAccent}
uncheckedColor={theme.textColorHint}
status={
filter === "chaptersDownloaded > 0"
? "checked"
: "unchecked"
}
onPress={() =>
filter === "chaptersDownloaded > 0"
? dispatch(filterLibrary(sort, ""))
: dispatch(
filterLibrary(
sort,
"chaptersDownloaded > 0"
)
)
}
/>
<Checkbox.Item
label="Unread"
labelStyle={{ color: theme.textColorPrimary }}
color={theme.colorAccent}
uncheckedColor={theme.textColorHint}
status={
filter === "unread = 1" ? "checked" : "unchecked"
}
onPress={() =>
filter === "unread = 1"
? dispatch(filterLibrary(sort, ""))
: dispatch(filterLibrary(sort, "unread = 1"))
}
/>
<Checkbox.Item
label="Completed"
labelStyle={{ color: theme.textColorPrimary }}
color={theme.colorAccent}
uncheckedColor={theme.textColorHint}
status={
filter === "chaptersUnread IS NULL"
? "checked"
: "unchecked"
}
onPress={() =>
filter === "chaptersUnread IS NULL"
? dispatch(filterLibrary(sort, ""))
: dispatch(
filterLibrary(
sort,
"chaptersUnread IS NULL"
)
)
}
/>
</View>
</View>
</Bottomsheet>
);
};
export default LibraryFilterSheet;
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
borderTopRightRadius: 8,
borderTopLeftRadius: 8,
},
});
+1 -1
View File
@@ -56,7 +56,7 @@ const Novel = ({ route, navigation }) => {
}, [getNovelAction]);
const onRefresh = () => {
dispatch(updateNovelAction(sourceId, novelUrl, novelId));
dispatch(updateNovelAction(sourceId, novelUrl, novelId, sort, filter));
showToast(`Updated ${novelName}`);
};
+19 -5
View File
@@ -2,6 +2,7 @@ import {
GET_LIBRARY_NOVELS,
GET_LIBRARY_SEARCH_RESULTS,
SET_LIBRARY_LOADING,
SORT_FILTER_LIBRARY,
} from "./library.types";
import {
getLibrary,
@@ -9,8 +10,9 @@ import {
} from "../../Database/queries/LibraryQueries";
import { restoreBackup } from "../../Services/backup";
export const getLibraryAction = () => async (dispatch) => {
const res = await getLibrary();
export const getLibraryAction = (sort, filter) => async (dispatch) => {
// console.log(sort, filter);
const res = await getLibrary(sort, filter);
dispatch({
type: GET_LIBRARY_NOVELS,
@@ -18,11 +20,23 @@ export const getLibraryAction = () => async (dispatch) => {
});
};
export const searchLibraryAction = (searchText) => async (dispatch) => {
const res = await searchLibrary(searchText);
export const searchLibraryAction =
(searchText, sort, filter) => async (dispatch) => {
const res = await searchLibrary(searchText, sort, filter);
dispatch({
type: GET_LIBRARY_SEARCH_RESULTS,
payload: res,
});
};
export const filterLibrary = (sort, filter) => async (dispatch) => {
dispatch({ type: SORT_FILTER_LIBRARY, payload: { sort, filter } });
const res = await getLibrary(sort, filter);
dispatch({
type: GET_LIBRARY_SEARCH_RESULTS,
type: GET_LIBRARY_NOVELS,
payload: res,
});
};
+8
View File
@@ -2,11 +2,13 @@ import {
GET_LIBRARY_NOVELS,
GET_LIBRARY_SEARCH_RESULTS,
SET_LIBRARY_LOADING,
SORT_FILTER_LIBRARY,
} from "./library.types";
const initialState = {
novels: [],
searchResults: [],
filters: { sort: "novels.novelId ASC", filter: "" },
loading: true,
};
@@ -20,6 +22,12 @@ const libraryReducer = (state = initialState, action) => {
return { ...state, novels: payload, loading: false };
case GET_LIBRARY_SEARCH_RESULTS:
return { ...state, novels: payload, loading: false };
case SORT_FILTER_LIBRARY:
console.log("2.", payload.sort, payload.filter);
return {
...state,
filters: { sort: payload.sort, filter: payload.filter },
};
default:
return state;
}
+2
View File
@@ -1,3 +1,5 @@
export const GET_LIBRARY_NOVELS = "GET_LIBRARY_NOVELS";
export const GET_LIBRARY_SEARCH_RESULTS = "GET_LIBRARY_SEARCH_RESULTS";
export const SET_LIBRARY_LOADING = "SET_LIBRARY_LOADING";
export const SORT_FILTER_LIBRARY = "SORT_FILTER_LIBRARY";
+2 -2
View File
@@ -257,13 +257,13 @@ export const deleteAllChaptersAction = (chapters) => async (dispatch) => {
};
export const updateNovelAction =
(sourceId, novelUrl, novelId) => async (dispatch) => {
(sourceId, novelUrl, novelId, sort, filter) => async (dispatch) => {
dispatch({ type: FETCHING_NOVEL });
await updateNovel(sourceId, novelUrl, novelId);
let novel = await getNovel(sourceId, novelUrl);
let chapters = await getChapters(novel.novelId);
let chapters = await getChapters(novel.novelId, sort, filter);
dispatch({
type: UPDATE_NOVEL,