From d11348d9a39f8afa3f69c13340e70dd6cf0bf695 Mon Sep 17 00:00:00 2001 From: Rajarshee Chatterjee Date: Sat, 30 Oct 2021 09:56:44 +0530 Subject: [PATCH] Add parallel searching in global search and migration --- src/components/List.js | 8 +- src/screens/browse/SettingsBrowseScreen.js | 5 + .../browse/globalsearch/GlobalSearch.js | 129 ++++++--------- .../globalsearch/GlobalSearchNovelCover.js | 45 +++--- .../globalsearch/GlobalSearchNovelList.js | 2 +- .../globalsearch/GlobalSearchSourceItem.js | 33 +++- .../browse/migration/MigrationNovelList.js | 2 +- .../browse/migration/MigrationNovels.js | 152 +++++++++--------- 8 files changed, 185 insertions(+), 191 deletions(-) diff --git a/src/components/List.js b/src/components/List.js index d09bc965..f356337f 100644 --- a/src/components/List.js +++ b/src/components/List.js @@ -72,13 +72,7 @@ const InfoItem = ({title, icon, theme}) => ( titleStyle={{color: theme.textColorSecondary, fontSize: 14}} titleNumberOfLines={5} left={() => - icon && ( - - ) + icon && } /> ); diff --git a/src/screens/browse/SettingsBrowseScreen.js b/src/screens/browse/SettingsBrowseScreen.js index b69174f7..5def40cd 100644 --- a/src/screens/browse/SettingsBrowseScreen.js +++ b/src/screens/browse/SettingsBrowseScreen.js @@ -89,6 +89,11 @@ const BrowseSettings = ({navigation}) => { } /> + Browse { ); const {searchAllSources = false} = useSettings(); - const [loading, setLoading] = useState(false); const [searchText, setSearchText] = useState(novelName); const [searchResults, setSearchResults] = useState([]); const [progress, setProgress] = useState(0); @@ -51,7 +45,6 @@ const GlobalSearch = ({route, navigation}) => { if (novelName) { onSubmitEditing(); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -65,47 +58,51 @@ const GlobalSearch = ({route, navigation}) => { const onChangeText = text => setSearchText(text); const onSubmitEditing = async () => { - setSearchResults([]); setProgress(0); let globalSearchSources = searchAllSources ? sources : pinnedSources; - for (let i = 0; i < globalSearchSources.length; i++) { - if (isMounted.current === false) { - break; + setSearchResults( + globalSearchSources.map(item => ({ + sourceId: item.sourceId, + sourceName: item.sourceName, + lang: item.lang, + loading: true, + novels: [], + error: null, + })), + ); + + globalSearchSources.map(async item => { + if (isMounted.current === true) { + try { + const source = getSource(item.sourceId); + const data = await source.searchNovels(encodeURI(searchText)); + + setSearchResults(prevState => + prevState.map(sourceItem => + sourceItem.sourceId === item.sourceId + ? {...sourceItem, novels: data, loading: false} + : {...sourceItem}, + ), + ); + } catch (e) { + setSearchResults(prevState => + prevState.map(sourceItem => + sourceItem.sourceId === item.sourceId + ? { + ...sourceItem, + loading: false, + error: e.message, + } + : sourceItem, + ), + ); + } + + setProgress(before => before + 1 / globalSearchSources.length); } - - try { - setLoading(true); - - const source = getSource(globalSearchSources[i].sourceId); - const data = await source.searchNovels(encodeURI(searchText)); - - setSearchResults(before => [ - ...before, - { - sourceId: globalSearchSources[i].sourceId, - sourceName: globalSearchSources[i].sourceName, - lang: globalSearchSources[i].lang, - novels: data, - }, - ]); - setLoading(false); - } catch (error) { - showToast(globalSearchSources[i].sourceName + ': ' + error.message); - setSearchResults(before => [ - ...before, - { - sourceId: globalSearchSources[i].sourceId, - sourceName: globalSearchSources[i].sourceName, - lang: globalSearchSources[i].lang, - novels: [], - }, - ]); - setLoading(false); - } - setProgress(before => before + 1 / globalSearchSources.length); - } + }); }; const renderItem = ({item}) => ( @@ -133,42 +130,22 @@ const GlobalSearch = ({route, navigation}) => { )} item.sourceId.toString()} renderItem={renderItem} extraData={pinned} ListEmptyComponent={ - <> - {!loading && ( - - )} - - } - ListFooterComponent={ - loading && ( - - - - ) + } /> @@ -176,5 +153,3 @@ const GlobalSearch = ({route, navigation}) => { }; export default GlobalSearch; - -// const styles = StyleSheet.create({}); diff --git a/src/screens/browse/globalsearch/GlobalSearchNovelCover.js b/src/screens/browse/globalsearch/GlobalSearchNovelCover.js index 5920bf41..4b7ef452 100644 --- a/src/screens/browse/globalsearch/GlobalSearchNovelCover.js +++ b/src/screens/browse/globalsearch/GlobalSearchNovelCover.js @@ -1,5 +1,6 @@ import React from 'react'; import {StyleSheet, View, Text, Image, Pressable} from 'react-native'; +import {defaultCoverUri} from '../../../sources/helpers/constants'; const GlobalSearchNovelCover = ({ novel, @@ -10,32 +11,28 @@ const GlobalSearchNovelCover = ({ }) => { const {novelName, novelCover} = novel; + const uri = + novelCover && !novelCover.startsWith('/') ? novelCover : defaultCoverUri; + return ( - + - <> - - - {novelName} - - + + + {novelName} + ); @@ -44,10 +41,16 @@ const GlobalSearchNovelCover = ({ export default GlobalSearchNovelCover; const styles = StyleSheet.create({ - novelContainer: { + container: { + flex: 1, + borderRadius: 6, + overflow: 'hidden', + }, + pressable: { paddingHorizontal: 5, paddingVertical: 4, borderRadius: 4, + flex: 1, }, novelCover: { height: 150, diff --git a/src/screens/browse/globalsearch/GlobalSearchNovelList.js b/src/screens/browse/globalsearch/GlobalSearchNovelList.js index 4b8e6bcf..d6f6e013 100644 --- a/src/screens/browse/globalsearch/GlobalSearchNovelList.js +++ b/src/screens/browse/globalsearch/GlobalSearchNovelList.js @@ -29,7 +29,7 @@ const GlobalSearchNovelList = ({data, theme, library, navigation}) => { contentContainerStyle={styles.listContainer} horizontal={true} data={data} - keyExtractor={item => item.novelUrl} + keyExtractor={item => item.sourceId + item.novelUrl} renderItem={renderItem} ListEmptyComponent={ListEmptyComponent} /> diff --git a/src/screens/browse/globalsearch/GlobalSearchSourceItem.js b/src/screens/browse/globalsearch/GlobalSearchSourceItem.js index e53c7660..6f021e08 100644 --- a/src/screens/browse/globalsearch/GlobalSearchSourceItem.js +++ b/src/screens/browse/globalsearch/GlobalSearchSourceItem.js @@ -1,10 +1,14 @@ import React from 'react'; -import {StyleSheet, View, Text} from 'react-native'; +import {StyleSheet, View, Text, ActivityIndicator} from 'react-native'; import GlobalSearchNovelList from './GlobalSearchNovelList'; const GlobalSearchSourceItem = ({source, library, theme, navigation}) => { - const {sourceName, lang, novels} = source; + const {sourceName, lang, loading, novels, error} = source; + + const colorError = { + color: theme.statusBar === 'dark-content' ? '#B3261E' : '#F2B8B5', + }; return ( <> @@ -14,12 +18,21 @@ const GlobalSearchSourceItem = ({source, library, theme, navigation}) => { {lang} - + {error ? ( + {error} + ) : loading ? ( + + ) : ( + + )} ); }; @@ -31,4 +44,8 @@ const styles = StyleSheet.create({ padding: 8, paddingVertical: 16, }, + error: { + paddingHorizontal: 8, + paddingVertical: 16, + }, }); diff --git a/src/screens/browse/migration/MigrationNovelList.js b/src/screens/browse/migration/MigrationNovelList.js index 527ff376..6bd18559 100644 --- a/src/screens/browse/migration/MigrationNovelList.js +++ b/src/screens/browse/migration/MigrationNovelList.js @@ -48,7 +48,7 @@ const MigrationNovelList = ({data, theme, library, navigation}) => { contentContainerStyle={styles.flatListCont} horizontal={true} data={data} - keyExtractor={item => item.novelUrl} + keyExtractor={item => item.sourceId + item.novelUrl} renderItem={renderItem} ListEmptyComponent={ { const isMounted = React.useRef(true); const [progress, setProgress] = useState(0); - const [loading, setLoading] = useState(false); const [searchResults, setSearchResults] = useState(''); const {searchAllSources = false} = useSettings(); let { @@ -44,48 +42,52 @@ const MigrationNovels = ({navigation, route}) => { const getSearchResults = async () => { let migrationSources = searchAllSources ? sources : pinnedSources; - for (let i = 0; i < migrationSources.length; i++) { - if (isMounted.current === false) { - break; + + setSearchResults( + migrationSources.map(item => ({ + sourceId: item.sourceId, + sourceName: item.sourceName, + lang: item.lang, + loading: true, + novels: [], + error: null, + })), + ); + + migrationSources.map(async item => { + if (isMounted.current === true) { + try { + const source = getSource(item.sourceId); + const data = await source.searchNovels(novelName); + + setSearchResults(prevState => + prevState.map(sourceItem => + sourceItem.sourceId === item.sourceId + ? {...sourceItem, novels: data, loading: false} + : {...sourceItem}, + ), + ); + } catch (e) { + setSearchResults(prevState => + prevState.map(sourceItem => + sourceItem.sourceId === item.sourceId + ? { + ...sourceItem, + loading: false, + error: e.message, + } + : sourceItem, + ), + ); + } + + setProgress(before => before + 1 / migrationSources.length); } - - try { - setLoading(true); - - const source = getSource(migrationSources[i].sourceId); - const data = await source.searchNovels(novelName); - - setSearchResults(before => [ - ...before, - { - sourceId: migrationSources[i].sourceId, - sourceName: migrationSources[i].sourceName, - sourceLanguage: migrationSources[i].lang, - novels: data, - }, - ]); - setLoading(false); - } catch (error) { - showToast(error.message); - - setSearchResults(before => [ - ...before, - { - sourceId: migrationSources[i].sourceId, - sourceName: migrationSources[i].sourceName, - sourceLanguage: migrationSources[i].lang, - novels: [], - }, - ]); - setLoading(false); - } - setProgress(before => before + 1 / migrationSources.length); - } + }); }; useEffect(() => { getSearchResults(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -95,62 +97,55 @@ const MigrationNovels = ({navigation, route}) => { }; }, []); + const colorError = { + color: theme.statusBar === 'dark-content' ? '#B3261E' : '#F2B8B5', + }; + const renderItem = ({item}) => ( <> {item.sourceName} - {item.sourceLanguage} + {item.lang} - + {item.error ? ( + {item.error} + ) : item.loading ? ( + + ) : ( + + )} ); return ( - navigation.goBack()} /> + {progress > 0 && ( )} item.sourceId.toString()} renderItem={renderItem} extraData={pinned} ListEmptyComponent={ - <> - {!loading && ( - - )} - - } - ListFooterComponent={ - loading && ( - - - - ) + } /> @@ -159,4 +154,9 @@ const MigrationNovels = ({navigation, route}) => { export default MigrationNovels; -// const styles = StyleSheet.create({}); +const styles = StyleSheet.create({ + error: { + paddingHorizontal: 8, + paddingVertical: 16, + }, +});