feat(Wallet/Toasts): Updated / added toasts related to community tokens received

- Added new role into ephemeral_notification_model: image.
- Added new view API to display ephemeral notifications with image and action.
- Added support to display asset or collectible image in a toast with different shape (circle or square).
- Default values assigned to the new parameters added in `CommunityTokensStore` meanwhile backend is not ready.
- Created page for `StatusToastMessage` in `storybook`.

Closes #13039
This commit is contained in:
Noelia 2024-01-19 12:40:41 +01:00 committed by Noelia
parent 09629985f7
commit 21a33cabd9
10 changed files with 216 additions and 22 deletions

View File

@ -13,6 +13,7 @@ type
title: string title: string
durationInMs: int durationInMs: int
subTitle: string subTitle: string
image: string
icon: string icon: string
iconColor: string iconColor: string
loading: bool loading: bool
@ -26,6 +27,7 @@ proc initItem*(id: int64,
title: string, title: string,
durationInMs = 0, durationInMs = 0,
subTitle = "", subTitle = "",
image = "",
icon = "", icon = "",
iconColor = "", iconColor = "",
loading = false, loading = false,
@ -40,6 +42,7 @@ proc initItem*(id: int64,
result.durationInMs = durationInMs result.durationInMs = durationInMs
result.title = title result.title = title
result.subTitle = subTitle result.subTitle = subTitle
result.image = image
result.icon = icon result.icon = icon
result.iconColor = iconColor result.iconColor = iconColor
result.loading = loading result.loading = loading
@ -64,6 +67,9 @@ proc durationInMs*(self: Item): int =
proc subTitle*(self: Item): string = proc subTitle*(self: Item): string =
self.subTitle self.subTitle
proc image*(self: Item): string =
self.image
proc icon*(self: Item): string = proc icon*(self: Item): string =
self.icon self.icon

View File

@ -8,6 +8,7 @@ type
DurationInMs DurationInMs
Title Title
SubTitle SubTitle
Image
Icon Icon
IconColor IconColor
Loading Loading
@ -41,6 +42,7 @@ QtObject:
ModelRole.DurationInMs.int:"durationInMs", ModelRole.DurationInMs.int:"durationInMs",
ModelRole.Title.int:"title", ModelRole.Title.int:"title",
ModelRole.SubTitle.int:"subTitle", ModelRole.SubTitle.int:"subTitle",
ModelRole.Image.int:"image",
ModelRole.Icon.int:"icon", ModelRole.Icon.int:"icon",
ModelRole.IconColor.int:"iconColor", ModelRole.IconColor.int:"iconColor",
ModelRole.Loading.int:"loading", ModelRole.Loading.int:"loading",
@ -69,6 +71,8 @@ QtObject:
result = newQVariant(item.title) result = newQVariant(item.title)
of ModelRole.SubTitle: of ModelRole.SubTitle:
result = newQVariant(item.subTitle) result = newQVariant(item.subTitle)
of ModelRole.Image:
result = newQVariant(item.image)
of ModelRole.Icon: of ModelRole.Icon:
result = newQVariant(item.icon) result = newQVariant(item.icon)
of ModelRole.IconColor: of ModelRole.IconColor:

View File

@ -204,6 +204,10 @@ method displayEphemeralWithActionNotification*(self: AccessInterface, title: str
ephNotifType: int, actionType: int, actionData: string, details = NotificationDetails()) {.base.} = ephNotifType: int, actionType: int, actionData: string, details = NotificationDetails()) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method displayEphemeralImageWithActionNotification*(self: AccessInterface, title: string, subTitle: string, image: string,
ephNotifType: int, actionType: int, actionData: string, details = NotificationDetails()) {.base.} =
raise newException(ValueError, "No implementation available")
method removeEphemeralNotification*(self: AccessInterface, id: int64) {.base.} = method removeEphemeralNotification*(self: AccessInterface, id: int64) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -1312,7 +1312,7 @@ method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle
elif(ephNotifType == EphemeralNotificationType.Danger.int): elif(ephNotifType == EphemeralNotificationType.Danger.int):
finalEphNotifType = EphemeralNotificationType.Danger finalEphNotifType = EphemeralNotificationType.Danger
let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, icon, "", let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, "", icon, "",
loading, finalEphNotifType, url, 0, "", details) loading, finalEphNotifType, url, 0, "", details)
self.view.ephemeralNotificationModel().addItem(item) self.view.ephemeralNotificationModel().addItem(item)
@ -1328,10 +1328,27 @@ method displayEphemeralWithActionNotification*[T](self: Module[T], title: string
elif(ephNotifType == EphemeralNotificationType.Danger.int): elif(ephNotifType == EphemeralNotificationType.Danger.int):
finalEphNotifType = EphemeralNotificationType.Danger finalEphNotifType = EphemeralNotificationType.Danger
let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, icon, iconColor, let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, "", icon, iconColor,
loading, finalEphNotifType, "", actionType, actionData, details) loading, finalEphNotifType, "", actionType, actionData, details)
self.view.ephemeralNotificationModel().addItem(item) self.view.ephemeralNotificationModel().addItem(item)
# TO UNIFY with the one above.
# Further refactor will be done in a next step
method displayEphemeralImageWithActionNotification*[T](self: Module[T], title: string, subTitle: string, image: string, ephNotifType: int,
actionType: int, actionData: string, details = NotificationDetails()) =
let now = getTime()
let id = now.toUnix * 1000000000 + now.nanosecond
var finalEphNotifType = EphemeralNotificationType.Default
if(ephNotifType == EphemeralNotificationType.Success.int):
finalEphNotifType = EphemeralNotificationType.Success
elif(ephNotifType == EphemeralNotificationType.Danger.int):
finalEphNotifType = EphemeralNotificationType.Danger
let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, image, "", "", false,
finalEphNotifType, "", actionType, actionData, details)
self.view.ephemeralNotificationModel().addItem(item)
method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle: string, details: NotificationDetails) = method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle: string, details: NotificationDetails) =
if(details.notificationType == NotificationType.NewMessage or if(details.notificationType == NotificationType.NewMessage or
details.notificationType == NotificationType.NewMessageWithPersonalMention or details.notificationType == NotificationType.NewMessageWithPersonalMention or

View File

@ -124,6 +124,12 @@ QtObject:
ephNotifType: int, actionType: int, actionData: string) {.slot.} = ephNotifType: int, actionType: int, actionData: string) {.slot.} =
self.delegate.displayEphemeralWithActionNotification(title, subTitle, icon, iconColor, loading, ephNotifType, actionType, actionData) self.delegate.displayEphemeralWithActionNotification(title, subTitle, icon, iconColor, loading, ephNotifType, actionType, actionData)
# TO UNIFY with the one above.
# Further refactor will be done in a next step
proc displayEphemeralImageWithActionNotification*(self: View, title: string, subTitle: string, image: string, ephNotifType: int,
actionType: int, actionData: string) {.slot.} =
self.delegate.displayEphemeralImageWithActionNotification(title, subTitle, image, ephNotifType, actionType, actionData)
proc removeEphemeralNotification*(self: View, id: string) {.slot.} = proc removeEphemeralNotification*(self: View, id: string) {.slot.} =
self.delegate.removeEphemeralNotification(id.parseInt) self.delegate.removeEphemeralNotification(id.parseInt)

File diff suppressed because one or more lines are too long

View File

@ -1636,10 +1636,25 @@ Item {
clip: false clip: false
delegate: StatusToastMessage { delegate: StatusToastMessage {
property bool isSquare : isSquareShape(model.actionData)
// Specific method to calculate image radius depending on if the toast represents some info about a collectible or an asset
function isSquareShape(data) {
// It expects the data is a JSON file containing `tokenType`
if(data) {
var parsedData = JSON.parse(data)
var tokenType = parsedData.tokenType
return tokenType === Constants.TokenType.ERC721
}
return false
}
objectName: "statusToastMessage" objectName: "statusToastMessage"
width: ListView.view.width width: ListView.view.width
primaryText: model.title primaryText: model.title
secondaryText: model.subTitle secondaryText: model.subTitle
image: model.image
imageRadius: model.image && isSquare ? 8 : imageSize / 2
icon.name: model.icon icon.name: model.icon
iconColor: model.iconColor iconColor: model.iconColor
loading: model.loading loading: model.loading

View File

@ -2,6 +2,8 @@ import QtQuick 2.15
import utils 1.0 import utils 1.0
import AppLayouts.Wallet 1.0
import AppLayouts.stores 1.0 import AppLayouts.stores 1.0
import AppLayouts.Chat.stores 1.0 as ChatStores import AppLayouts.Chat.stores 1.0 as ChatStores
@ -21,7 +23,8 @@ QtObject {
NavigateToCommunityAdmin = 1, NavigateToCommunityAdmin = 1,
OpenFinaliseOwnershipPopup = 2, OpenFinaliseOwnershipPopup = 2,
OpenSendModalPopup = 3, OpenSendModalPopup = 3,
ViewTransactionDetails = 4 ViewTransactionDetails = 4,
OpenFirstCommunityTokenPopup = 5
} }
// Stores: // Stores:
@ -54,22 +57,6 @@ QtObject {
communityId) communityId)
} }
function onCommunityTokenReceived(name, image, communityId, communityName, communityColor, balance, chainId, txHash) {
var data = {
communityId: communityId,
chainId: chainId,
txHash: txHash
}
Global.displayToastWithActionMessage(qsTr("You were airdropped %1 %2 asset from %3").arg(balance).arg(name).arg(communityName),
qsTr("View transaction details"),
image,
communityColor,
false,
Constants.ephemeralNotificationType.normal,
ToastsManager.ActionType.ViewTransactionDetails,
JSON.stringify(data))
}
function onSetSignerStateChanged(communityId, communityName, status, url) { function onSetSignerStateChanged(communityId, communityName, status, url) {
if (status === Constants.ContractTransactionStatus.Completed) { if (status === Constants.ContractTransactionStatus.Completed) {
Global.displayToastMessage(qsTr("%1 smart contract amended").arg(communityName), Global.displayToastMessage(qsTr("%1 smart contract amended").arg(communityName),
@ -140,6 +127,48 @@ QtObject {
Constants.ephemeralNotificationType.danger, Constants.ephemeralNotificationType.danger,
"") "")
} }
// Community token received in the user wallet:
function onCommunityTokenReceived(name, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName) {
// Some error control:
if(tokenType !== Constants.TokenType.ERC20 && tokenType !== Constants.TokenType.ERC721) {
console.warn("Community token Received: Unexpected token type while creating a toast message: " + tokenType)
return
}
var data = {
communityId: communityId,
chainId: chainId,
txHash: txHash,
tokenType: tokenType
}
if(isFirst) {
var tokenTypeText = ""
if(tokenType === Constants.TokenType.ERC20) {
tokenTypeText = qsTr("You received your first community asset")
} else if(tokenType === Constants.TokenType.ERC721) {
tokenTypeText = qsTr("You received your first community collectible")
}
// First community token received toast:
Global.displayImageToastWithActionMessage(qsTr("%1: %2 %3").arg(tokenTypeText).arg(balance).arg(name),
qsTr("Learn more"),
image,
Constants.ephemeralNotificationType.normal,
ToastsManager.ActionType.OpenFirstCommunityTokenPopup,
JSON.stringify(data))
} else {
// Generic community token received toast:
Global.displayImageToastWithActionMessage(qsTr("You were airdropped %1 %2 from %3 to %4").arg(balance).arg(name).arg(communityName).arg(walletAccountName),
qsTr("View transaction details"),
image,
Constants.ephemeralNotificationType.normal,
ToastsManager.ActionType.ViewTransactionDetails,
JSON.stringify(data))
}
}
} }
// Connections to global. These will lead the backend integration: // Connections to global. These will lead the backend integration:
@ -155,6 +184,10 @@ QtObject {
function onDisplayToastWithActionMessage(title: string, subTitle: string, icon: string, iconColor: string, loading: bool, ephNotifType: int, actionType: int, actionData: string) { function onDisplayToastWithActionMessage(title: string, subTitle: string, icon: string, iconColor: string, loading: bool, ephNotifType: int, actionType: int, actionData: string) {
root.rootStore.mainModuleInst.displayEphemeralWithActionNotification(title, subTitle, icon, iconColor, loading, ephNotifType, actionType, actionData) root.rootStore.mainModuleInst.displayEphemeralWithActionNotification(title, subTitle, icon, iconColor, loading, ephNotifType, actionType, actionData)
} }
function onDisplayImageToastWithActionMessage(title: string, subTitle: string, image: string, ephNotifType: int, actionType: int, actionData: string) {
root.rootStore.mainModuleInst.displayEphemeralImageWithActionNotification(title, subTitle, image, ephNotifType, actionType, actionData)
}
} }
// It will cover all specific actions (different than open external links) that can be done after clicking toast link text // It will cover all specific actions (different than open external links) that can be done after clicking toast link text
@ -169,6 +202,22 @@ QtObject {
case ToastsManager.ActionType.OpenSendModalPopup: case ToastsManager.ActionType.OpenSendModalPopup:
root.sendModalPopup.open() root.sendModalPopup.open()
return return
case ToastsManager.ActionType.ViewTransactionDetails:
var txHash = ""
if(actionData) {
var parsedData = JSON.parse(actionData)
txHash = parsedData.txHash
Global.changeAppSectionBySectionType(Constants.appSection.wallet,
WalletLayout.LeftPanelSelection.AllAddresses,
WalletLayout.RightPanelSelection.Activity)
// TODO: Final navigation to the specific transaction entry --> {transaction: txHash}) --> Issue #13249
return
}
console.warn("Unexpected transaction hash while trying to navigate to the details page: " + txHash)
return
case ToastsManager.ActionType.OpenFirstCommunityTokenPopup:
console.warn("TODO: #12366")
return
default: default:
console.warn("ToastsManager: Action type is not defined") console.warn("ToastsManager: Action type is not defined")
return return

View File

@ -34,7 +34,11 @@ QtObject {
signal communityOwnershipDeclined(string communityName) signal communityOwnershipDeclined(string communityName)
signal sendOwnerTokenStateChanged(string tokenName, int status, string url) signal sendOwnerTokenStateChanged(string tokenName, int status, string url)
signal ownerTokenReceived(string communityId, string communityName) signal ownerTokenReceived(string communityId, string communityName)
signal communityTokenReceived(string name, string image, string communityId, string communityName, string communityColor, string balance, int chainId, string txHash) signal communityTokenReceived(string name, string image,
string communityId, string communityName,
string balance, int chainId,
string txHash, bool isFirst,
int tokenType, string walletAccountName)
// Minting tokens: // Minting tokens:
function deployCollectible(communityId, collectibleItem) function deployCollectible(communityId, collectibleItem)
@ -132,8 +136,16 @@ QtObject {
root.ownerTokenReceived(communityId, communityName) root.ownerTokenReceived(communityId, communityName)
} }
function onCommunityTokenReceived(name, image, communityId, communityName, communityColor, balance, chainId, txHash) { function onCommunityTokenReceived(name, image, communityId, communityName, communityColor /*Unused, can be removed*/, balance, chainId, txHash/*, isFirst, tokenType, walletAccountName*/) {
root.communityTokenReceived(name, image, communityId, communityName, communityColor, balance, chainId, txHash) // TODO BACKEND: #13250
// ** `isFirst` property will be true if it's the first time the user receives a community asset and a community collectible
// ** `tokenType` property will determine if the received minted token is an ERC20 or an ERC720
// ** `walletAccountName` property will provide the wallet account name where the token was received
var isFirst = false
var tokenType = Constants.TokenType.ERC20
var walletAccountName = "Status account"
root.communityTokenReceived(name, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName)
} }
function onSetSignerStateChanged(communityId, communityName, status, url) { function onSetSignerStateChanged(communityId, communityName, status, url) {

View File

@ -27,6 +27,7 @@ QtObject {
signal displayToastMessage(string title, string subTitle, string icon, bool loading, int ephNotifType, string url) signal displayToastMessage(string title, string subTitle, string icon, bool loading, int ephNotifType, string url)
signal displayToastWithActionMessage(string title, string subTitle, string icon, string iconColor, bool loading, int ephNotifType, int actionType, string data) signal displayToastWithActionMessage(string title, string subTitle, string icon, string iconColor, bool loading, int ephNotifType, int actionType, string data)
signal displayImageToastWithActionMessage(string title, string subTitle, string image, int ephNotifType, int actionType, string data)
signal openPopupRequested(var popupComponent, var params) signal openPopupRequested(var popupComponent, var params)
signal closePopupRequested() signal closePopupRequested()