Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
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.
Binary file removed assets/ui/ethereum.png
Binary file not shown.
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.
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.
Binary file removed assets/ui/optimism.png
Binary file not shown.
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.
Binary file removed assets/ui/polygon.png
Binary file not shown.
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.
Binary file removed assets/ui/polygonZkEVM.png
Binary file not shown.
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.
1 change: 1 addition & 0 deletions src/Globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export default {
// Border
BORDER_DEFAULT: '#C4CBD5',
BORDER_SEPARATOR: '#E5E5E5',
BORDER_DROPDOWN: '#EAEBF2',
},
SCREENS: {
WELCOME: 'Welcome',
Expand Down
169 changes: 169 additions & 0 deletions src/components/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import React, {FC, useEffect, useRef, useState} from 'react';
import {
FlatList,
Image,
Platform,
Pressable,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import Modal from 'react-native-modal';
import Globals from 'src/Globals';

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

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(
(
_fx: number,
_fy: number,
_width: number,
height: number,
_px: number,
py: number,
) => {
const topOffset =
Platform.OS === 'ios' ? py + height + 15 : py + height - 10;
setDropdownTop(topOffset);
},
);
}
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]}>
{selectedOption?.icon && (
<Image source={selectedOption.icon} style={styles.optionIcon} />
)}
<Image
style={[
styles.caretIcon,
{transform: [{rotate: isVisible ? '180deg' : '0deg'}]},
]}
source={require('../../../assets/ui/icCaretDown.png')}
/>
</TouchableOpacity>

{/* Dropdown Options */}
<Modal
backdropOpacity={0}
style={styles.modalStyles}
isVisible={isVisible}
animationIn="fadeIn"
animationOut="fadeOut">
<Pressable
style={styles.overlay}
onPress={() => setTimeout(() => setIsVisible(false), 100)}>
<Pressable style={[styles.dropdownContainer, {top: dropdownTop}]}>
<FlatList
data={data}
keyExtractor={item => item.value}
renderItem={({item}) => (
<TouchableOpacity
style={[
styles.option,
item.value === selectedOption.value && styles.activeOption,
]}
onPress={() => handleSelect(item)}>
{item.icon && (
<Image source={item.icon} style={styles.optionIcon} />
)}
<Text style={styles.optionText}>{item.label}</Text>
</TouchableOpacity>
)}
ItemSeparatorComponent={() => <View style={styles.optionGap} />}
/>
</Pressable>
</Pressable>
</Modal>
</>
);
};

export {Dropdown};

const styles = StyleSheet.create({
mainView: {
backgroundColor: Globals.COLORS.WHITE,
borderWidth: 1.5,
borderColor: Globals.COLORS.BORDER_DROPDOWN,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 12,
borderRadius: 12,
height: 48,
},
caretIcon: {
height: 24,
width: 24,
resizeMode: 'contain',
},
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: 380,
zIndex: 2,
},
option: {
flexDirection: 'row',
alignItems: 'center',
padding: 4,
borderRadius: 8,
},
activeOption: {
backgroundColor: Globals.COLORS.PILL_BG_DEFAULT,
},
optionGap: {
marginTop: 12,
},
optionIcon: {
height: 24,
width: 24,
resizeMode: 'contain',
},
optionText: {
marginLeft: 4,
},
modalStyles: {margin: 0},
});
15 changes: 15 additions & 0 deletions src/components/dropdown/Dropdown.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {ImageSourcePropType, ViewStyle} from 'react-native';
import {chainNameType} from 'src/helpers/ChainHelper';

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

export type DropdownOption = {
value: string;
label: string;
icon: ImageSourcePropType | null;
};
2 changes: 2 additions & 0 deletions src/components/dropdown/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Dropdown';
export * from './Dropdown.types';
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
48 changes: 41 additions & 7 deletions src/components/ui/ChannelsDisplayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import EPNSActivity from 'src/components/loaders/EPNSActivity';
import ChannelItem from 'src/components/ui/ChannelItem';
import {usePushApi} from 'src/contexts/PushApiContext';
import {useSheets} from 'src/contexts/SheetContext';
import ENV_CONFIG from 'src/env.config';
import {ChainHelper} from 'src/helpers/ChainHelper';
import useChannels from 'src/hooks/channel/useChannels';
import useSubscriptions from 'src/hooks/channel/useSubscriptions';
import {
Expand All @@ -24,21 +26,30 @@ import {

import GLOBALS from '../../Globals';
import Globals from '../../Globals';
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 @@ -65,12 +76,20 @@ const ChannelsDisplayer = () => {
}
resetChannelData();
setSelectedCategory(category as string);
setSelectedChain('');
};

const handleChainChange = (option: DropdownOption) => {
resetChannelData();
setSelectedCategory(Globals.CONSTANTS.ALL_CATEGORIES);
setSelectedChain(chainList[0].value !== option.value ? option.value : '');
};

return (
<View style={styles.container}>
{/* Render Search Bar */}
<View style={styles.searchBarWrapper}>
{/* Render Search Bar */}
<View style={styles.searchView}>
<Image
source={require('assets/ui/search.png')}
Expand All @@ -82,10 +101,18 @@ const ChannelsDisplayer = () => {
handleChannelSearch(e);
}}
value={search}
placeholder={'Search for channel name or address'}
placeholder={'Search for channel name...'}
placeholderTextColor="#7D7F89"
/>
</View>

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

{/* Render Channel Categories(tags) */}
Expand All @@ -96,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 @@ -124,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 @@ -189,14 +216,18 @@ const styles = StyleSheet.create({
searchBarWrapper: {
paddingHorizontal: 16,
marginBottom: 24,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
searchView: {
flexDirection: 'row',
alignItems: 'center',
borderRadius: 16,
backgroundColor: GLOBALS.COLORS.BG_SEARCH_BAR,
height: 42,
paddingHorizontal: 8,
height: 48,
paddingHorizontal: 12,
width: '73%',
},
searchBar: {
fontSize: 14,
Expand All @@ -216,6 +247,9 @@ const styles = StyleSheet.create({
marginVertical: 24,
},
footerLoadingView: {paddingVertical: 10},
dropdownField: {
width: '23%',
},
});

export default ChannelsDisplayer;
Loading
Loading