feat(@desktop/wallet): add support for more NFTs media types

Fixes #9836
This commit is contained in:
Dario Gabriel Lipicar 2023-04-03 12:38:05 -03:00 committed by dlipicar
parent 99a9f2ace0
commit ed6ea5d90c
18 changed files with 546 additions and 115 deletions

View File

@ -65,113 +65,91 @@ QtObject:
read = getNetworkIconUrl read = getNetworkIconUrl
notify = networkIconUrlChanged notify = networkIconUrlChanged
proc currentCollectibleChanged(self: View) {.signal.}
proc getName(self: View): QVariant {.slot.} = proc getName(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getName()) return newQVariant(self.collectible.getName())
proc nameChanged(self: View) {.signal.}
QtProperty[QVariant] name: QtProperty[QVariant] name:
read = getName read = getName
notify = nameChanged notify = currentCollectibleChanged
proc getID(self: View): QVariant {.slot.} = proc getID(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getId()) return newQVariant(self.collectible.getId())
proc idChanged(self: View) {.signal.}
QtProperty[QVariant] id: QtProperty[QVariant] id:
read = getID read = getID
notify = idChanged notify = currentCollectibleChanged
proc getTokenID(self: View): QVariant {.slot.} = proc getTokenID(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getTokenId().toString()) return newQVariant(self.collectible.getTokenId().toString())
proc tokenIdChanged(self: View) {.signal.}
QtProperty[QVariant] tokenId: QtProperty[QVariant] tokenId:
read = getTokenID read = getTokenID
notify = tokenIdChanged notify = currentCollectibleChanged
proc getDescription(self: View): QVariant {.slot.} = proc getDescription(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getDescription()) return newQVariant(self.collectible.getDescription())
proc descriptionChanged(self: View) {.signal.}
QtProperty[QVariant] description: QtProperty[QVariant] description:
read = getDescription read = getDescription
notify = descriptionChanged notify = currentCollectibleChanged
proc getBackgroundColor(self: View): QVariant {.slot.} = proc getBackgroundColor(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getBackgroundColor()) return newQVariant(self.collectible.getBackgroundColor())
proc backgroundColorChanged(self: View) {.signal.}
QtProperty[QVariant] backgroundColor: QtProperty[QVariant] backgroundColor:
read = getBackgroundColor read = getBackgroundColor
notify = backgroundColorChanged notify = currentCollectibleChanged
proc getMediaUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getMediaUrl())
QtProperty[QVariant] mediaUrl:
read = getMediaUrl
notify = currentCollectibleChanged
proc getMediaType(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getMediaType())
QtProperty[QVariant] mediaType:
read = getMediaType
notify = currentCollectibleChanged
proc getImageUrl(self: View): QVariant {.slot.} = proc getImageUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getImageUrl()) return newQVariant(self.collectible.getImageUrl())
proc imageUrlChanged(self: View) {.signal.}
QtProperty[QVariant] imageUrl: QtProperty[QVariant] imageUrl:
read = getImageUrl read = getImageUrl
notify = imageUrlChanged notify = currentCollectibleChanged
proc getCollectionName(self: View): QVariant {.slot.} = proc getCollectionName(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getCollectionName()) return newQVariant(self.collectible.getCollectionName())
proc collectionNameChanged(self: View) {.signal.}
QtProperty[QVariant] collectionName: QtProperty[QVariant] collectionName:
read = getCollectionName read = getCollectionName
notify = collectionNameChanged notify = currentCollectibleChanged
proc getCollectionImageUrl(self: View): QVariant {.slot.} = proc getCollectionImageUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getCollectionImageUrl()) return newQVariant(self.collectible.getCollectionImageUrl())
proc collectionImageUrlChanged(self: View) {.signal.}
QtProperty[QVariant] collectionImageUrl: QtProperty[QVariant] collectionImageUrl:
read = getCollectionImageUrl read = getCollectionImageUrl
notify = collectionImageUrlChanged notify = currentCollectibleChanged
proc getPermalink(self: View): QVariant {.slot.} = proc getPermalink(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getPermalink()) return newQVariant(self.collectible.getPermalink())
proc permalinkChanged(self: View) {.signal.}
QtProperty[QVariant] permalink: QtProperty[QVariant] permalink:
read = getPermalink read = getPermalink
notify = permalinkChanged notify = currentCollectibleChanged
proc propertiesChanged(self: View) {.signal.}
proc getProperties*(self: View): QVariant {.slot.} = proc getProperties*(self: View): QVariant {.slot.} =
return newQVariant(self.propertiesModel) return newQVariant(self.propertiesModel)
QtProperty[QVariant] properties: QtProperty[QVariant] properties:
read = getProperties read = getProperties
notify = propertiesChanged notify = currentCollectibleChanged
proc rankingsChanged(self: View) {.signal.}
proc getRankings*(self: View): QVariant {.slot.} = proc getRankings*(self: View): QVariant {.slot.} =
return newQVariant(self.rankingsModel) return newQVariant(self.rankingsModel)
QtProperty[QVariant] rankings: QtProperty[QVariant] rankings:
read = getRankings read = getRankings
notify = rankingsChanged notify = currentCollectibleChanged
proc statsChanged(self: View) {.signal.}
proc getStats*(self: View): QVariant {.slot.} = proc getStats*(self: View): QVariant {.slot.} =
return newQVariant(self.statsModel) return newQVariant(self.statsModel)
QtProperty[QVariant] stats: QtProperty[QVariant] stats:
read = getStats read = getStats
notify = statsChanged notify = currentCollectibleChanged
proc update*(self: View, address: string, tokenId: string) {.slot.} = proc update*(self: View, address: string, tokenId: string) {.slot.} =
self.delegate.update(address, parse(tokenId, Uint256)) self.delegate.update(address, parse(tokenId, Uint256))
@ -190,20 +168,8 @@ QtObject:
self.networkIconUrlChanged() self.networkIconUrlChanged()
self.collectible = collectible self.collectible = collectible
self.collectionNameChanged()
self.collectionImageUrlChanged()
self.nameChanged()
self.idChanged()
self.tokenIdChanged()
self.descriptionChanged()
self.backgroundColorChanged()
self.imageUrlChanged()
self.propertiesModel.setItems(collectible.getProperties()) self.propertiesModel.setItems(collectible.getProperties())
self.propertiesChanged()
self.rankingsModel.setItems(collectible.getRankings()) self.rankingsModel.setItems(collectible.getRankings())
self.rankingsChanged()
self.statsModel.setItems(collectible.getStats()) self.statsModel.setItems(collectible.getStats())
self.statsChanged()
self.currentCollectibleChanged()

View File

@ -7,6 +7,8 @@ type
address: string address: string
tokenId: UInt256 tokenId: UInt256
name: string name: string
mediaUrl: string
mediaType: string
imageUrl: string imageUrl: string
backgroundColor: string backgroundColor: string
description: string description: string
@ -25,6 +27,8 @@ proc initItem*(
address: string, address: string,
tokenId: UInt256, tokenId: UInt256,
name: string, name: string,
mediaUrl: string,
mediaType: string,
imageUrl: string, imageUrl: string,
backgroundColor: string, backgroundColor: string,
description: string, description: string,
@ -41,6 +45,8 @@ proc initItem*(
result.address = address result.address = address
result.tokenId = tokenId result.tokenId = tokenId
result.name = if (name != ""): name else: ("#" & tokenId.toString()) result.name = if (name != ""): name else: ("#" & tokenId.toString())
result.mediaUrl = mediaUrl
result.mediaType = mediaType
result.imageUrl = imageUrl result.imageUrl = imageUrl
result.backgroundColor = if (backgroundColor == ""): "transparent" else: ("#" & backgroundColor) result.backgroundColor = if (backgroundColor == ""): "transparent" else: ("#" & backgroundColor)
result.description = description result.description = description
@ -55,7 +61,7 @@ proc initItem*(
result.isPinned = isPinned result.isPinned = isPinned
proc initItem*: Item = proc initItem*: Item =
result = initItem(-1, "", u256(0), "", "", "transparent", "Collectibles", "", @[], @[], @[], "", "", "", false) result = initItem(-1, "", u256(0), "", "", "", "", "transparent", "Collectibles", "", @[], @[], @[], "", "", "", false)
proc initLoadingItem*: Item = proc initLoadingItem*: Item =
result = initItem() result = initItem()
@ -67,6 +73,8 @@ proc `$`*(self: Item): string =
address: {self.address}, address: {self.address},
tokenId: {self.tokenId}, tokenId: {self.tokenId},
name: {self.name}, name: {self.name},
mediaUrl: {self.mediaUrl},
mediaType: {self.mediaType},
imageUrl: {self.imageUrl}, imageUrl: {self.imageUrl},
backgroundColor: {self.backgroundColor}, backgroundColor: {self.backgroundColor},
description: {self.description}, description: {self.description},
@ -90,6 +98,12 @@ proc getTokenId*(self: Item): UInt256 =
proc getName*(self: Item): string = proc getName*(self: Item): string =
return self.name return self.name
proc getMediaUrl*(self: Item): string =
return self.mediaUrl
proc getMediaType*(self: Item): string =
return self.mediaType
proc getImageUrl*(self: Item): string = proc getImageUrl*(self: Item): string =
return self.imageUrl return self.imageUrl

View File

@ -8,6 +8,8 @@ type
Address Address
TokenId TokenId
Name Name
MediaUrl
MediaType
ImageUrl ImageUrl
BackgroundColor BackgroundColor
Description Description
@ -118,6 +120,8 @@ QtObject:
CollectibleRole.Address.int:"address", CollectibleRole.Address.int:"address",
CollectibleRole.TokenId.int:"tokenId", CollectibleRole.TokenId.int:"tokenId",
CollectibleRole.Name.int:"name", CollectibleRole.Name.int:"name",
CollectibleRole.MediaUrl.int:"mediaUrl",
CollectibleRole.MediaType.int:"mediaType",
CollectibleRole.ImageUrl.int:"imageUrl", CollectibleRole.ImageUrl.int:"imageUrl",
CollectibleRole.BackgroundColor.int:"backgroundColor", CollectibleRole.BackgroundColor.int:"backgroundColor",
CollectibleRole.Description.int:"description", CollectibleRole.Description.int:"description",
@ -151,6 +155,10 @@ QtObject:
result = newQVariant(item.getTokenId().toString()) result = newQVariant(item.getTokenId().toString())
of CollectibleRole.Name: of CollectibleRole.Name:
result = newQVariant(item.getName()) result = newQVariant(item.getName())
of CollectibleRole.MediaUrl:
result = newQVariant(item.getMediaUrl())
of CollectibleRole.MediaType:
result = newQVariant(item.getMediaType())
of CollectibleRole.ImageUrl: of CollectibleRole.ImageUrl:
result = newQVariant(item.getImageUrl()) result = newQVariant(item.getImageUrl())
of CollectibleRole.BackgroundColor: of CollectibleRole.BackgroundColor:

View File

@ -3,11 +3,19 @@ import ../../../../../../app_service/service/collectible/dto
import collectibles_item, collectible_trait_item import collectibles_item, collectible_trait_item
proc collectibleToItem*(c: CollectibleDto, co: CollectionDto, isPinned: bool = false) : Item = proc collectibleToItem*(c: CollectibleDto, co: CollectionDto, isPinned: bool = false) : Item =
var mediaUrl = c.animationUrl
var mediaType = c.animationMediaType
if mediaUrl == "":
mediaUrl = c.imageUrl
mediaType = "image"
return initItem( return initItem(
c.id, c.id,
c.address, c.address,
c.tokenId, c.tokenId,
c.name, c.name,
mediaUrl,
mediaType,
c.imageUrl, c.imageUrl,
c.backgroundColor, c.backgroundColor,
c.description, c.description,

View File

@ -24,7 +24,7 @@ type CollectibleTrait* = ref object
type CollectibleDto* = ref object type CollectibleDto* = ref object
id*: int id*: int
tokenId*: Uint256 tokenId*: Uint256
address*, collectionSlug*, name*, description*, permalink*, imageThumbnailUrl*, imageUrl*, backgroundColor*: string address*, collectionSlug*, name*, description*, permalink*, imageThumbnailUrl*, imageUrl*, animationUrl*, animationMediaType*, backgroundColor*: string
properties*, rankings*, statistics*: seq[CollectibleTrait] properties*, rankings*, statistics*: seq[CollectibleTrait]
proc newCollectibleDto*: CollectibleDto = proc newCollectibleDto*: CollectibleDto =
@ -51,10 +51,27 @@ proc isNumeric(s: string): bool =
result = false result = false
proc `$`*(self: CollectionDto): string = proc `$`*(self: CollectionDto): string =
return fmt"CollectionDto(name:{self.name}, slug:{self.slug})" return fmt"""CollectionDto(
name:{self.name},
slug:{self.slug},
imageUrl:{self.imageUrl}
"""
proc `$`*(self: CollectibleDto): string = proc `$`*(self: CollectibleDto): string =
return fmt"CollectibleDto(id:{self.id}, address:{self.address}, tokenId:{self.tokenId}, collectionSlug:{self.collectionSlug}, name:{self.name}, description:{self.description}, permalink:{self.permalink}, imageUrl: {self.imageUrl}, imageThumbnailUrl: {self.imageThumbnailUrl}, backgroundColor: {self.backgroundColor})" return fmt"""CollectibleDto(
id:{self.id},
address:{self.address},
tokenId:{self.tokenId},
collectionSlug:{self.collectionSlug},
name:{self.name},
description:{self.description},
permalink:{self.permalink},
imageUrl: {self.imageUrl},
imageThumbnailUrl: {self.imageThumbnailUrl},
animationUrl: {self.animationUrl},
animationMediaType: {self.animationMediaType},
backgroundColor: {self.backgroundColor})
"""
proc getCollectionTraits*(jsonCollection: JsonNode): Table[string, CollectionTrait] = proc getCollectionTraits*(jsonCollection: JsonNode): Table[string, CollectionTrait] =
var traitList: Table[string, CollectionTrait] = initTable[string, CollectionTrait]() var traitList: Table[string, CollectionTrait] = initTable[string, CollectionTrait]()
@ -98,6 +115,8 @@ proc toCollectibleDto*(jsonAsset: JsonNode): CollectibleDto =
permalink: jsonAsset{"permalink"}.getStr, permalink: jsonAsset{"permalink"}.getStr,
imageThumbnailUrl: jsonAsset{"image_thumbnail_url"}.getStr, imageThumbnailUrl: jsonAsset{"image_thumbnail_url"}.getStr,
imageUrl: jsonAsset{"image_url"}.getStr, imageUrl: jsonAsset{"image_url"}.getStr,
animationUrl: jsonAsset{"animation_url"}.getStr,
animationMediaType: jsonAsset{"animation_media_type"}.getStr,
backgroundColor: jsonAsset{"background_color"}.getStr, backgroundColor: jsonAsset{"background_color"}.getStr,
properties: getTrait(jsonAsset, CollectibleTraitType.Properties), properties: getTrait(jsonAsset, CollectibleTraitType.Properties),
rankings: getTrait(jsonAsset, CollectibleTraitType.Rankings), rankings: getTrait(jsonAsset, CollectibleTraitType.Rankings),

View File

@ -9,6 +9,7 @@
\list \list
\li \l{StatusAddress} \li \l{StatusAddress}
\li \l{StatusAnimatedImage}
\li \l{StatusBadge} \li \l{StatusBadge}
\li \l{StatusChatInfoToolBar} \li \l{StatusChatInfoToolBar}
\li \l{StatusChatListAndCategories} \li \l{StatusChatListAndCategories}
@ -22,6 +23,7 @@
\li \l{StatusExpandableItem} \li \l{StatusExpandableItem}
\li \l{StatusFlowSelector} \li \l{StatusFlowSelector}
\li \l{StatusGroupBox} \li \l{StatusGroupBox}
\li \l{StatusImage}
\li \l{StatusImageSettings} \li \l{StatusImageSettings}
\li \l{StatusItemSelector} \li \l{StatusItemSelector}
\li \l{StatusLetterIdenticon} \li \l{StatusLetterIdenticon}
@ -37,7 +39,9 @@
\li \l{StatusNavigationListItem} \li \l{StatusNavigationListItem}
\li \l{StatusNavigationPanelHeadline} \li \l{StatusNavigationPanelHeadline}
\li \l{StatusRoundIcon} \li \l{StatusRoundIcon}
\li \l{StatusRoundedComponent}
\li \l{StatusRoundedImage} \li \l{StatusRoundedImage}
\li \l{StatusRoundedMedia}
\li \l{StatusMacWindowButtons} \li \l{StatusMacWindowButtons}
\li \l{StatusListItemBadge} \li \l{StatusListItemBadge}
\li \l{StatusExpandableItem} \li \l{StatusExpandableItem}
@ -45,6 +49,7 @@
\li \l{StatusSmartIdenticon} \li \l{StatusSmartIdenticon}
\li \l{StatusTagSelector} \li \l{StatusTagSelector}
\li \l{StatusToastMessage} \li \l{StatusToastMessage}
\li \l{StatusVideo}
\li \l{StatusWizardStepper} \li \l{StatusWizardStepper}
\endlist \endlist
*/ */

View File

@ -0,0 +1,47 @@
import QtQuick 2.13
/*!
\qmltype StatusAnimatedImage
\inherits AnimatedImage
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Draws an animated image. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-animatedimage.html}{AnimatedImage}.
This is a plain wrapper for the AnimatedImage QML type. It sets some default property values and
adds some properties common to other media type wrappers.
Example of how to use it:
\qml
StatusAnimatedImage {
width: 100
height: 100
source: "qrc:/demoapp/data/logo-test-image.gif"
}
\endqml
*/
AnimatedImage {
id: root
/*!
\qmlproperty bool StatusAnimatedImage::isLoading
\c true when the image is currently being loaded (status === AnimatedImage.Loading).
\c false otherwise.
*/
readonly property bool isLoading: status === AnimatedImage.Loading
/*!
\qmlproperty bool StatusAnimatedImage::isError
\c true when an error occurred while loading the image (status === AnimatedImage.Error).
\c false otherwise.
\note Setting an empty source is not considered an error.
*/
readonly property bool isError: status === AnimatedImage.Error
fillMode: AnimatedImage.PreserveAspectFit
}

View File

@ -0,0 +1,56 @@
import QtQuick 2.13
/*!
\qmltype StatusImage
\inherits Image
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Draws an image. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-image.html}{Image}.
This is a plain wrapper for the Image QML type. It sets some default property values and
adds some properties common to other media type wrappers.
Example of how to use it:
\qml
StatusImage {
anchors.fill: parent
width: 100
height: 100
source: "qrc:/demoapp/data/logo-test-image.png"
}
\endqml
*/
Image {
id: root
/*!
\qmlproperty bool StatusAnimatedImage::isLoading
\c true when the image is currently being loaded (status === Image.Loading).
\c false otherwise.
*/
readonly property bool isLoading: status === Image.Loading
/*!
\qmlproperty bool StatusAnimatedImage::isError
\c true when an error occurred while loading the image (status === Image.Error).
\c false otherwise.
\note Setting an empty source is not considered an error.
*/
readonly property bool isError: status === Image.Error
fillMode: Image.PreserveAspectFit
onSourceChanged: {
if (sourceSize.width < width || sourceSize.height < height) {
sourceSize = Qt.binding(() => Qt.size(width * 2, height * 2))
} else {
sourceSize = undefined
}
}
}

View File

@ -0,0 +1,84 @@
import QtQuick 2.13
import QtGraphicalEffects 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusRoundedComponent
\inherits Rectangle
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Base component . Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-rectangle.html}{Rectangle}.
This is a base component for content wrapped by a Rectangle with an optional Loading animation.
Example of how to use it:
\qml
StatusRoundedComponent {
isLoading: image.isLoading
isError: image.isError
showLoadingIndicator: true
image.source: "qrc:/demoapp/data/logo-test-image.png"
StatusImage {
id: image
anchors.fill: parent
}
}
\endqml
*/
Rectangle {
id: root
/*!
\qmlproperty bool StatusRoundedComponent::showLoadingIndicator
Set to \c true to enable the Loading animation.
Set to \c false to disable the Loading animation.
\note When enabled, the animation will be shown only when isLoading is \c true and
isError is \c false.
*/
property bool showLoadingIndicator: false
/*!
\qmlproperty bool StatusRoundedComponent::isLoading
Set to \c true when the content is loading.
Set to \c false when the content is finished loading.
*/
property bool isLoading: false
/*!
\qmlproperty bool StatusRoundedComponent::isError
Set to \c true when some error occured while loading the content.
Set to \c false when if the content's state is normal.
*/
property bool isError: false
implicitWidth: 40
implicitHeight: 40
color: "transparent"
radius: width / 2
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
x: root.x; y: root.y
width: root.width
height: root.height
radius: root.radius
}
}
Loader {
id: itemSelector
anchors.centerIn: parent
active: showLoadingIndicator && !isError && isLoading
sourceComponent: StatusLoadingIndicator {
color: Theme.palette.directColor6
}
}
}

View File

@ -3,53 +3,31 @@ import QtGraphicalEffects 1.0
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
Rectangle { /*!
id: root \qmltype StatusRoundedImage
\inherits StatusRoundedComponent
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Specialization of StatusRoundedComponent with a StatusImage as content.
property bool showLoadingIndicator: false Example of how to use it:
\qml
StatusRoundedImage {
image.source: "qrc:/demoapp/data/logo-test-image.png"
}
\endqml
*/
StatusRoundedComponent {
id: root
property alias image: image property alias image: image
implicitWidth: 40 isLoading: image.isLoading
implicitHeight: 40 isError: image.isError
color: "transparent"
radius: width / 2
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
x: root.x; y: root.y
width: root.width
height: root.height
radius: root.radius
}
}
Image { StatusImage {
id: image id: image
anchors.fill: parent
width: root.width
height: root.height
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
onSourceChanged: {
if (sourceSize.width < width || sourceSize.height < height) {
sourceSize = Qt.binding(() => Qt.size(width * 2, height * 2))
} else {
sourceSize = undefined
}
}
}
Loader {
id: itemSelector
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
active: showLoadingIndicator && image.status === Image.Loading
sourceComponent: StatusLoadingIndicator {
color: Theme.palette.directColor6
}
} }
} }

View File

@ -0,0 +1,178 @@
import QtQuick 2.15
import QtQml 2.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusRoundedMedia
\inherits StatusRoundedComponent
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Specialization of StatusRoundedComponent which handles different media types as content.
This component is a StatusRoundedComponent which is able to display several types of media using
the corresponding component according to the provided \l{https://www.iana.org/assignments/media-types/media-types.xhtml}{media type},
with the posibility of diplaying a fallback image if the media fails to load properly.
The list of supported media types and how the component to display them is chosen is the following:
- \c image
Initially, we try to display the media using StatusAnimatedImage. If loading fails, we try using
StatusImage. If that results in an error as well, we display the fallback image using StatusImage.
- \c video
Initially, we try to display the media using StatusVideo. If that results in an error,
we display the fallback image using StatusImage.
- For any other media type, we default to showing the fallback image using StatusImage.
Example of how to use it:
\qml
StatusRoundedMedia {
width: 100
height: 100
mediaUrl: "qrc:/demoapp/data/test-video.avi"
mediaType: "video"
fallbackImageUrl: "qrc:/demoapp/data/test-image.png"
}
\endqml
*/
StatusRoundedComponent {
id: root
enum MediaType {
Image,
Video,
Unknown
}
/*!
\qmlproperty url StatusRoundedMedia::mediaUrl
Used to set the source for the main media we want to display.
*/
property url mediaUrl
/*!
\qmlproperty string StatusRoundedMedia::mediaType
\l{https://www.iana.org/assignments/media-types/media-types.xhtml}{Media type} corresponding to the media pointed to by mediaUrl.
*/
property string mediaType
/*!
\qmlproperty url StatusRoundedMedia::fallbackImageUrl
Image shown in case attempting to load the media pointed to by mediaUrl results in an error.
*/
property url fallbackImageUrl
readonly property int componentMediaType: {
if (root.mediaType.startsWith("image")) {
return StatusRoundedMedia.MediaType.Image
} else if (root.mediaType.startsWith("video")) {
return StatusRoundedMedia.MediaType.Video
}
return StatusRoundedMedia.MediaType.Unknown
}
isLoading: {
if (mediaLoader.status === Loader.Ready) {
return mediaLoader.item.isLoading
}
return true
}
Binding on isError {
when: mediaLoader.status === Loader.Ready
value: mediaLoader.item ? mediaLoader.item.isError : true
delayed: true
restoreMode: Binding.RestoreBindingOrValue
}
onIsErrorChanged: {
if (isError) {
d.errorCounter = d.errorCounter + 1
processError()
}
}
QtObject {
id: d
property bool isFallback: false
property int errorCounter: 0
function reset() {
isFallback = false
errorCounter = 0
}
}
Loader {
id: mediaLoader
anchors.fill: parent
asynchronous: true
visible: !root.isError && !root.isLoading
}
Component.onCompleted: updateMediaLoader()
onMediaUrlChanged: updateMediaLoader()
onComponentMediaTypeChanged: updateMediaLoader()
onFallbackImageUrlChanged: updateMediaLoader()
function updateMediaLoader() {
d.reset()
if (root.mediaUrl !== "") {
if (componentMediaType === StatusRoundedMedia.MediaType.Image) {
mediaLoader.setSource("StatusAnimatedImage.qml",
{
"source": root.mediaUrl
});
return
} else if (componentMediaType === StatusRoundedMedia.MediaType.Video) {
mediaLoader.setSource("StatusVideo.qml",
{
"player.source": root.mediaUrl
});
return
}
}
setFallbackImage()
}
function processError() {
if (!d.isFallback) {
// AnimatedImage sometimes cannot load stuff that plan Image can, try that first
if (componentMediaType === StatusRoundedMedia.MediaType.Image && d.errorCounter <= 1) {
mediaLoader.setSource("StatusImage.qml",
{
"source": root.mediaUrl
})
return
} else if (root.fallbackImageUrl !== "") {
setFallbackImage()
return
}
}
setEmptyComponent()
}
function setFallbackImage() {
d.isFallback = true
mediaLoader.setSource("StatusImage.qml",
{
"source": root.fallbackImageUrl
})
}
function setEmptyComponent() {
mediaLoader.setSource("StatusImage.qml",
{
"source": ""
});
}
}

View File

@ -0,0 +1,50 @@
import QtQuick 2.13
import QtMultimedia 5.15
/*!
\qmltype StatusVideo
\inherits Item
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Displays a video. Bundles \l{https://doc.qt.io/qt-5/qml-qtmultimedia-mediaplayer.html}{MediaPlayer} and
\l{https://doc.qt.io/qt-5/qml-qtmultimedia-video.html}{Video}.
This is a plain wrapper for the MediaPlayer and Video QML types, providing an interface similar to the Image QML type. It
sets some default property values and adds some properties common to other media type wrappers.
Example of how to use it:
\qml
StatusVideo {
anchors.fill: parent
width: 100
height: 100
player.source: "qrc:/demoapp/data/test-video.avi"
}
\endqml
*/
Item {
id: root
readonly property bool isLoading: player.playbackState !== MediaPlayer.PlayingState
readonly property bool isError: player.status === MediaPlayer.InvalidMedia
property alias player: player
property alias output: output
MediaPlayer {
id: player
autoPlay: true
muted: true
loops: MediaPlayer.Infinite
}
VideoOutput {
id: output
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectFit
source: player
}
}

View File

@ -2,6 +2,7 @@ module StatusQ.Components
StatusAddress 0.1 StatusAddress.qml StatusAddress 0.1 StatusAddress.qml
StatusAddressPanel 0.1 StatusAddressPanel.qml StatusAddressPanel 0.1 StatusAddressPanel.qml
StatusAnimatedImage 0.1 StatusAnimatedImage.qml
StatusBadge 0.1 StatusBadge.qml StatusBadge 0.1 StatusBadge.qml
StatusChatInfoToolBar 0.1 StatusChatInfoToolBar.qml StatusChatInfoToolBar 0.1 StatusChatInfoToolBar.qml
StatusChatList 0.1 StatusChatList.qml StatusChatList 0.1 StatusChatList.qml
@ -10,6 +11,7 @@ StatusChatListCategory 0.1 StatusChatListCategory.qml
StatusChatListCategoryItem 0.1 StatusChatListCategoryItem.qml StatusChatListCategoryItem 0.1 StatusChatListCategoryItem.qml
StatusChatListAndCategories 0.1 StatusChatListAndCategories.qml StatusChatListAndCategories 0.1 StatusChatListAndCategories.qml
StatusCursorDelegate 0.1 StatusCursorDelegate.qml StatusCursorDelegate 0.1 StatusCursorDelegate.qml
StatusImage 0.1 StatusImage.qml
StatusToolBar 0.1 StatusToolBar.qml StatusToolBar 0.1 StatusToolBar.qml
StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListItem.qml StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListItem.qml
StatusEmoji 0.1 StatusEmoji.qml StatusEmoji 0.1 StatusEmoji.qml
@ -26,7 +28,9 @@ StatusMemberListItem 0.1 StatusMemberListItem.qml
StatusNavigationListItem 0.1 StatusNavigationListItem.qml StatusNavigationListItem 0.1 StatusNavigationListItem.qml
StatusNavigationPanelHeadline 0.1 StatusNavigationPanelHeadline.qml StatusNavigationPanelHeadline 0.1 StatusNavigationPanelHeadline.qml
StatusRoundIcon 0.1 StatusRoundIcon.qml StatusRoundIcon 0.1 StatusRoundIcon.qml
StatusRoundedComponent 0.1 StatusRoundedComponent.qml
StatusRoundedImage 0.1 StatusRoundedImage.qml StatusRoundedImage 0.1 StatusRoundedImage.qml
StatusRoundedMedia 0.1 StatusRoundedMedia.qml
StatusMacWindowButtons 0.1 StatusMacWindowButtons.qml StatusMacWindowButtons 0.1 StatusMacWindowButtons.qml
StatusListItemBadge 0.1 StatusListItemBadge.qml StatusListItemBadge 0.1 StatusListItemBadge.qml
StatusListItemTag 0.1 StatusListItemTag.qml StatusListItemTag 0.1 StatusListItemTag.qml
@ -40,6 +44,7 @@ StatusMessageDetails 0.1 StatusMessageDetails.qml
StatusMessageSenderDetails 0.1 StatusMessageSenderDetails.qml StatusMessageSenderDetails 0.1 StatusMessageSenderDetails.qml
StatusTagSelector 0.1 StatusTagSelector.qml StatusTagSelector 0.1 StatusTagSelector.qml
StatusToastMessage 0.1 StatusToastMessage.qml StatusToastMessage 0.1 StatusToastMessage.qml
StatusVideo 0.1 StatusVideo.qml
StatusWizardStepper 0.1 StatusWizardStepper.qml StatusWizardStepper 0.1 StatusWizardStepper.qml
StatusImageCropPanel 0.1 StatusImageCropPanel.qml StatusImageCropPanel 0.1 StatusImageCropPanel.qml
StatusColorSpace 0.0 StatusColorSpace.qml StatusColorSpace 0.0 StatusColorSpace.qml

View File

@ -12,6 +12,7 @@
<file>StatusQ/Components/qmldir</file> <file>StatusQ/Components/qmldir</file>
<file>StatusQ/Components/StatusAddress.qml</file> <file>StatusQ/Components/StatusAddress.qml</file>
<file>StatusQ/Components/StatusAddressPanel.qml</file> <file>StatusQ/Components/StatusAddressPanel.qml</file>
<file>StatusQ/Components/StatusAnimatedImage.qml</file>
<file>StatusQ/Components/StatusBadge.qml</file> <file>StatusQ/Components/StatusBadge.qml</file>
<file>StatusQ/Components/StatusCard.qml</file> <file>StatusQ/Components/StatusCard.qml</file>
<file>StatusQ/Components/StatusChatInfoToolBar.qml</file> <file>StatusQ/Components/StatusChatInfoToolBar.qml</file>
@ -19,6 +20,7 @@
<file>StatusQ/Components/StatusChatListAndCategories.qml</file> <file>StatusQ/Components/StatusChatListAndCategories.qml</file>
<file>StatusQ/Components/StatusChatListCategoryItem.qml</file> <file>StatusQ/Components/StatusChatListCategoryItem.qml</file>
<file>StatusQ/Components/StatusChatListItem.qml</file> <file>StatusQ/Components/StatusChatListItem.qml</file>
<file>StatusQ/Components/StatusImage.qml</file>
<file>StatusQ/Components/StatusToolBar.qml</file> <file>StatusQ/Components/StatusToolBar.qml</file>
<file>StatusQ/Components/StatusColorSpace.qml</file> <file>StatusQ/Components/StatusColorSpace.qml</file>
<file>StatusQ/Components/StatusCommunityCard.qml</file> <file>StatusQ/Components/StatusCommunityCard.qml</file>
@ -46,11 +48,14 @@
<file>StatusQ/Components/StatusMessageHeader.qml</file> <file>StatusQ/Components/StatusMessageHeader.qml</file>
<file>StatusQ/Components/StatusNavigationListItem.qml</file> <file>StatusQ/Components/StatusNavigationListItem.qml</file>
<file>StatusQ/Components/StatusNavigationPanelHeadline.qml</file> <file>StatusQ/Components/StatusNavigationPanelHeadline.qml</file>
<file>StatusQ/Components/StatusRoundedComponent.qml</file>
<file>StatusQ/Components/StatusRoundedImage.qml</file> <file>StatusQ/Components/StatusRoundedImage.qml</file>
<file>StatusQ/Components/StatusRoundedMedia.qml</file>
<file>StatusQ/Components/StatusRoundIcon.qml</file> <file>StatusQ/Components/StatusRoundIcon.qml</file>
<file>StatusQ/Components/StatusSmartIdenticon.qml</file> <file>StatusQ/Components/StatusSmartIdenticon.qml</file>
<file>StatusQ/Components/StatusTagSelector.qml</file> <file>StatusQ/Components/StatusTagSelector.qml</file>
<file>StatusQ/Components/StatusToastMessage.qml</file> <file>StatusQ/Components/StatusToastMessage.qml</file>
<file>StatusQ/Components/StatusVideo.qml</file>
<file>StatusQ/Components/StatusWizardStepper.qml</file> <file>StatusQ/Components/StatusWizardStepper.qml</file>
<file>StatusQ/Controls/Validators/qmldir</file> <file>StatusQ/Controls/Validators/qmldir</file>
<file>StatusQ/Controls/Validators/StatusAddressOrEnsValidator.qml</file> <file>StatusQ/Controls/Validators/StatusAddressOrEnsValidator.qml</file>

View File

@ -71,7 +71,7 @@ StatusScrollView {
width: gridView.cellWidth width: gridView.cellWidth
title: model.name ? model.name : "..." title: model.name ? model.name : "..."
subTitle: d.getStateText(model.deployState) subTitle: d.getStateText(model.deployState)
imageUrl: model.image ? model.image : "" fallbackImageUrl: model.image ? model.image : ""
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent" // TODO BACKEND backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent" // TODO BACKEND
isLoading: false isLoading: false
navigationIconVisible: true navigationIconVisible: true

View File

@ -56,7 +56,9 @@ Item {
width: gridView.cellWidth width: gridView.cellWidth
title: model.name ? model.name : "..." title: model.name ? model.name : "..."
subTitle: model.collectionName ? model.collectionName : "" subTitle: model.collectionName ? model.collectionName : ""
imageUrl: model.imageUrl ? model.imageUrl : "" mediaUrl: model.mediaUrl ? model.mediaUrl : ""
mediaType: model.mediaType ? model.mediaType : ""
fallbackImageUrl: model.imageUrl
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent" backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent"
isLoading: model.isLoading isLoading: model.isLoading

View File

@ -51,7 +51,7 @@ Item {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
spacing: 24 spacing: 24
StatusRoundedImage { StatusRoundedMedia {
id: collectibleimage id: collectibleimage
readonly property int size : root.isNarrowMode ? 132 : 253 readonly property int size : root.isNarrowMode ? 132 : 253
width: size width: size
@ -60,7 +60,9 @@ Item {
color: currentCollectible.backgroundColor color: currentCollectible.backgroundColor
border.color: Theme.palette.directColor8 border.color: Theme.palette.directColor8
border.width: 1 border.width: 1
image.source: currentCollectible.imageUrl mediaUrl: currentCollectible.mediaUrl
mediaType: currentCollectible.mediaType
fallbackImageUrl: currentCollectible.imageUrl
} }
Column { Column {

View File

@ -15,7 +15,9 @@ Control {
property string title: "" property string title: ""
property string subTitle: "" property string subTitle: ""
property string backgroundColor: "transparent" property string backgroundColor: "transparent"
property url imageUrl : "" property url mediaUrl : ""
property string mediaType: ""
property url fallbackImageUrl : ""
property bool isLoading: false property bool isLoading: false
property bool navigationIconVisible: false property bool navigationIconVisible: false
@ -35,7 +37,7 @@ Control {
contentItem: ColumnLayout { contentItem: ColumnLayout {
spacing: 0 spacing: 0
StatusRoundedImage { StatusRoundedMedia {
id: image id: image
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
@ -43,7 +45,9 @@ Control {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: width Layout.preferredHeight: width
radius: 12 radius: 12
image.source: root.imageUrl mediaUrl: root.mediaUrl
mediaType: root.mediaType
fallbackImageUrl: root.fallbackImageUrl
border.color: Theme.palette.baseColor2 border.color: Theme.palette.baseColor2
border.width: 1 border.width: 1
showLoadingIndicator: true showLoadingIndicator: true