-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
feat: snaps dynamic UI #12429
Draft
Daniel-Cross
wants to merge
47
commits into
main
Choose a base branch
from
epic/snaps-ui
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,718
−46
Draft
feat: snaps dynamic UI #12429
Changes from 25 commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
1a12630
added content from the original POC branch
Daniel-Cross 280b4a7
working branch
Daniel-Cross fc9b773
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 24f219e
fixed import issue
Daniel-Cross 6c7f28d
add SnapInterfaceController to types
owencraston 02f2054
add requestUserApproval snap method
owencraston f86e326
render snaps dialog
owencraston 1426dc6
disable old state check
owencraston 4bf801e
working dialogs except for custom
Daniel-Cross db88bb6
added updateInterface
Daniel-Cross 2786acd
input on custom dialog now working
Daniel-Cross 8d20924
removed some unused imports and comsole logs
Daniel-Cross f0d31c4
added missing components
Daniel-Cross 351cebf
removed debounce
Daniel-Cross 04876a5
resolved conflicts
Daniel-Cross 34bed71
Merge branch 'main' into epic/snaps-ui
Daniel-Cross d8aea79
changed import location
Daniel-Cross 2f34a7d
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 0b14396
updated snapshots
Daniel-Cross c141fb5
fix engine lint errors
owencraston 93a3d17
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 7bc4a32
fix engine tests
owencraston a3d5413
add @nobles/hashes
owencraston b180225
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 683f4cd
updated snapshots
Daniel-Cross 956aac3
Merge branch 'main' into epic/snaps-ui
Daniel-Cross c5664c1
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 8666385
updated based on feedback and synced files with the extension
Daniel-Cross 4fecdd1
resolved conflicts
Daniel-Cross 41923b1
added input props
Daniel-Cross 26476fb
made changes to types for input
Daniel-Cross dcd89af
addressed footer type issue
Daniel-Cross 29ea7f6
fixed some liter issues
Daniel-Cross ef4832b
fixed colours
Daniel-Cross 5da4e2e
fixed colour import
Daniel-Cross e8bd935
fixed global element declaration issue
Daniel-Cross c8f3a18
removed the need to use flex on mobile
Daniel-Cross d7d00ad
changed enum related values in footer
Daniel-Cross d8e191c
fixed linting issues from enums
Daniel-Cross 5f42682
removed enum usage
Daniel-Cross 125e2da
removed unused import
Daniel-Cross 53a16d9
added a package for handling html entities
Daniel-Cross cde7cee
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 59f2c7d
yarn deduplicate
Daniel-Cross 4a1d0fb
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 79d81bf
Merge branch 'main' into epic/snaps-ui
Daniel-Cross 7d03e1b
Merge branch 'main' into epic/snaps-ui
Daniel-Cross File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
app/components/Approvals/Snaps/SnapDialogApproval/SnapDialogApproval.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { StyleSheet } from 'react-native'; | ||
import { Theme } from '../../../../util/theme/models'; | ||
import Device from '../../../../util/device'; | ||
|
||
/** | ||
* | ||
* @param params Style sheet params. | ||
* @param params.theme App theme from ThemeContext. | ||
* @param params.vars Inputs that the style sheet depends on. | ||
* @returns StyleSheet object. | ||
*/ | ||
const styleSheet = (params: { theme: Theme }) => { | ||
const { theme } = params; | ||
const { colors } = theme; | ||
return StyleSheet.create({ | ||
root: { | ||
backgroundColor: colors.background.default, | ||
paddingTop: 24, | ||
paddingLeft: 16, | ||
paddingRight: 16, | ||
borderTopLeftRadius: 20, | ||
borderTopRightRadius: 20, | ||
minHeight: 200, | ||
paddingBottom: Device.isIphoneX() ? 20 : 0, | ||
}, | ||
actionContainer: { | ||
flex: 0, | ||
paddingVertical: 16, | ||
justifyContent: 'center', | ||
}, | ||
}); | ||
}; | ||
|
||
export default styleSheet; |
166 changes: 166 additions & 0 deletions
166
app/components/Approvals/Snaps/SnapDialogApproval/SnapsDialogApproval.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps) | ||
import React, { useState } from 'react'; | ||
import { useStyles } from '../../../hooks/useStyles'; | ||
import { strings } from '../../../../../locales/i18n'; | ||
import stylesheet from './SnapDialogApproval.styles'; | ||
import useApprovalRequest from '../../../Views/confirmations/hooks/useApprovalRequest'; | ||
import { View } from 'react-native-animatable'; | ||
import ApprovalModal from '../../ApprovalModal'; | ||
import BottomSheetFooter, { | ||
ButtonsAlignment, | ||
} from '../../../../component-library/components/BottomSheets/BottomSheetFooter'; | ||
import { | ||
ButtonVariants, | ||
ButtonSize, | ||
} from '../../../../component-library/components/Buttons/Button'; | ||
import Engine from '../../../../core/Engine'; | ||
import { SnapUIRenderer } from '../SnapUIRenderer/SnapUIRenderer'; | ||
import { SnapId } from '@metamask/snaps-sdk'; | ||
import { IconName } from '../../../../component-library/components/Icons/Icon'; | ||
|
||
enum SnapDialogTypes { | ||
ALERT = 'snap_dialog:alert', | ||
CONFIRM = 'snap_dialog:confirmation', | ||
PROMPT = 'snap_dialog:prompt', | ||
CUSTOM = 'snap_dialog', | ||
} | ||
|
||
enum TemplateConfirmation { | ||
Ok = 'template_confirmation.ok', | ||
CANCEL = 'template_confirmation.cancel', | ||
} | ||
|
||
const SnapDialogApproval = () => { | ||
const [isLoading, setIsLoading] = useState(false); | ||
const { approvalRequest } = useApprovalRequest(); | ||
const { styles } = useStyles(stylesheet, {}); | ||
|
||
const onCancel = async () => { | ||
if (!approvalRequest) return; | ||
|
||
await Engine.acceptPendingApproval(approvalRequest.id, null as any); | ||
}; | ||
|
||
const onConfirmInput = async () => { | ||
setIsLoading(true); | ||
if (!approvalRequest) return; | ||
|
||
const inputState = | ||
await Engine.context.SnapInterfaceController.getInterface( | ||
approvalRequest?.origin as SnapId, | ||
approvalRequest.requestData.id, | ||
); | ||
await Engine.acceptPendingApproval( | ||
approvalRequest.id, | ||
inputState.state['custom-input'] as any, | ||
); | ||
setIsLoading(false); | ||
}; | ||
|
||
const onConfirm = async () => { | ||
setIsLoading(true); | ||
if (!approvalRequest) return; | ||
|
||
await Engine.acceptPendingApproval(approvalRequest.id, true as any); | ||
|
||
setIsLoading(false); | ||
}; | ||
|
||
const onReject = async () => { | ||
if (!approvalRequest) return; | ||
|
||
await Engine.acceptPendingApproval(approvalRequest.id, false as any); | ||
}; | ||
|
||
if ( | ||
approvalRequest?.type !== SnapDialogTypes.ALERT && | ||
approvalRequest?.type !== SnapDialogTypes.CONFIRM && | ||
approvalRequest?.type !== SnapDialogTypes.PROMPT && | ||
approvalRequest?.type !== SnapDialogTypes.CUSTOM | ||
) | ||
return null; | ||
|
||
const getDialogButtons = (type: SnapDialogTypes | undefined) => { | ||
switch (type) { | ||
case SnapDialogTypes.ALERT: | ||
return [ | ||
{ | ||
variant: ButtonVariants.Primary, | ||
label: strings(TemplateConfirmation.Ok), | ||
size: ButtonSize.Lg, | ||
onPress: onCancel, | ||
}, | ||
]; | ||
|
||
case SnapDialogTypes.CONFIRM: | ||
case SnapDialogTypes.PROMPT: | ||
return [ | ||
{ | ||
variant: ButtonVariants.Secondary, | ||
label: strings(TemplateConfirmation.CANCEL), | ||
size: ButtonSize.Lg, | ||
onPress: onReject, | ||
}, | ||
{ | ||
variant: ButtonVariants.Primary, | ||
label: strings(TemplateConfirmation.Ok), | ||
size: ButtonSize.Lg, | ||
onPress: onConfirm, | ||
}, | ||
]; | ||
case SnapDialogTypes.CUSTOM: | ||
return [ | ||
{ | ||
variant: ButtonVariants.Secondary, | ||
label: strings(TemplateConfirmation.CANCEL), | ||
size: ButtonSize.Lg, | ||
onPress: onCancel, | ||
startIconName: IconName.Close, | ||
}, | ||
{ | ||
variant: ButtonVariants.Primary, | ||
label: strings(TemplateConfirmation.Ok), | ||
size: ButtonSize.Lg, | ||
onPress: onConfirmInput, | ||
endIconName: IconName.Check, | ||
}, | ||
]; | ||
|
||
default: | ||
return []; | ||
} | ||
}; | ||
|
||
const buttons = getDialogButtons(approvalRequest?.type); | ||
const snapId = approvalRequest?.origin; | ||
const interfaceId = approvalRequest?.requestData?.id; | ||
|
||
return ( | ||
<ApprovalModal | ||
isVisible={ | ||
approvalRequest?.type === SnapDialogTypes.ALERT || | ||
approvalRequest?.type === SnapDialogTypes.CONFIRM || | ||
approvalRequest?.type === SnapDialogTypes.PROMPT || | ||
approvalRequest?.type === SnapDialogTypes.CUSTOM | ||
} | ||
onCancel={onCancel} | ||
> | ||
<View style={styles.root}> | ||
<SnapUIRenderer | ||
snapId={snapId} | ||
interfaceId={interfaceId} | ||
isLoading={isLoading} | ||
/> | ||
<View style={styles.actionContainer}> | ||
<BottomSheetFooter | ||
buttonsAlignment={ButtonsAlignment.Horizontal} | ||
buttonPropsArray={buttons} | ||
/> | ||
</View> | ||
</View> | ||
</ApprovalModal> | ||
); | ||
}; | ||
|
||
export default SnapDialogApproval; | ||
///: END:ONLY_INCLUDE_IF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps) | ||
export { default } from './SnapDialogApproval'; | ||
///: END:ONLY_INCLUDE_IF |
9 changes: 9 additions & 0 deletions
9
app/components/Approvals/Snaps/SnapUICard/SnapUICard.props.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { ImageSourcePropType } from 'react-native'; | ||
|
||
export interface SnapUICardProps { | ||
image?: ImageSourcePropType; | ||
title?: string; | ||
description?: string; | ||
value?: string; | ||
extra?: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps,external-snaps) | ||
import React from 'react'; | ||
import { Image } from 'react-native'; | ||
// External dependencies. | ||
import { | ||
AlignItems, | ||
Display, | ||
FlexDirection, | ||
JustifyContent, | ||
TextAlign, | ||
} from '../SnapUIRenderer/utils'; | ||
import Text, { | ||
TextColor, | ||
TextVariant, | ||
} from '../../../../component-library/components/Texts/Text'; | ||
import { SnapUICardProps } from './SnapUICard.props'; | ||
import styles, { Box } from '../../../UI/Box'; | ||
|
||
export const SnapUICard: React.FC<SnapUICardProps> = ({ | ||
image, | ||
title, | ||
description, | ||
value, | ||
extra, | ||
}) => ( | ||
<Box | ||
testID="snaps-ui-card" | ||
display={Display.Flex} | ||
justifyContent={JustifyContent.spaceBetween} | ||
alignItems={AlignItems.center} | ||
> | ||
<Box display={Display.Flex} gap={4} alignItems={AlignItems.center}> | ||
{image && ( | ||
<Image | ||
width={32} | ||
height={32} | ||
source={image} | ||
style={styles.overflowHidden} | ||
/> | ||
)} | ||
<Box | ||
display={Display.Flex} | ||
flexDirection={FlexDirection.Column} | ||
style={styles.overflowHidden} | ||
> | ||
<Text | ||
variant={TextVariant.BodyMDMedium} | ||
numberOfLines={1} | ||
ellipsizeMode="tail" | ||
> | ||
{title} | ||
</Text> | ||
{description && ( | ||
<Text | ||
color={TextColor.Alternative} | ||
numberOfLines={1} | ||
ellipsizeMode="tail" | ||
> | ||
{description} | ||
</Text> | ||
)} | ||
</Box> | ||
</Box> | ||
<Box | ||
display={Display.Flex} | ||
flexDirection={FlexDirection.Column} | ||
textAlign={TextAlign.right} | ||
style={styles.overflowHidden} | ||
> | ||
<Text | ||
variant={TextVariant.BodyMDMedium} | ||
numberOfLines={1} | ||
ellipsizeMode="tail" | ||
> | ||
{value} | ||
</Text> | ||
{extra && ( | ||
<Text | ||
color={TextColor.Alternative} | ||
numberOfLines={1} | ||
ellipsizeMode="tail" | ||
> | ||
{extra} | ||
</Text> | ||
)} | ||
</Box> | ||
</Box> | ||
); | ||
///: END:ONLY_INCLUDE_IF |
1 change: 1 addition & 0 deletions
1
app/components/Approvals/Snaps/SnapUIRenderer/SnapInterface.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type SetCurrentInputFocus = (name: string | null) => void; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Custom dialogs can define their own buttons. We will need to support that instead of hardcoding their text and the logic to confirm/cancel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a Type for the expected data on these custom buttons? I took a look at the extension code but it's much different to how we implement on Mobile so it's hard to make sense of what data should be expected for the custom buttons. Also, it doesn't seem to have any custom data coming from the snaps when I search for the button content.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The buttons should be extracted from
Container -> Footer
.https://github.com/MetaMask/snaps/blob/main/packages/snaps-sdk/src/jsx/components/Container.ts
https://github.com/MetaMask/snaps/blob/main/packages/snaps-sdk/src/jsx/components/Footer.ts
On the extension we have some fallback buttons if the Snap doesn't define some, the logic is somewhat complicated, but can be found here: https://github.com/MetaMask/metamask-extension/blob/main/ui/components/app/snaps/snap-ui-renderer/components/footer.ts