Skip to content

Commit

Permalink
Improvement: Optimise available plugins by flashlist
Browse files Browse the repository at this point in the history
  • Loading branch information
nyagami committed Jul 15, 2024
1 parent 1ad2c98 commit 7b1eb7d
Showing 1 changed file with 130 additions and 130 deletions.
260 changes: 130 additions & 130 deletions src/screens/browse/components/BrowseTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import {
View,
Text,
StyleSheet,
SectionList,
RefreshControl,
ListRenderItem,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useBrowseSettings, usePlugins } from '@hooks/persisted';
Expand All @@ -30,7 +28,6 @@ import Animated, {
useSharedValue,
withTiming,
} from 'react-native-reanimated';
import { groupBy } from 'lodash-es';

interface AvailableTabProps {
searchText: string;
Expand Down Expand Up @@ -248,7 +245,7 @@ export const InstalledTab = memo(
);

interface AvailablePluginCardProps {
plugin: PluginItem;
plugin: PluginItem & { header: boolean };
theme: ThemeColors;
installPlugin: (plugin: PluginItem) => Promise<void>;
}
Expand All @@ -270,140 +267,132 @@ const AvailablePluginCard = ({
lineHeight: ratio.value * 20,
}));
return (
<Animated.View
style={[styles.container, { backgroundColor: theme.surface }, viewStyles]}
>
<Animated.View style={{ flexDirection: 'row' }}>
<Animated.Image
source={{ uri: plugin.iconUrl }}
style={[styles.icon, imageStyles, { backgroundColor: theme.surface }]}
/>
<Animated.View style={styles.details}>
<Animated.Text
numberOfLines={1}
style={[
{
color: theme.onSurface,
},
styles.name,
textStyles,
]}
>
{plugin.name}
</Animated.Text>
<Animated.Text
numberOfLines={1}
<View>
{plugin.header ? (
<Text style={[styles.listHeader, { color: theme.onSurfaceVariant }]}>
{plugin.lang}
</Text>
) : null}
<Animated.View
style={[
styles.container,
{ backgroundColor: theme.surface },
viewStyles,
]}
>
<Animated.View style={{ flexDirection: 'row' }}>
<Animated.Image
source={{ uri: plugin.iconUrl }}
style={[
{ color: theme.onSurfaceVariant },
styles.addition,
textStyles,
styles.icon,
imageStyles,
{ backgroundColor: theme.surface },
]}
>
{`${plugin.lang} - ${plugin.version}`}
</Animated.Text>
/>
<Animated.View style={styles.details}>
<Animated.Text
numberOfLines={1}
style={[
{
color: theme.onSurface,
},
styles.name,
textStyles,
]}
>
{plugin.name}
</Animated.Text>
<Animated.Text
numberOfLines={1}
style={[
{ color: theme.onSurfaceVariant },
styles.addition,
textStyles,
]}
>
{`${plugin.lang} - ${plugin.version}`}
</Animated.Text>
</Animated.View>
</Animated.View>
<IconButtonV2
name="download-outline"
color={theme.primary}
onPress={() => {
ratio.value = withTiming(0, { duration: 500 });
installPlugin(plugin)
.then(() => {
showToast(
getString('browseScreen.installedPlugin', {
name: plugin.name,
}),
);
})
.catch((error: Error) => {
showToast(error.message);
ratio.value = 1;
});
}}
size={22}
theme={theme}
/>
</Animated.View>
<IconButtonV2
name="download-outline"
color={theme.primary}
onPress={() => {
ratio.value = withTiming(0, { duration: 500 });
installPlugin(plugin)
.then(() => {
showToast(
getString('browseScreen.installedPlugin', {
name: plugin.name,
}),
);
})
.catch((error: Error) => {
showToast(error.message);
ratio.value = 1;
});
}}
size={22}
theme={theme}
/>
</Animated.View>
</View>
);
};

export const AvailableTab = memo(({ searchText, theme }: AvailableTabProps) => {
const navigation = useNavigation<MoreStackScreenProps['navigation']>();

const [refreshing, setRefreshing] = useState(false);
const {
filteredAvailablePlugins,
languagesFilter,
refreshPlugins,
installPlugin,
} = usePlugins();
const { filteredAvailablePlugins, refreshPlugins, installPlugin } =
usePlugins();

const sections = useMemo(() => {
const list = [];
const lowerCaseSearchText = searchText.toLocaleLowerCase();
const group = groupBy(
searchText
? filteredAvailablePlugins.filter(
plg =>
plg.name.toLocaleLowerCase().includes(lowerCaseSearchText) ||
plg.id.toLocaleLowerCase().includes(lowerCaseSearchText),
)
: filteredAvailablePlugins,
'lang',
);
for (const language of languagesFilter) {
if (group[language]?.length) {
list.push({
header: language,
data: group[language],
});
}
const searchedPlugins = useMemo(() => {
let res = filteredAvailablePlugins;
if (searchText) {
const lowerCaseSearchText = searchText.toLocaleLowerCase();
res = filteredAvailablePlugins.filter(
plg =>
plg.name.toLocaleLowerCase().includes(lowerCaseSearchText) ||
plg.id.includes(lowerCaseSearchText),
);
}
return list;
let previousLang: string | null = null;
return res
.sort((a, b) => a.lang.localeCompare(b.lang))
.map(plg => {
if (plg.lang !== previousLang) {
previousLang = plg.lang;
return { ...plg, header: true };
} else {
return { ...plg, header: false };
}
});
}, [searchText, filteredAvailablePlugins]);

const renderItem: ListRenderItem<PluginItem> = useCallback(
({ item }) => {
return (
<AvailablePluginCard
plugin={item}
theme={theme}
installPlugin={installPlugin}
/>
);
},
[theme, sections],
);
const renderItem: FlashListRenderItem<PluginItem & { header: boolean }> =
useCallback(
({ item }) => {
return (
<AvailablePluginCard
plugin={item}
theme={theme}
installPlugin={installPlugin}
/>
);
},
[theme, searchedPlugins],
);

return (
<SectionList
sections={sections}
showsVerticalScrollIndicator={false}
<FlashList
estimatedItemSize={64}
data={searchedPlugins}
extraData={theme}
renderItem={renderItem}
removeClippedSubviews={true}
showsVerticalScrollIndicator={false}
keyExtractor={item => item.id + '_available'}
contentContainerStyle={{ flexGrow: 1 }}
ListEmptyComponent={
<EmptyView
icon="(・Д・。"
description=" No repositories yet. Add your first plugin repository to get
started."
actions={[
{
iconName: 'cog-outline',
title: 'Add Repository',
onPress: () =>
navigation.navigate('MoreStack', {
screen: 'SettingsStack',
params: {
screen: 'RespositorySettings',
},
}),
},
]}
theme={theme}
/>
}
refreshControl={
<RefreshControl
refreshing={refreshing}
Expand All @@ -419,13 +408,28 @@ export const AvailableTab = memo(({ searchText, theme }: AvailableTabProps) => {
progressBackgroundColor={theme.primary}
/>
}
renderItem={renderItem}
renderSectionHeader={({ section: { header, data } }) =>
data.length ? (
<Text style={[styles.listHeader, { color: theme.onSurfaceVariant }]}>
{header}
</Text>
) : null
ListEmptyComponent={
<View style={{ marginTop: 100 }}>
<EmptyView
icon="(・Д・。"
description=" No repositories yet. Add your first plugin repository to get
started."
actions={[
{
iconName: 'cog-outline',
title: 'Add Repository',
onPress: () =>
navigation.navigate('MoreStack', {
screen: 'SettingsStack',
params: {
screen: 'RespositorySettings',
},
}),
},
]}
theme={theme}
/>
</View>
}
/>
);
Expand Down Expand Up @@ -467,8 +471,4 @@ const styles = StyleSheet.create({
alignItems: 'center',
paddingHorizontal: 8,
},
spinner: {
marginRight: 12,
marginLeft: -1,
},
});

0 comments on commit 7b1eb7d

Please sign in to comment.