Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wallet][MS-336] Custom wallet for coins/nft #604

Merged
merged 31 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ea7b446
init custom wallet
ltminhthu Jul 22, 2024
59ead0b
construct a custom wallet
ltminhthu Jul 27, 2024
8f7c4d6
add assets for samo widget
ltminhthu Jul 29, 2024
5c6388b
revert explorer
ltminhthu Jul 29, 2024
a0e8f11
adjust custom wallet layout
ltminhthu Jul 29, 2024
08790d1
add advertisement for custom wallet
ltminhthu Jul 29, 2024
584e1ab
adjust some related functions
ltminhthu Jul 29, 2024
01e3045
add more ads and ads indicator
ltminhthu Jul 29, 2024
3191fa7
add ads indicator to the ads
ltminhthu Jul 29, 2024
db08ef9
filter out the custom wallet when users have no required tokens
ltminhthu Jul 29, 2024
e967e3f
fix some small details such as types, size, etc.
ltminhthu Jul 29, 2024
c9d2f38
hndle case wrapped sol is inserted
ltminhthu Jul 30, 2024
325fd43
refactor: filter out the custom wallet meet the requirement
ltminhthu Jul 30, 2024
8710579
fix: type check error
ltminhthu Jul 30, 2024
23c0c9d
fix: get wrong collection id
ltminhthu Jul 30, 2024
8289459
replace nft tab by collectible list
ltminhthu Aug 5, 2024
b43a2b2
remove widget type, group it to widget category
ltminhthu Aug 5, 2024
0ac6cb1
replace token tab by token list
ltminhthu Aug 5, 2024
ccb8b2a
abstract filter for widget
ltminhthu Aug 5, 2024
ebca821
change data to widget
ltminhthu Aug 5, 2024
150020b
fix: type check
ltminhthu Aug 5, 2024
6fc0f21
keep the old style of slider tabs and add linear gradient
ltminhthu Aug 5, 2024
6baa969
refactor: name the constant
ltminhthu Aug 5, 2024
0dee521
fix: change icon size to optional
ltminhthu Aug 5, 2024
5e90793
remove redundant log
ltminhthu Aug 5, 2024
696c0aa
update scroll animation for advertisement
ltminhthu Aug 6, 2024
6f03cf5
adjust the filter return from object to boolean
ltminhthu Aug 6, 2024
0bdcc98
move token list and collectible list to components
ltminhthu Aug 6, 2024
d04a81f
combine category and subcategory of widget
ltminhthu Aug 6, 2024
b8d3351
update type after changing the way to classify widgets
ltminhthu Aug 6, 2024
ea75b52
fix: eslint
ltminhthu Aug 6, 2024
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 apps/wallet/assets/img/widget/samo-ad-1.png
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 apps/wallet/assets/img/widget/samo-banner.png
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 apps/wallet/assets/img/widget/samo-cover.png
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 apps/wallet/assets/img/widget/samo-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
116 changes: 116 additions & 0 deletions apps/wallet/src/components/CollectibleList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type { FC } from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { Text, View } from '@walless/gui';
import type { NftDocument } from '@walless/store';
import CollectionCard from 'components/CollectionCard';
import type { WrappedCollection } from 'utils/hooks';
import { useLazyGridLayout } from 'utils/hooks';
import { navigate } from 'utils/navigation';

interface Props {
collections?: WrappedCollection[];
nfts?: NftDocument[];
}

export const CollectibleList: FC<Props> = ({ collections = [], nfts = [] }) => {
const { onGridContainerLayout, width } = useLazyGridLayout({
referenceWidth: 150,
gap: gridGap,
});

const handlePressCollection = (ele: WrappedCollection) => {
const collectionId = ele._id.split('/')[2];

navigate('Dashboard', {
screen: 'Explore',
params: {
screen: 'Collection',
params: {
screen: 'Default',
params: { id: collectionId },
},
},
});
};

const handlePressCollectible = (ele: NftDocument) => {
const collectibleId = ele._id.split('/')[2];

navigate('Dashboard', {
screen: 'Explore',
params: {
screen: 'Collection',
params: { screen: 'NFT', params: { id: collectibleId } },
},
});
};

return (
<ScrollView
style={styles.container}
showsVerticalScrollIndicator={false}
onLayout={(e) => onGridContainerLayout(e.nativeEvent.layout)}
>
{collections.length === 0 && nfts.length === 0 && (
<View horizontal style={styles.emptyContainer}>
<Text style={styles.emptyText}>You do not have any NFT yet</Text>
</View>
)}
<View style={styles.contentContainer}>
{width > 0 &&
collections.map((ele, index) => {
return (
<CollectionCard
key={index}
item={ele}
collectibleCount={ele.count}
onPress={() => handlePressCollection(ele)}
size={width}
/>
);
})}
</View>

<View style={styles.contentContainer}>
{width > 0 &&
nfts.map((ele, index) => {
return (
<CollectionCard
key={index}
item={ele}
onPress={() => handlePressCollectible(ele)}
size={width}
/>
);
})}
</View>
</ScrollView>
);
};

export default CollectibleList;

const gridGap = 18;
const styles = StyleSheet.create({
container: {
marginTop: 16,
marginBottom: 32,
borderRadius: 12,
overflow: 'hidden',
},
contentContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: gridGap,
overflow: 'hidden',
},
emptyContainer: {
flex: 1,
justifyContent: 'center',
},
emptyText: {
marginTop: 120,
fontSize: 13,
color: '#566674',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface Props<T extends Token> {
itemStyle?: StyleProp<ViewStyle>;
separateStyle?: StyleProp<ViewStyle>;
contentContainerStyle?: StyleProp<ViewStyle>;
items: TokenDocument<T>[];
tokens: TokenDocument<T>[];
ListHeaderComponent?: ComponentType<TokenDocument<T>> | ReactElement;
onPressItem?: (item: TokenDocument<T>) => void;
}
Expand All @@ -23,7 +23,7 @@ export const TokenList = <T extends Token>({
itemStyle,
separateStyle,
contentContainerStyle,
items,
tokens,
ListHeaderComponent,
onPressItem,
}: Props<T>) => {
Expand All @@ -39,7 +39,7 @@ export const TokenList = <T extends Token>({
style={[
itemStyle,
index === 0 && styles.firstItem,
index === items.length - 1 && styles.lastItem,
index === tokens.length - 1 && styles.lastItem,
]}
onPress={handlePressItem}
/>
Expand All @@ -52,7 +52,7 @@ export const TokenList = <T extends Token>({
showsVerticalScrollIndicator={false}
style={style}
contentContainerStyle={contentContainerStyle}
data={items}
data={tokens}
renderItem={renderItem}
keyExtractor={(item) => item._id}
ItemSeparatorComponent={() => <Separator style={separateStyle} />}
Expand Down
62 changes: 62 additions & 0 deletions apps/wallet/src/components/WidgetButtons/ButtonItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { FC } from 'react';
import type { TextStyle, ViewStyle } from 'react-native';
import { StyleSheet } from 'react-native';
import { Hoverable, Text, View } from '@walless/gui';
import type { IconProps } from '@walless/icons';

export interface WidgetButtonProps {
style?: ViewStyle;
title?: string;
titleStyle?: TextStyle;
Icon: FC<IconProps>;
iconColor?: string;
iconSize?: number;
onPress?: () => void;
}

export const ButtonItem: FC<WidgetButtonProps> = ({
Icon,
iconColor,
iconSize,
onPress,
style,
title,
titleStyle,
}) => {
const innerStyle: ViewStyle = {
width: 38,
height: 38,
borderRadius: 12,
gap: 8,
backgroundColor: onPress ? '#0694D3' : '#43525F',
alignItems: 'center',
justifyContent: 'center',
};

return (
<View noSelect style={styles.container}>
<Hoverable
style={[innerStyle, style]}
onPress={onPress}
disabled={!onPress}
>
{<Icon color={iconColor} size={iconSize || 24} />}
</Hoverable>
{title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
</View>
);
};

const styles = StyleSheet.create({
container: {
alignItems: 'center',
},
innerContainer: {
borderRadius: 12,
},
title: {
color: '#4e5e6b',
fontSize: 13,
marginTop: 8,
},
});
40 changes: 40 additions & 0 deletions apps/wallet/src/components/WidgetButtons/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { FC } from 'react';
import type { ViewStyle } from 'react-native';
import { StyleSheet } from 'react-native';
import { View } from '@walless/gui';

import type { WidgetButtonProps } from './ButtonItem';
import { ButtonItem } from './ButtonItem';

interface Props {
style?: ViewStyle;
buttons: WidgetButtonProps[];
}

const WidgetButtons: FC<Props> = ({ style, buttons }) => {
return (
<View style={[styles.container, style]}>
{buttons.map((item, idx) => (
<ButtonItem
key={idx}
Icon={item.Icon}
iconColor={item.iconColor}
iconSize={item.iconSize}
onPress={item.onPress}
style={item.style}
title={item.title}
titleStyle={item.titleStyle}
/>
))}
</View>
);
};

export default WidgetButtons;

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
gap: 18,
},
});
13 changes: 9 additions & 4 deletions apps/wallet/src/features/Explorer/Highlights/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { FC } from 'react';
import { useState } from 'react';
import { StyleSheet } from 'react-native';
import { Text, View } from '@walless/gui';
import { mockWidgets } from 'state/widget';
import type { WidgetDocument } from '@walless/store';

import CardCarousel from './CardCarousel';
import HighlightIndicator from './HighlightIndicator';

const Highlights = () => {
interface Props {
widgets: WidgetDocument[];
}

const Highlights: FC<Props> = ({ widgets }) => {
const [currentIndex, setCurrentIndex] = useState(0);

return (
Expand All @@ -18,14 +23,14 @@ const Highlights = () => {

<View style={styles.highlightList}>
<CardCarousel
widgets={mockWidgets}
widgets={widgets}
currentIndex={currentIndex}
onChangeCurrentIndex={setCurrentIndex}
/>

<HighlightIndicator
currentIndex={currentIndex}
dataLength={mockWidgets.length}
dataLength={widgets.length}
/>
</View>
</View>
Expand Down
6 changes: 3 additions & 3 deletions apps/wallet/src/features/Explorer/Widgets/CategoryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import Animated, {
interpolateColor,
useAnimatedStyle,
} from 'react-native-reanimated';
import type { WidgetType } from '@walless/core';
import type { WidgetCategories } from '@walless/core';

const AnimatedHoverable = Animated.createAnimatedComponent(TouchableOpacity);

interface CategoryButtonProps {
index: number;
title: WidgetType;
onPress: (index: number, category: WidgetType) => void;
title: WidgetCategories;
onPress: (index: number, category: WidgetCategories) => void;
animatedValue: SharedValue<number>;
data: number[];
}
Expand Down
18 changes: 11 additions & 7 deletions apps/wallet/src/features/Explorer/Widgets/CategoryButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import type { FC } from 'react';
import { Animated, StyleSheet } from 'react-native';
import { useSharedValue, withTiming } from 'react-native-reanimated';
import { WidgetType } from '@walless/core';
import { SubcategoryToCategoryMapping, WidgetCategories } from '@walless/core';
import type { WidgetDocument } from '@walless/store';
import { mockWidgets } from 'state/widget';

import CategoryButton from './CategoryButton';

interface CategoryButtonsProps {
widgets: WidgetDocument[];
setWidgets: (widgets: WidgetDocument[]) => void;
}

const CategoryButtons: FC<CategoryButtonsProps> = ({ setWidgets }) => {
const CategoryButtons: FC<CategoryButtonsProps> = ({ widgets, setWidgets }) => {
const currentIndex = useSharedValue(0);
const animatedValue = useSharedValue(0);
const categories = Object.values(WidgetType);
const categories = Object.values(WidgetCategories);

const inputRange = categories.map((_, index) => index);

const handleCategoryPress = (activeIndex: number, category: WidgetType) => {
const handleCategoryPress = (
activeIndex: number,
category: WidgetCategories,
) => {
currentIndex.value = activeIndex;
animatedValue.value = withTiming(activeIndex);
const filteredLayoutCards = mockWidgets.filter(
(item) => item.widgetType === category,

const filteredLayoutCards = widgets.filter(
(widget) => SubcategoryToCategoryMapping[widget.category] === category,
);
setWidgets(filteredLayoutCards);
};
Expand Down
Loading
Loading