Skip to content

Commit

Permalink
[Issue-1068]: Add banner crowdloan unlock in Crowdloan tab
Browse files Browse the repository at this point in the history
  • Loading branch information
dominhquang committed Oct 19, 2023
1 parent 5d3867d commit 86c0051
Show file tree
Hide file tree
Showing 23 changed files with 337 additions and 44 deletions.
Binary file added src/assets/crowdloan-advertising-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 src/assets/crowdloan-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ export const Images = {
avatarPlaceholder: require('./avatar-placeholder.png'),
browserBanner: require('./browser-banner.png'),
circleRobot: require('./circle-robot.png'),
crowdloanBanner: require('./crowdloan-banner.png'),
crowdloanAdvertisingBanner: require('./crowdloan-advertising-banner.png'),
};
9 changes: 8 additions & 1 deletion src/components/design-system-ui/modal/ModalBaseV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface SWModalProps {
level?: number;
onChangeModalVisible?: () => void;
isUseForceHidden?: boolean;
disabledOnPressBackDrop?: boolean;
}

export type SWModalRefProps = {
Expand All @@ -43,6 +44,7 @@ const ModalBaseV2 = React.forwardRef<SWModalRefProps, SWModalProps>(
onChangeModalVisible,
isUseForceHidden,
isAllowSwipeDown = true,
disabledOnPressBackDrop = false,
},
ref,
) => {
Expand Down Expand Up @@ -146,7 +148,12 @@ const ModalBaseV2 = React.forwardRef<SWModalRefProps, SWModalProps>(
<>
{!isForcedHidden && isVisible && (
<>
<TouchableOpacity activeOpacity={0.8} style={_styles.backDropButton} onPress={onClose} />
<TouchableOpacity
activeOpacity={0.8}
style={_styles.backDropButton}
onPress={onClose}
disabled={disabledOnPressBackDrop}
/>
<GestureDetector gesture={gesture}>
{
// @ts-ignore
Expand Down
3 changes: 3 additions & 0 deletions src/components/design-system-ui/modal/SwModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface SWModalProps {
modalBaseV2Ref?: React.RefObject<SWModalRefProps>;
level?: number;
isUseSafeAreaView?: boolean;
disabledOnPressBackDrop?: boolean;
renderHeader?: React.ReactNode;
}

Expand Down Expand Up @@ -83,6 +84,7 @@ const SwModal = React.forwardRef<ModalRefProps, SWModalProps>(
level,
isUseSafeAreaView = true,
renderHeader,
disabledOnPressBackDrop,
},
ref,
) => {
Expand Down Expand Up @@ -136,6 +138,7 @@ const SwModal = React.forwardRef<ModalRefProps, SWModalProps>(
setVisible={setVisible}
height={childrenHeight}
ref={modalBaseV2Ref}
disabledOnPressBackDrop={disabledOnPressBackDrop}
isUseForceHidden={Platform.OS === 'android'}
onChangeModalVisible={onChangeModalVisible}
isAllowSwipeDown={isAllowSwipeDown}
Expand Down
14 changes: 14 additions & 0 deletions src/hooks/campaign/useGetBannerByScreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useMemo } from 'react';

import { RootState } from 'stores/index';
import { useSelector } from 'react-redux';

const useGetBannerByScreen = (screen: string) => {
const { banners } = useSelector((state: RootState) => state.campaign);

return useMemo(() => {
return banners.filter(item => item.data.position.includes(screen));
}, [banners, screen]);
};

export default useGetBannerByScreen;
39 changes: 3 additions & 36 deletions src/hooks/screen/Home/Crypto/useBuyToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import { AccountType } from 'types/ui-types';
import { TokenItemType } from 'components/Modal/common/TokenSelector';
import { ModalRef } from 'types/modalRef';
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
import { ColorMap } from 'styles/color';
import { Linking, Platform } from 'react-native';
import { ServiceItem, baseServiceItems } from 'screens/Home/Crypto/ServiceModal';
import { BuyServiceInfo, CreateBuyOrderFunction, SupportService } from 'types/buy';
import { createBanxaOrder, createCoinbaseOrder, createTransakOrder } from 'utils/buy';
import { BrowserOptions, createBanxaOrder, createCoinbaseOrder, createTransakOrder } from 'utils/buy';
import { isEthereumAddress } from '@polkadot/util-crypto';
import { isAccountAll } from 'utils/accountAll';
import useAppLock from 'hooks/useAppLock';
Expand Down Expand Up @@ -201,7 +200,7 @@ export default function useBuyToken(currentSymbol?: string) {
}, []);

const onBuyToken = useCallback(
async (currentUrl?: string, animated = true) => {
async (currentUrl?: string) => {
if (!selectedBuyAccount || !selectedBuyToken || !selectedService) {
console.warn(
'no: selectedBuyAccount || selectedBuyToken || selectedService',
Expand Down Expand Up @@ -243,39 +242,7 @@ export default function useBuyToken(currentSymbol?: string) {
if (await InAppBrowser.isAvailable()) {
// A delay to change the StatusBar when the browser is opened
isOpenInAppBrowser.current = true;
await InAppBrowser.open(currentUrl || url, {
// iOS Properties
dismissButtonStyle: 'done',
preferredBarTintColor: ColorMap.dark1,
preferredControlTintColor: ColorMap.light,
animated,
modalEnabled: true,
enableBarCollapsing: false,
// Android Properties
showTitle: true,
toolbarColor: ColorMap.dark1,
secondaryToolbarColor: ColorMap.dark1,
navigationBarColor: ColorMap.dark1,
navigationBarDividerColor: 'white',
enableUrlBarHiding: true,
enableDefaultShare: true,
forceCloseOnRedirection: false,
// Specify full animation resource identifier(package:anim/name)
// or only resource name(in case of animation bundled with app).
animations: {
startEnter: 'slide_in_right',
startExit: 'slide_out_left',
endEnter: 'slide_in_left',
endExit: 'slide_out_right',
},
headers: {
'my-custom-header': 'my custom header value',
},
hasBackButton: true,
browserPackage: undefined,
showInRecents: true,
includeReferrer: true,
});
await InAppBrowser.open(currentUrl || url, BrowserOptions);

isOpenInAppBrowser.current = false;
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/messaging/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import {
RequestAuthorizationBlock,
RequestAuthorizationPerSite,
RequestBondingSubmit,
RequestCampaignBannerComplete,
RequestChangeMasterPassword,
RequestConnectWalletConnect,
RequestCronAndSubscriptionAction,
Expand Down Expand Up @@ -1447,3 +1448,7 @@ export async function getMetadata(genesisHash?: string | null, isPartial = false

return null;
}

export async function completeBannerCampaign(request: RequestCampaignBannerComplete): Promise<boolean> {
return sendMessage('pri(campaign.banner.complete)', request);
}
7 changes: 7 additions & 0 deletions src/providers/DataContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
subscribeXcmRefMap,
subscribeConnectWCRequests,
subscribeWalletConnectSessions,
subscribeProcessingCampaign,
} from 'stores/utils';
import React, { useContext, useEffect, useRef } from 'react';
import { Provider } from 'react-redux';
Expand Down Expand Up @@ -304,6 +305,12 @@ export const DataContextProvider = ({ children }: DataContextProviderProps) => {
});

// Features
_DataContext.addHandler({
...subscribeProcessingCampaign,
name: 'subscribeProcessingCampaign',
relatedStores: ['campaign'],
isStartImmediately: true,
});
_DataContext.addHandler({
...subscribePrice,
name: 'subscribePrice',
Expand Down
103 changes: 103 additions & 0 deletions src/screens/Home/Crowdloans/CampaignBannerModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useCallback, useRef } from 'react';
import { Button, Icon, SwModal } from 'components/design-system-ui';
import { useSubWalletTheme } from 'hooks/useSubWalletTheme';
import ModalStyle from './style';
import { Linking, View } from 'react-native';
import { ArrowCircleRight, XCircle } from 'phosphor-react-native';
import FastImage from 'react-native-fast-image';
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
import { BrowserOptions } from 'utils/buy';
import { CampaignBanner, CampaignButton } from '@subwallet/extension-base/background/KoniTypes';
import { completeBannerCampaign } from 'messaging/index';

interface Props {
visible: boolean;
setVisible: (value: boolean) => void;
banner: CampaignBanner;
}

export type ButtonSchema = 'primary' | 'secondary' | 'warning' | 'danger' | 'ghost';

enum ButtonIcon {
// @ts-ignore
xCircle = XCircle,
// @ts-ignore
arrowCircleRight = ArrowCircleRight,
}

const CampaignBannerModal = ({ visible, banner, setVisible }: Props) => {
const theme = useSubWalletTheme().swThemes;
const _style = ModalStyle(theme);
const isOpenInAppBrowser = useRef(false);

const onPressJoinNow = async (url?: string) => {
if (url) {
if (await InAppBrowser.isAvailable()) {
isOpenInAppBrowser.current = true;
await InAppBrowser.open(url, BrowserOptions);

isOpenInAppBrowser.current = false;
} else {
Linking.openURL(url);
}
}
};

const onCloseBanner = useCallback(() => {
setVisible(false);
completeBannerCampaign({
slug: banner.slug,
}).catch(console.error);
}, [banner.slug, setVisible]);

const onPressBtn = (item: CampaignButton) => {
return () => {
if (item.type === 'open_url') {
const url = item.metadata?.url as string | undefined;

if (url) {
onPressJoinNow(url);
}
}

if (item.metadata?.doneOnClick) {
onCloseBanner();
}
};
};

return (
<SwModal
isUseModalV2={true}
setVisible={setVisible}
modalVisible={visible}
disabledOnPressBackDrop
isAllowSwipeDown={false}>
<View style={{ width: '100%' }}>
<FastImage
style={{
height: 144,
borderRadius: theme.borderRadiusLG,
marginBottom: theme.margin,
}}
resizeMode="cover"
source={{ uri: banner.data.media }}
/>

<View style={_style.footerAreaStyle}>
{banner.buttons.map(item => (
<Button
type={item.color as ButtonSchema}
style={{ flex: 1 }}
onPress={onPressBtn(item)}
icon={<Icon phosphorIcon={item.icon ? ButtonIcon[item.icon] : undefined} size={'lg'} weight={'fill'} />}>
{item.name}
</Button>
))}
</View>
</View>
</SwModal>
);
};

export default CampaignBannerModal;
24 changes: 24 additions & 0 deletions src/screens/Home/Crowdloans/CampaignBannerModal/style/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { StyleSheet, TextStyle, ViewStyle } from 'react-native';
import { ThemeTypes } from 'styles/themes';
import { FontMedium } from 'styles/sharedStyles';

export interface CreateMasterPasswordStyle {
textStyle: TextStyle;
footerAreaStyle: ViewStyle;
}

export default (theme: ThemeTypes) =>
StyleSheet.create<CreateMasterPasswordStyle>({
textStyle: {
fontSize: theme.fontSize,
lineHeight: theme.fontSize * theme.lineHeight,
color: theme.colorTextLight4,
textAlign: 'center',
...FontMedium,
},

footerAreaStyle: {
flexDirection: 'row',
gap: 16,
},
});
57 changes: 54 additions & 3 deletions src/screens/Home/Crowdloans/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import i18n from 'utils/i18n/i18n';

import { RocketLaunch } from 'phosphor-react-native';
Expand All @@ -11,8 +11,15 @@ import { useIsFocused } from '@react-navigation/native';
import { CrowdloanItemType } from 'types/index';
import { useSelector } from 'react-redux';
import { RootState } from 'stores/index';
import { ListRenderItemInfo } from 'react-native';
import { Linking, ListRenderItemInfo, TouchableOpacity } from 'react-native';
import { CrowdloanItem } from 'screens/Home/Crowdloans/CrowdloanItem';
import FastImage from 'react-native-fast-image';
import { Images } from 'assets/index';
import { BUTTON_ACTIVE_OPACITY } from 'constants/index';
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
import { BrowserOptions } from 'utils/buy';
import useGetBannerByScreen from 'hooks/campaign/useGetBannerByScreen';
import { CampaignBanner } from '@subwallet/extension-base/background/KoniTypes';

const renderListEmptyComponent = () => {
return (
Expand Down Expand Up @@ -43,11 +50,35 @@ export const CrowdloansScreen = () => {
{ label: i18n.filterOptions.win, value: FilterValue.WINNER },
{ label: i18n.filterOptions.fail, value: FilterValue.FAIL },
];

const banners = useGetBannerByScreen('crowdloan');
const isOpenInAppBrowser = useRef(false);
const renderItem = ({ item }: ListRenderItemInfo<CrowdloanItemType>) => {
return <CrowdloanItem item={item} isShowBalance={isShowBalance} />;
};

const openBanner = async (url: string) => {
if (await InAppBrowser.isAvailable()) {
isOpenInAppBrowser.current = true;
await InAppBrowser.open(url, BrowserOptions);

isOpenInAppBrowser.current = false;
} else {
Linking.openURL(url);
}
};

const onPressBanner = useCallback((item: CampaignBanner) => {
return () => {
if (item.data.action === 'open_url') {
const url = item.data.metadata?.url as string | undefined;

if (url) {
openBanner(url);
}
}
};
}, []);

const crowdloanData = useMemo(() => {
const result = items.sort(
// @ts-ignore
Expand Down Expand Up @@ -104,6 +135,26 @@ export const CrowdloansScreen = () => {
filterFunction={getListByFilterOpt}
isShowMainHeader
placeholder={i18n.placeholder.searchProject}
beforeListItem={
<>
{banners.map(item => (
<TouchableOpacity
onPress={onPressBanner(item)}
activeOpacity={BUTTON_ACTIVE_OPACITY}
style={{ marginHorizontal: theme.margin }}>
<FastImage
style={{
height: 88,
borderRadius: theme.borderRadiusLG,
marginVertical: theme.marginXS,
}}
resizeMode="cover"
source={{ uri: item.data.media }}
/>
</TouchableOpacity>
))}
</>
}
/>
</>
);
Expand Down
Loading

0 comments on commit 86c0051

Please sign in to comment.