Skip to content

Commit

Permalink
DMAPP- Chain filter integration
Browse files Browse the repository at this point in the history
- Integrated dropdown UI for chain filter in the channel screen
- Updated channel list API hook to handle chain filter
- Fixed interaction issue in dropdown option list
- Added scroll-to-start behavior in channel category component when selecting another filter, resetting the category to default
  • Loading branch information
meKushdeepSingh committed Feb 21, 2025
1 parent c32710b commit 4116b10
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 45 deletions.
Binary file added assets/ui/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ui/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ui/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 30 additions & 12 deletions src/components/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, {FC, useRef, useState} from 'react';
import React, {FC, useEffect, useRef, useState} from 'react';
import {
FlatList,
Image,
Pressable,
StyleSheet,
Text,
TouchableOpacity,
Expand All @@ -12,12 +13,21 @@ import Globals from 'src/Globals';

import {DropdownProps} from './Dropdown.types';

const Dropdown: FC<DropdownProps> = ({style, data}) => {
const Dropdown: FC<DropdownProps> = ({style, data, onChange, value}) => {
// State
const [isVisible, setIsVisible] = useState(false);
const [selectedOption, setSelectedOption] = useState(data[0]);
const [dropdownTop, setDropdownTop] = useState(0);
const buttonRef = useRef<TouchableOpacity>(null);

// Set default value
useEffect(() => {
if (value.length == 0) {
setSelectedOption(data[0]);
}
}, [value]);

// Open dropdown
const openDropdown = () => {
if (buttonRef.current) {
buttonRef.current?.measure(
Expand All @@ -36,14 +46,17 @@ const Dropdown: FC<DropdownProps> = ({style, data}) => {
setTimeout(() => setIsVisible(true), 100);
};

// Handle option select
const handleSelect = (option: any) => {
setSelectedOption(option);
setTimeout(() => setIsVisible(false), 100);
onChange(option);
};
return (
<>
{/* Dropdown Field */}
<TouchableOpacity
activeOpacity={0.8}
onPress={openDropdown}
ref={buttonRef}
style={[styles.mainView, style]}>
Expand All @@ -62,15 +75,14 @@ const Dropdown: FC<DropdownProps> = ({style, data}) => {
{/* Dropdown Options */}
<Modal
backdropOpacity={0}
style={{margin: 0, paddingHorizontal: 16}}
style={styles.modalStyles}
isVisible={isVisible}
animationIn="fadeInDownBig"
animationOut="fadeOutUpBig">
<TouchableOpacity
activeOpacity={1}
animationIn="fadeIn"
animationOut="fadeOut">
<Pressable
style={styles.overlay}
onPress={() => setTimeout(() => setIsVisible(false), 100)}>
<View style={[styles.dropdownContainer, {top: dropdownTop}]}>
<Pressable style={[styles.dropdownContainer, {top: dropdownTop}]}>
<FlatList
data={data}
keyExtractor={item => item.value}
Expand All @@ -89,8 +101,8 @@ const Dropdown: FC<DropdownProps> = ({style, data}) => {
)}
ItemSeparatorComponent={() => <View style={styles.optionGap} />}
/>
</View>
</TouchableOpacity>
</Pressable>
</Pressable>
</Modal>
</>
);
Expand All @@ -108,6 +120,7 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
padding: 12,
borderRadius: 12,
height: 48,
},
caretIcon: {
height: 24,
Expand All @@ -116,14 +129,18 @@ const styles = StyleSheet.create({
},
overlay: {
flex: 1,
position: 'relative',
zIndex: 1,
paddingHorizontal: 16,
},
dropdownContainer: {
backgroundColor: Globals.COLORS.WHITE,
borderWidth: 1,
borderColor: Globals.COLORS.BORDER_DROPDOWN,
padding: 8,
borderRadius: 12,
maxHeight: 350,
maxHeight: 380,
zIndex: 2,
},
option: {
flexDirection: 'row',
Expand All @@ -135,7 +152,7 @@ const styles = StyleSheet.create({
backgroundColor: Globals.COLORS.PILL_BG_DEFAULT,
},
optionGap: {
height: 12,
marginTop: 12,
},
optionIcon: {
height: 24,
Expand All @@ -145,4 +162,5 @@ const styles = StyleSheet.create({
optionText: {
marginLeft: 4,
},
modalStyles: {margin: 0},
});
14 changes: 9 additions & 5 deletions src/components/dropdown/Dropdown.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import {chainNameType} from 'src/helpers/ChainHelper';

export type DropdownProps = {
style?: ViewStyle;
data: {
value: string;
label: string;
icon: ImageSourcePropType | null;
}[];
data: DropdownOption[];
onChange: (data: DropdownOption) => void;
value: string;
};

export type DropdownOption = {
value: string;
label: string;
icon: ImageSourcePropType | null;
};
21 changes: 19 additions & 2 deletions src/components/ui/ChannelCategories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {FC, useState} from 'react';
import React, {FC, useEffect, useRef} from 'react';
import {ScrollView, StyleSheet, View} from 'react-native';
import Globals from 'src/Globals';
import {useChannelCategories} from 'src/hooks/channel/useChannelCategories';

import {Pill} from '../pill';
Expand All @@ -16,12 +15,30 @@ const ChannelCategories: FC<ChannelCategoriesProps> = ({
value,
disabled,
}) => {
// Ref
const scrollViewRef = useRef<ScrollView>(null);

// Hooks
const {isLoading, channelCategories} = useChannelCategories();

// Scroll to start
useEffect(() => {
if (!isLoading && channelCategories?.length > 0) {
const shouldScrollToStart = channelCategories[0].value === value;
if (shouldScrollToStart && scrollViewRef.current) {
scrollViewRef.current.scrollTo({
x: 0,
animated: true,
});
}
}
}, [value]);

if (!isLoading && channelCategories?.length > 0) {
return (
<View style={styles.mainView}>
<ScrollView
ref={scrollViewRef}
contentContainerStyle={styles.scrollViewStyle}
showsHorizontalScrollIndicator={false}
horizontal>
Expand Down
43 changes: 23 additions & 20 deletions src/components/ui/ChannelsDisplayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,30 @@ import {

import GLOBALS from '../../Globals';
import Globals from '../../Globals';
import {Dropdown} from '../dropdown';
import {Dropdown, DropdownOption} from '../dropdown';
import {ChannelCategories} from './ChannelCategories';

const ChannelsDisplayer = () => {
// Get allowed chains
const chainList = ChainHelper.getSelectChains(ENV_CONFIG.ALLOWED_NETWORKS);

// State
const [search, setSearch] = React.useState('');
const [selectedCategory, setSelectedCategory] = useState<string>(
Globals.CONSTANTS.ALL_CATEGORIES,
);
const [selectedChain, setSelectedChain] = useState<string>('');

// Redux
const channelResults = useSelector(selectChannels);
const isLoadingSubscriptions = useSelector(selectIsLoadingSubscriptions);

// Hooks
const {isLoading, isLoadingMore, resetChannelData, loadMore} = useChannels({
tag: selectedCategory,
searchQuery: search,
filter: selectedChain,
});

const isLoadingSubscriptions = useSelector(selectIsLoadingSubscriptions);
const {refreshSubscriptions} = useSubscriptions();
const {userPushSDKInstance} = usePushApi();
const {openSheet} = useSheets();
Expand All @@ -68,19 +76,13 @@ const ChannelsDisplayer = () => {
}
resetChannelData();
setSelectedCategory(category as string);
setSelectedChain('');
};

const getSelectChains = (chainIdList: Array<number>) => {
return chainIdList?.map((key: number) => {
return {
value: key.toString(),
label:
ChainHelper.networkName?.[
key as keyof typeof ChainHelper.networkName
] ?? '',
icon: ChainHelper.chainIdToLogo(key),
};
});
const handleChainChange = (option: DropdownOption) => {
resetChannelData();
setSelectedCategory(Globals.CONSTANTS.ALL_CATEGORIES);
setSelectedChain(chainList[0].value !== option.value ? option.value : '');
};

return (
Expand All @@ -106,8 +108,10 @@ const ChannelsDisplayer = () => {

{/* Render Dropdown Field */}
<Dropdown
onChange={handleChainChange}
style={styles.dropdownField}
data={getSelectChains(ENV_CONFIG.ALLOWED_NETWORKS)}
data={chainList}
value={selectedChain}
/>
</View>

Expand All @@ -119,7 +123,7 @@ const ChannelsDisplayer = () => {
/>

{/* Render No Data View */}
{channelResults.length === 0 && (
{channelResults?.length === 0 && (
<View style={[styles.infodisplay]}>
{!isLoading && !isLoadingSubscriptions ? (
// Show channel not found label
Expand Down Expand Up @@ -147,7 +151,7 @@ const ChannelsDisplayer = () => {
)}

{/* Render Channel List */}
{channelResults.length !== 0 && !isLoadingSubscriptions && (
{channelResults?.length !== 0 && !isLoadingSubscriptions && (
<FlatList
data={channelResults}
style={styles.channels}
Expand Down Expand Up @@ -223,7 +227,7 @@ const styles = StyleSheet.create({
backgroundColor: GLOBALS.COLORS.BG_SEARCH_BAR,
height: 48,
paddingHorizontal: 12,
width: '70%',
width: '73%',
},
searchBar: {
fontSize: 14,
Expand All @@ -244,8 +248,7 @@ const styles = StyleSheet.create({
},
footerLoadingView: {paddingVertical: 10},
dropdownField: {
width: '25%',
height: 48,
width: '23%',
},
});

Expand Down
33 changes: 31 additions & 2 deletions src/helpers/ChainHelper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {ImageSourcePropType} from 'react-native';
import {DropdownOption} from 'src/components/dropdown';

export type chainNameType =
| 'ETH_TEST_SEPOLIA'
Expand All @@ -19,6 +20,10 @@ export type chainNameType =
| 'FUSE_TESTNET'
| 'CYBER_TESTNET'
| 'CYBER_MAINNET'
| 'BASE_MAINNET'
| 'BASE_SEPOLIA'
| 'LINEA_MAINNET'
| 'LINEA_SEPOLIA'
| 'FUSE_TESTNET'
| undefined;

Expand Down Expand Up @@ -61,6 +66,14 @@ export const ChainHelper = {
return 'CYBER_MAINNET';
case 111557560:
return 'CYBER_TESTNET';
case 8453:
return 'BASE_MAINNET';
case 84532:
return 'BASE_SEPOLIA';
case 59144:
return 'LINEA_MAINNET';
case 59141:
return 'LINEA_SEPOLIA';
default:
return undefined;
}
Expand Down Expand Up @@ -94,9 +107,12 @@ export const ChainHelper = {
case 'CYBER_MAINNET':
case 'CYBER_TESTNET':
return require('assets/ui/cyber.png');
case 'CYBER_MAINNET':
case 'CYBER_TESTNET':
case 'BASE_MAINNET':
case 'BASE_SEPOLIA':
return require('assets/ui/base.png');
case 'LINEA_MAINNET':
case 'LINEA_SEPOLIA':
return require('assets/ui/linea.png');

default:
return null;
Expand Down Expand Up @@ -135,4 +151,17 @@ export const ChainHelper = {
59141: 'Linea Sepolia',
59144: 'Linea Mainnet',
},

getSelectChains: (chainIdList: Array<number>): DropdownOption[] => {
return chainIdList?.map((key: number) => {
return {
value: key.toString(),
label:
ChainHelper.networkName?.[
key as keyof typeof ChainHelper.networkName
] ?? '',
icon: ChainHelper.chainIdToLogo(key),
};
});
},
};
Loading

0 comments on commit 4116b10

Please sign in to comment.