From f2dd6b11bd8ce12197c1e48c406b2b2de2ca99c0 Mon Sep 17 00:00:00 2001 From: TecktonikMuffin Date: Tue, 3 May 2022 20:27:55 +1200 Subject: [PATCH] Add release list filters (None, on watch list, downloaded) --- HelperFunctions/index.ts | 8 ++ components/ReleaseHeader.tsx | 121 ++++++++++++++---- components/ReleasesTab.tsx | 36 +++++- .../SettingsDivider.tsx | 11 +- 4 files changed, 143 insertions(+), 33 deletions(-) diff --git a/HelperFunctions/index.ts b/HelperFunctions/index.ts index fa52a08..fb6e5d0 100644 --- a/HelperFunctions/index.ts +++ b/HelperFunctions/index.ts @@ -49,6 +49,14 @@ export function humanFileSize(bytes: number, si = false, dp = 1) { return bytes.toFixed(dp) + ' ' + units[u]; } +export async function asyncFilter( + arr: T[], + predicate: (value: T) => Promise, +) { + const results = await Promise.all(arr.map(predicate)); + return arr.filter((_v, index) => results[index]); +} + interface FilePathModuleInterface { getFolderPathFromUri( contentUri: string, diff --git a/components/ReleaseHeader.tsx b/components/ReleaseHeader.tsx index fade062..4c967fa 100644 --- a/components/ReleaseHeader.tsx +++ b/components/ReleaseHeader.tsx @@ -1,46 +1,123 @@ import * as React from 'react'; import {View, Keyboard, useWindowDimensions} from 'react-native'; -import {Appbar, Button, Searchbar} from 'react-native-paper'; +import {Appbar, Button, Dialog, Portal, Searchbar} from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; +import {RadioButton} from 'react-native-paper'; +import AsyncStorage from '@react-native-async-storage/async-storage'; interface ReleaseTabHeaderProps { onSearchChanged: (query: string) => void; onSearchCancelled: () => void; + onFilterChanged: (filter: ShowFilter) => void; } -export const ReleaseTabHeader = (props: ReleaseTabHeaderProps) => { - const {onSearchChanged, onSearchCancelled} = props; + +export enum ShowFilter { + None = '0', + Downloaded = '1', + Watching = '2', +} + +export const ReleaseTabHeader = ({ + onSearchChanged, + onSearchCancelled, + onFilterChanged, +}: ReleaseTabHeaderProps) => { const {width} = useWindowDimensions(); const [searchQuery, setSearchQuery] = React.useState(''); + const [filterPanelShown, setFilterPanelShown] = React.useState(false); + const [checked, setChecked] = React.useState(ShowFilter.None); const onChangeText = (query: string) => { onSearchChanged(query); setSearchQuery(query); }; - const onBackButtonPressed = () => { + const onBackButtonPressed = async () => { onSearchCancelled(); setSearchQuery(''); Keyboard.dismiss(); + const lastFilter = (await AsyncStorage.getItem( + 'headerFilter', + )) as ShowFilter | null; + if (lastFilter) { + setChecked(lastFilter); + onFilterChanged(lastFilter); + } + }; + + const onFilterPressed = async (filterValue: ShowFilter) => { + setChecked(filterValue); + onFilterChanged(filterValue); + await AsyncStorage.setItem('headerFilter', filterValue); + }; + + const toggleFilterPanel = () => { + setFilterPanelShown(!filterPanelShown); }; + React.useEffect(() => { + (async () => { + const lastFilter = (await AsyncStorage.getItem( + 'headerFilter', + )) as ShowFilter | null; + if (lastFilter) { + setChecked(lastFilter); + onFilterChanged(lastFilter); + } + })(); + }, [onFilterChanged]); + return ( - - - - - - + <> + + + + + + + + + + Filter + + onFilterPressed(value as ShowFilter)} + value={checked}> + + + + + + + + + + + ); }; diff --git a/components/ReleasesTab.tsx b/components/ReleasesTab.tsx index 375d903..40aa799 100644 --- a/components/ReleasesTab.tsx +++ b/components/ReleasesTab.tsx @@ -6,9 +6,11 @@ import {Appearance} from 'react-native-appearance'; import {ShowInfo, WatchList} from '../models/models'; import AsyncStorage from '@react-native-async-storage/async-storage'; import {StorageKeys} from '../enums/enum'; -import {ReleaseTabHeader} from './ReleaseHeader'; +import {ReleaseTabHeader, ShowFilter} from './ReleaseHeader'; import {SubsPleaseApi} from '../SubsPleaseApi'; import debounce from 'lodash.debounce'; +import {downloadedShows} from '../services/DownloadedShows'; +import {asyncFilter} from '../HelperFunctions'; type ReleaseTabProps = { shows: ShowInfo[]; @@ -23,6 +25,8 @@ export const ReleasesTab = ({ const {colors} = useTheme(); const [watchList, setWatchList] = React.useState(); const [showList, setShowList] = React.useState(shows); + const [filteredShowList, setFilteredShowList] = React.useState(shows); + const [showFilter, setShowFilter] = React.useState(ShowFilter.None); const scrollY = React.useRef(new Animated.Value(0)).current; const {height} = useWindowDimensions(); @@ -44,6 +48,33 @@ export const ReleasesTab = ({ })(); }, []); + React.useEffect(() => { + (async () => { + const getFilteredList = async () => { + if (showFilter === ShowFilter.Downloaded) { + return asyncFilter(showList, async show => { + const downloadedEpisodesForSeries = await asyncFilter( + show.downloads, + async download => + (download.res === '720' || download.res === '1080') && + (await downloadedShows.isShowDownloaded( + show.show, + download.magnet, + )), + ); + return downloadedEpisodesForSeries.length > 0; + }); + } else if (showFilter === ShowFilter.Watching) { + const watchingShowNames = + watchList?.shows.map(show => show.showName) ?? []; + return showList.filter(show => watchingShowNames.includes(show.show)); + } + return showList; + }; + setFilteredShowList(await getFilteredList()); + })(); + }, [showFilter, showList, watchList?.shows]); + const onWatchListChanged = (updatedWatchList: WatchList) => { setWatchList({...updatedWatchList}); AsyncStorage.setItem( @@ -73,10 +104,11 @@ export const ReleasesTab = ({ setShowFilter(filterValue)} /> { const {colors} = useTheme();