Filter sources by languages
This commit is contained in:
@@ -21,6 +21,7 @@ import { githubUpdateChecker } from "../Hooks/githubUpdateChecker";
|
||||
import NewUpdateDialog from "../Components/NewUpdateDialog";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import BrowseNovelUpdates from "../Screens/Browse/discover/BrowseNovelUpdates";
|
||||
import BrowseSettings from "../Screens/Browse/BrowseSettings";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
@@ -49,6 +50,10 @@ const MainNavigator = () => {
|
||||
<Stack.Screen name="MoreStack" component={MoreStack} />
|
||||
<Stack.Screen name="Extension" component={Extension} />
|
||||
<Stack.Screen name="BrowseMal" component={MalTopNovels} />
|
||||
<Stack.Screen
|
||||
name="BrowseSettings"
|
||||
component={BrowseSettings}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="BrowseNovelUpdates"
|
||||
component={BrowseNovelUpdates}
|
||||
|
||||
@@ -26,15 +26,23 @@ const Browse = ({ navigation }) => {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
// const [globalSearch, setGlobalSearch] = useState(false);
|
||||
// const [searchResult, setSearchResults] = useState();
|
||||
const { sources, search, loading, pinned } = useSelector(
|
||||
(state) => state.sourceReducer
|
||||
);
|
||||
let {
|
||||
sources,
|
||||
search,
|
||||
loading,
|
||||
pinned,
|
||||
filters,
|
||||
showNovelUpdates,
|
||||
showMyAnimeList,
|
||||
} = useSelector((state) => state.sourceReducer);
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isPinned = (sourceId) =>
|
||||
pinned.indexOf(sourceId) === -1 ? false : true;
|
||||
|
||||
sources = sources.filter((source) => filters.indexOf(source.lang) === -1);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getSourcesAction());
|
||||
}, [getSourcesAction]);
|
||||
@@ -88,6 +96,10 @@ const Browse = ({ navigation }) => {
|
||||
icon: "swap-vertical-variant",
|
||||
onPress: () => navigation.navigate("Migration"),
|
||||
},
|
||||
{
|
||||
icon: "cog-outline",
|
||||
onPress: () => navigation.navigate("BrowseSettings"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -99,17 +111,23 @@ const Browse = ({ navigation }) => {
|
||||
extraData={pinned}
|
||||
ListHeaderComponent={
|
||||
<View>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorSecondary,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
}}
|
||||
>
|
||||
Discover
|
||||
</Text>
|
||||
<NovelUpdatesCard theme={theme} />
|
||||
<MalCard theme={theme} />
|
||||
{(showNovelUpdates || showMyAnimeList) && (
|
||||
<>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorSecondary,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
}}
|
||||
>
|
||||
Discover
|
||||
</Text>
|
||||
{showNovelUpdates && (
|
||||
<NovelUpdatesCard theme={theme} />
|
||||
)}
|
||||
{showMyAnimeList && <MalCard theme={theme} />}
|
||||
</>
|
||||
)}
|
||||
{pinned.length > 0 && (
|
||||
<FlatList
|
||||
contentContainerStyle={{ paddingBottom: 16 }}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import React, { useState } from "react";
|
||||
import { FlatList, Pressable, Text, View } from "react-native";
|
||||
|
||||
import { Switch } from "react-native-paper";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { Appbar } from "../../Components/Appbar";
|
||||
import { ScreenContainer } from "../../Components/Common";
|
||||
import { ListSubHeader } from "../../Components/List";
|
||||
|
||||
import { useTheme } from "../../Hooks/reduxHooks";
|
||||
import {
|
||||
enableDiscover,
|
||||
filterLanguage,
|
||||
} from "../../redux/source/source.actions";
|
||||
|
||||
const BrowseSettings = ({ navigation }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const { filters, showNovelUpdates, showMyAnimeList } = useSelector(
|
||||
(state) => state.sourceReducer
|
||||
);
|
||||
const languages = ["English", "Spanish"];
|
||||
|
||||
const renderItem = ({ item }) => {
|
||||
return (
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={{
|
||||
padding: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
onPress={() => dispatch(filterLanguage(item))}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>{item}</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={filters.indexOf(item) === -1 ? true : false}
|
||||
onValueChange={() => dispatch(filterLanguage(item))}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ScreenContainer theme={theme}>
|
||||
<Appbar onBackAction={navigation.goBack} title="Sources" />
|
||||
<View>
|
||||
<ListSubHeader theme={theme}>Discover</ListSubHeader>
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={{
|
||||
padding: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
onPress={() => dispatch(enableDiscover("showNovelUpdates"))}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>
|
||||
Novel Updates
|
||||
</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={showNovelUpdates}
|
||||
onValueChange={() =>
|
||||
dispatch(enableDiscover("showNovelUpdates"))
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={{
|
||||
padding: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
onPress={() => dispatch(enableDiscover("showMyAnimeList"))}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>
|
||||
MyAnimeList
|
||||
</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={showMyAnimeList}
|
||||
onValueChange={() =>
|
||||
dispatch(enableDiscover("showMyAnimeList"))
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
<ListSubHeader theme={theme}>Languages</ListSubHeader>
|
||||
<FlatList
|
||||
data={languages}
|
||||
keyExtractor={(item) => item}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</View>
|
||||
</ScreenContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrowseSettings;
|
||||
@@ -0,0 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
const NovelUpdatesBottomSheet = () => {};
|
||||
|
||||
export default NovelUpdatesBottomSheet;
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import { Dimensions, View, Text, StyleSheet } from "react-native";
|
||||
|
||||
import Slider from "@react-native-community/slider";
|
||||
import { Dimensions, View, Text } from "react-native";
|
||||
import { getDeviceOrientation } from "../../../Services/utils/helpers";
|
||||
|
||||
const VerticalScrollbar = ({
|
||||
theme,
|
||||
@@ -12,28 +13,22 @@ const VerticalScrollbar = ({
|
||||
scrollViewRef,
|
||||
setScrollPercentage,
|
||||
}) => {
|
||||
const onSlidingComplete = (value) => {
|
||||
setLoading(true);
|
||||
setScrollPercentage(value);
|
||||
scrollViewRef.current.scrollTo({
|
||||
x: 0,
|
||||
y: Math.round((value * contentSize) / 100),
|
||||
animated: false,
|
||||
});
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
if (hide) {
|
||||
return null;
|
||||
} else {
|
||||
} else if (getDeviceOrientation() === "potrait") {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
position: "absolute",
|
||||
zIndex: 2,
|
||||
// top: 0,
|
||||
right: -(Dimensions.get("window").width / 2) + 40,
|
||||
bottom: 300,
|
||||
backgroundColor: "rgba(0,0,0,0.6)",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
borderRadius: 50,
|
||||
marginHorizontal: 8,
|
||||
transform: [{ rotate: "90deg" }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.verticalSliderContainer}>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
@@ -46,27 +41,16 @@ const VerticalScrollbar = ({
|
||||
<Slider
|
||||
style={{
|
||||
flex: 1,
|
||||
// width: Dimensions.get("window").width - 100,
|
||||
height: 40,
|
||||
// paddingHorizontal: 8,
|
||||
}}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={1}
|
||||
value={scrollPercentage}
|
||||
onSlidingComplete={(value) => {
|
||||
setLoading(true);
|
||||
setScrollPercentage(value);
|
||||
scrollViewRef.current.scrollTo({
|
||||
x: 0,
|
||||
y: Math.round((value * contentSize) / 100),
|
||||
animated: false,
|
||||
});
|
||||
setLoading(false);
|
||||
}}
|
||||
onSlidingComplete={onSlidingComplete}
|
||||
thumbTintColor={theme.colorAccent}
|
||||
minimumTrackTintColor={theme.colorAccent}
|
||||
maximumTrackTintColor="#000000"
|
||||
maximumTrackTintColor={theme.textColorPrimary}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
@@ -79,7 +63,69 @@ const VerticalScrollbar = ({
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.horizontalSliderContainer}>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
marginLeft: 16,
|
||||
}}
|
||||
>
|
||||
{scrollPercentage}
|
||||
</Text>
|
||||
<Slider
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 40,
|
||||
}}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={1}
|
||||
value={scrollPercentage}
|
||||
onSlidingComplete={onSlidingComplete}
|
||||
thumbTintColor={theme.colorAccent}
|
||||
minimumTrackTintColor={theme.colorAccent}
|
||||
maximumTrackTintColor={theme.textColorPrimary}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
marginRight: 16,
|
||||
}}
|
||||
>
|
||||
100
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default VerticalScrollbar;
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { GET_SOURCES, PIN_SOURCES, SEARCH_SOURCES } from "./source.types";
|
||||
import { fetchSources } from "../../Services/Source/source";
|
||||
import {
|
||||
ENABLE_DISCOVER,
|
||||
FILTER_LANGUAGE,
|
||||
GET_SOURCES,
|
||||
PIN_SOURCES,
|
||||
SEARCH_SOURCES,
|
||||
} from "./source.types";
|
||||
import { showToast } from "../../Hooks/showToast";
|
||||
import { sources } from "../../sources/sources";
|
||||
|
||||
@@ -26,3 +31,17 @@ export const pinSourceAction = (sourceId) => async (dispatch) => {
|
||||
payload: sourceId,
|
||||
});
|
||||
};
|
||||
|
||||
export const filterLanguage = (language) => async (dispatch) => {
|
||||
dispatch({
|
||||
type: FILTER_LANGUAGE,
|
||||
payload: language,
|
||||
});
|
||||
};
|
||||
|
||||
export const enableDiscover = (key) => async (dispatch) => {
|
||||
dispatch({
|
||||
type: ENABLE_DISCOVER,
|
||||
payload: key,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { GET_SOURCES, SEARCH_SOURCES, PIN_SOURCES } from "./source.types";
|
||||
import {
|
||||
GET_SOURCES,
|
||||
SEARCH_SOURCES,
|
||||
PIN_SOURCES,
|
||||
FILTER_LANGUAGE,
|
||||
ENABLE_DISCOVER,
|
||||
} from "./source.types";
|
||||
|
||||
const initialState = {
|
||||
sources: [],
|
||||
pinnedSources: [],
|
||||
search: [],
|
||||
pinned: [],
|
||||
filters: [],
|
||||
showNovelUpdates: true,
|
||||
showMyAnimeList: true,
|
||||
loading: true,
|
||||
};
|
||||
|
||||
@@ -30,6 +39,22 @@ const sourceReducer = (state = initialState, action) => {
|
||||
source.sourceName.toLowerCase().includes(payload)
|
||||
),
|
||||
};
|
||||
|
||||
case FILTER_LANGUAGE:
|
||||
return {
|
||||
...state,
|
||||
filters:
|
||||
state.filters.indexOf(payload) === -1
|
||||
? [...state.filters, payload]
|
||||
: state.filters.filter(
|
||||
(language) => language !== payload
|
||||
),
|
||||
};
|
||||
case ENABLE_DISCOVER:
|
||||
return {
|
||||
...state,
|
||||
[payload]: !state[payload],
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const GET_SOURCES = "GET_SOURCES";
|
||||
export const SEARCH_SOURCES = "SEARCH_SOURCES";
|
||||
export const PIN_SOURCES = "PIN_SOURCES";
|
||||
export const FILTER_LANGUAGE = "FILTER_LANGUAGE";
|
||||
export const ENABLE_DISCOVER = "ENABLE_DISCOVER";
|
||||
|
||||
Reference in New Issue
Block a user