Skip to content

Commit

Permalink
Merge pull request #201 from secretkeylabs/imamahzafar/tab-close-request
Browse files Browse the repository at this point in the history
handle when a tab that initiated a request is closed
  • Loading branch information
m-aboelenein authored Jan 19, 2023
2 parents ee55d83 + 0625c5c commit 78f3a82
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 122 deletions.
60 changes: 60 additions & 0 deletions src/app/components/infoContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import styled from 'styled-components';
import InfoIcon from '@assets/img/info.svg';

const Container = styled.div((props) => ({
display: 'flex',
flexDirection: 'row',
borderRadius: 12,
alignItems: 'flex-start',
backgroundColor: 'transparent',
padding: props.theme.spacing(8),
marginBottom: props.theme.spacing(6),
border: '1px solid rgba(255, 255, 255, 0.2)',
}));

const TextContainer = styled.div((props) => ({
marginLeft: props.theme.spacing(5),
display: 'flex',
flexDirection: 'column',
}));

const BoldText = styled.h1((props) => ({
...props.theme.body_bold_m,
color: props.theme.colors.white['0'],
}));

const SubText = styled.h1((props) => ({
...props.theme.body_xs,
marginTop: props.theme.spacing(2),
color: props.theme.colors.white['200'],
}));

const Text = styled.h1((props) => ({
...props.theme.body_m,
color: props.theme.colors.white['200'],
lineHeight: 1.4,
}));

interface Props {
titleText?: string;
bodyText: string;
}

function InfoContainer({ titleText, bodyText }: Props) {
return (
<Container>
<img src={InfoIcon} alt="alert" />
<TextContainer>
{titleText ? (
<>
<BoldText>{titleText}</BoldText>
<SubText>{bodyText}</SubText>
</>
)
: <Text>{bodyText}</Text>}
</TextContainer>
</Container>
);
}

export default InfoContainer;
23 changes: 2 additions & 21 deletions src/app/components/sendForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from 'react';
import IconBitcoin from '@assets/img/send/ic_sats_ticker.svg';
import IconStacks from '@assets/img/dashboard/stack_icon.svg';
import InfoIcon from '@assets/img/send/info.svg';
import { getTicker } from '@utils/helper';
import { StoreState } from '@stores/index';
import { useSelector } from 'react-redux';
Expand All @@ -16,6 +15,7 @@ import ActionButton from '@components/button';
import { useNavigate } from 'react-router-dom';
import { useBNSResolver, useDebounce } from '@hooks/useBnsName';
import { getFiatEquivalent } from '@secretkeylabs/xverse-core/transactions';
import InfoContainer from '@components/infoContainer';

const ScrollContainer = styled.div`
display: flex;
Expand All @@ -40,16 +40,6 @@ const RowContainer = styled.div({
alignItems: 'center',
});

const InfoContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'row',
padding: props.theme.spacing(8),
marginTop: props.theme.spacing(8),
marginBottom: props.theme.spacing(32.5),
border: `1px solid ${props.theme.colors.background.elevation3}`,
borderRadius: 8,
}));

const Container = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
Expand All @@ -67,10 +57,6 @@ const ErrorText = styled.h1((props) => ({
color: props.theme.colors.feedback.error,
}));

const TextContainer = styled.div((props) => ({
marginLeft: props.theme.spacing(5),
}));

const InputFieldContainer = styled.div(() => ({
flex: 1,
}));
Expand Down Expand Up @@ -391,12 +377,7 @@ function SendForm({
</InputFieldContainer>
</MemoInputContainer>
</Container>
<InfoContainer>
<TickerImage src={InfoIcon} />
<TextContainer>
<SubText>{t('MEMO_INFO')}</SubText>
</TextContainer>
</InfoContainer>
<InfoContainer bodyText={t('MEMO_INFO')} />
</>
)}
</OuterContainer>
Expand Down
43 changes: 28 additions & 15 deletions src/app/components/transactionsRequests/ContractCallRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ import { Args, ContractFunction } from '@secretkeylabs/xverse-core/types/api/sta
import FtPostConditionCard from '@components/postCondition/ftPostConditionCard';
import NftPostConditionCard from '@components/postCondition/nftPostConditionCard';
import AccountHeaderComponent from '@components/accountHeader';
import useOnOriginTabClose from '@hooks/useOnTabClosed';
import InfoContainer from '@components/infoContainer';
import finalizeTxSignature from './utils';

const PostConditionContainer = styled.div((props) => ({
display: 'flex',
marginTop: props.theme.spacing(12),
paddingTop: props.theme.spacing(12),
paddingBottom: props.theme.spacing(12),
borderTop: `0.5px solid ${props.theme.colors.background.elevation3}`,
Expand Down Expand Up @@ -137,17 +138,18 @@ const Detail = styled.h1((props) => ({
marginTop: props.theme.spacing(2),
}));

const InfoContainer = styled.div({
const FuncArgContainer = styled.div({
display: 'flex',
flexDirection: 'column',
});

const Container = styled.div({
const Container = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
});
marginBottom: props.theme.spacing(12),
}));

const ButtonText = styled.div((props) => ({
...props.theme.body_xs,
Expand Down Expand Up @@ -189,24 +191,33 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
const {
request, unsignedTx, funcMetaData, coinsMetaData, tabId, requestToken,
} = props;
const { t } = useTranslation('translation', { keyPrefix: 'CONTRACT_CALL_REQUEST' });
const [hasTabClosed, setHasTabClosed] = useState(false);
const { t } = useTranslation('translation');
const [isShowMore, setIsShowMore] = useState(false);
const Illustration = headerImageMapping[request.functionName ?? ''];

useOnOriginTabClose(
tabId,
() => {
setHasTabClosed(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
},
);

const showMoreButton = (
<ShowMoreButtonContainer>
<Line />
<ButtonContainer>
<ShowMoreButton onClick={() => setIsShowMore(!isShowMore)}>
<ButtonText>{isShowMore ? t('SHOW_LESS') : t('SHOW_MORE')}</ButtonText>
<ButtonSymbolText>{isShowMore ? t('MINUS') : t('PLUS')}</ButtonSymbolText>
<ButtonText>{isShowMore ? t('CONTRACT_CALL_REQUEST.SHOW_LESS') : t('CONTRACT_CALL_REQUEST.SHOW_MORE')}</ButtonText>
<ButtonSymbolText>{isShowMore ? t('CONTRACT_CALL_REQUEST.MINUS') : t('CONTRACT_CALL_REQUEST.PLUS')}</ButtonSymbolText>
</ShowMoreButton>
</ButtonContainer>
</ShowMoreButtonContainer>
);

const renderContractAddress = isShowMore && (
<RedirectAddressView recipient={request.contractAddress} title={t('CONTRACT_ADDRESS')} />
<RedirectAddressView recipient={request.contractAddress} title={t('CONTRACT_CALL_REQUEST.CONTRACT_ADDRESS')} />
);
type ArgToView = { name: string; value: string; type: any };
const getFunctionArgs = (): Array<ArgToView> => {
Expand Down Expand Up @@ -249,27 +260,27 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
const args = getFunctionArgs();
if (isShowMore) {
return args.map((arg, index) => (
<InfoContainer>
<FuncArgContainer>
<Title>{arg.name}</Title>
<Value>{arg.value}</Value>
<Detail>{arg.type}</Detail>
</InfoContainer>
</FuncArgContainer>
));
}
};

const showSponsoredTransactionTag = (
<SponsoredContainer>
<SponsoredTag>
<SponosredText>{t('SPONSORED')}</SponosredText>
<SponosredText>{t('CONTRACT_CALL_REQUEST.SPONSORED')}</SponosredText>
</SponsoredTag>
</SponsoredContainer>
);

const postConditionAlert = unsignedTx?.postConditionMode === 2
&& unsignedTx?.postConditions.values.length <= 0 && (
<PostConditionContainer>
<PostConditionAlertText>{t('POST_CONDITION_ALERT')}</PostConditionAlertText>
<PostConditionAlertText>{t('CONTRACT_CALL_REQUEST.POST_CONDITION_ALERT')}</PostConditionAlertText>
</PostConditionContainer>
);
const { network } = useWalletSelector();
Expand Down Expand Up @@ -360,13 +371,15 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
<FunctionTitle>{request.functionName}</FunctionTitle>
<DappTitle>{`Requested by ${request.appDetails?.name}`}</DappTitle>
</Container>
{hasTabClosed && <InfoContainer titleText={t('WINDOW_CLOSED_ALERT.TITLE')} bodyText={t('WINDOW_CLOSED_ALERT.BODY')} />}

{postConditionAlert}
{request.sponsored && showSponsoredTransactionTag}
{renderPostConditionsCard()}
<InfoContainer>
<Title>{t('FUNCTION')}</Title>
<FuncArgContainer>
<Title>{t('CONTRACT_CALL_REQUEST.FUNCTION')}</Title>
<Value>{request?.functionName}</Value>
</InfoContainer>
</FuncArgContainer>
{functionArgsView()}
{renderContractAddress}
{showMoreButton}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ import {
} from '@secretkeylabs/xverse-core';
import { useNavigate } from 'react-router-dom';
import AccountHeaderComponent from '@components/accountHeader';
import useOnOriginTabClose from '@hooks/useOnTabClosed';
import InfoContainer from '@components/infoContainer';
import finalizeTxSignature from './utils';

const Container = styled.div({
const Container = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
});
marginBottom: props.theme.spacing(12),
}));

const TopImage = styled.img({
width: 88,
Expand All @@ -44,7 +47,7 @@ const Value = styled.h1((props) => ({
marginTop: props.theme.spacing(2),
}));

const InfoContainer = styled.div((props) => ({
const ColumnContainer = styled.div((props) => ({
display: 'flex',
marginTop: props.theme.spacing(6),
paddingTop: props.theme.spacing(12),
Expand All @@ -64,7 +67,6 @@ const DownloadContainer = styled.div((props) => ({

const PostConditionContainer = styled.div((props) => ({
display: 'flex',
marginTop: props.theme.spacing(12),
paddingTop: props.theme.spacing(12),
paddingBottom: props.theme.spacing(12),
borderTop: `0.5px solid ${props.theme.colors.background.elevation3}`,
Expand Down Expand Up @@ -140,10 +142,16 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
unsignedTx, codeBody, contractName, sponsored, tabId, requestToken,
} = props;
const { network } = useWalletSelector();
const { t } = useTranslation('translation', { keyPrefix: 'DEPLOY_CONTRACT_REQUEST' });
const [hasTabClosed, setHasTabClosed] = useState(false);
const { t } = useTranslation('translation');
const [loaderForBroadcastingTx, setLoaderForBroadcastingTx] = useState<boolean>(false);
const navigate = useNavigate();

useOnOriginTabClose(tabId, () => {
setHasTabClosed(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
});

const broadcastTx = async (tx: StacksTransaction[]) => {
try {
setLoaderForBroadcastingTx(true);
Expand Down Expand Up @@ -207,15 +215,15 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
const showSponsoredTransactionTag = (
<SponsoredContainer>
<SponsoredTag>
<SponosredText>{t('SPONSORED')}</SponosredText>
<SponosredText>{t('DEPLOY_CONTRACT_REQUEST.SPONSORED')}</SponosredText>
</SponsoredTag>
</SponsoredContainer>
);

const postConditionAlert = unsignedTx?.postConditionMode === 2
&& unsignedTx?.postConditions.values.length <= 0 && (
<PostConditionContainer>
<PostConditionAlertText>{t('POST_CONDITION_ALERT')}</PostConditionAlertText>
<PostConditionAlertText>{t('DEPLOY_CONTRACT_REQUEST.POST_CONDITION_ALERT')}</PostConditionAlertText>
</PostConditionContainer>
);

Expand All @@ -231,28 +239,29 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
>
<Container>
<TopImage src={DeployContractImage} alt="deploy_contract" />
<FunctionTitle>{t('DEPLOY_CONTRACT')}</FunctionTitle>
<FunctionTitle>{t('DEPLOY_CONTRACT_REQUEST.DEPLOY_CONTRACT')}</FunctionTitle>
</Container>
{hasTabClosed && <InfoContainer titleText={t('WINDOW_CLOSED_ALERT.TITLE')} bodyText={t('WINDOW_CLOSED_ALERT.BODY')} />}
{postConditionAlert}
{sponsored && showSponsoredTransactionTag}
{unsignedTx?.postConditions?.values?.map((postCondition) => (
<StxPostConditionCard postCondition={postCondition as PostCondition} />
))}
<InfoContainer>
<Title>{t('CONTRACT_NAME')}</Title>
<ColumnContainer>
<Title>{t('DEPLOY_CONTRACT_REQUEST.CONTRACT_NAME')}</Title>
<Value>{contractName}</Value>
<DownloadContainer>
<Title>{t('FUNCTION')}</Title>
<Title>{t('DEPLOY_CONTRACT_REQUEST.FUNCTION')}</Title>
<DownloadButtonContainer>
<Button onClick={downloadCode}>
<>
<ButtonText>{t('DOWNLOAD')}</ButtonText>
<ButtonText>{t('DEPLOY_CONTRACT_REQUEST.DOWNLOAD')}</ButtonText>
<ButtonImage src={DownloadImage} />
</>
</Button>
</DownloadButtonContainer>
</DownloadContainer>
</InfoContainer>
</ColumnContainer>
</ConfirmStxTransationComponent>
</>
);
Expand Down
21 changes: 21 additions & 0 deletions src/app/hooks/useOnTabClosed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { InternalMethods } from 'content-scripts/message-types';
import { BackgroundMessages } from 'content-scripts/messages';
import { useEffect } from 'react';

export default function useOnOriginTabClose(
tabId: number,
handler: () => void,
) {
useEffect(() => {
const messageHandler = (message: BackgroundMessages) => {
if (message.method !== InternalMethods.OriginatingTabClosed) return;
if (message.payload.tabId === Number(tabId)) {
handler();
}
};

chrome.runtime.onMessage.addListener(messageHandler);

return () => chrome.runtime.onMessage.removeListener(handler);
}, []);
}
Loading

0 comments on commit 78f3a82

Please sign in to comment.