diff --git a/src/app/modules/main/wallet_section/collectibles/current_collectible/view.nim b/src/app/modules/main/wallet_section/collectibles/current_collectible/view.nim
index 180dbf8fc7..6a9e33f475 100644
--- a/src/app/modules/main/wallet_section/collectibles/current_collectible/view.nim
+++ b/src/app/modules/main/wallet_section/collectibles/current_collectible/view.nim
@@ -65,113 +65,91 @@ QtObject:
read = getNetworkIconUrl
notify = networkIconUrlChanged
+ proc currentCollectibleChanged(self: View) {.signal.}
+
proc getName(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getName())
-
- proc nameChanged(self: View) {.signal.}
-
QtProperty[QVariant] name:
read = getName
- notify = nameChanged
+ notify = currentCollectibleChanged
proc getID(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getId())
-
- proc idChanged(self: View) {.signal.}
-
QtProperty[QVariant] id:
read = getID
- notify = idChanged
+ notify = currentCollectibleChanged
proc getTokenID(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getTokenId().toString())
-
- proc tokenIdChanged(self: View) {.signal.}
-
QtProperty[QVariant] tokenId:
read = getTokenID
- notify = tokenIdChanged
+ notify = currentCollectibleChanged
proc getDescription(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getDescription())
-
- proc descriptionChanged(self: View) {.signal.}
-
QtProperty[QVariant] description:
read = getDescription
- notify = descriptionChanged
+ notify = currentCollectibleChanged
proc getBackgroundColor(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getBackgroundColor())
-
- proc backgroundColorChanged(self: View) {.signal.}
-
QtProperty[QVariant] backgroundColor:
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.} =
return newQVariant(self.collectible.getImageUrl())
-
- proc imageUrlChanged(self: View) {.signal.}
-
QtProperty[QVariant] imageUrl:
read = getImageUrl
- notify = imageUrlChanged
+ notify = currentCollectibleChanged
proc getCollectionName(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getCollectionName())
-
- proc collectionNameChanged(self: View) {.signal.}
-
QtProperty[QVariant] collectionName:
read = getCollectionName
- notify = collectionNameChanged
+ notify = currentCollectibleChanged
proc getCollectionImageUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getCollectionImageUrl())
-
- proc collectionImageUrlChanged(self: View) {.signal.}
-
QtProperty[QVariant] collectionImageUrl:
read = getCollectionImageUrl
- notify = collectionImageUrlChanged
+ notify = currentCollectibleChanged
proc getPermalink(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getPermalink())
-
- proc permalinkChanged(self: View) {.signal.}
-
QtProperty[QVariant] permalink:
read = getPermalink
- notify = permalinkChanged
-
- proc propertiesChanged(self: View) {.signal.}
+ notify = currentCollectibleChanged
proc getProperties*(self: View): QVariant {.slot.} =
return newQVariant(self.propertiesModel)
-
QtProperty[QVariant] properties:
read = getProperties
- notify = propertiesChanged
-
- proc rankingsChanged(self: View) {.signal.}
+ notify = currentCollectibleChanged
proc getRankings*(self: View): QVariant {.slot.} =
return newQVariant(self.rankingsModel)
-
QtProperty[QVariant] rankings:
read = getRankings
- notify = rankingsChanged
-
- proc statsChanged(self: View) {.signal.}
+ notify = currentCollectibleChanged
proc getStats*(self: View): QVariant {.slot.} =
return newQVariant(self.statsModel)
-
QtProperty[QVariant] stats:
read = getStats
- notify = statsChanged
+ notify = currentCollectibleChanged
proc update*(self: View, address: string, tokenId: string) {.slot.} =
self.delegate.update(address, parse(tokenId, Uint256))
@@ -190,20 +168,8 @@ QtObject:
self.networkIconUrlChanged()
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.propertiesChanged()
-
self.rankingsModel.setItems(collectible.getRankings())
- self.rankingsChanged()
-
self.statsModel.setItems(collectible.getStats())
- self.statsChanged()
+
+ self.currentCollectibleChanged()
\ No newline at end of file
diff --git a/src/app/modules/main/wallet_section/collectibles/models/collectibles_item.nim b/src/app/modules/main/wallet_section/collectibles/models/collectibles_item.nim
index 2a4cc4f815..b61c4c199e 100644
--- a/src/app/modules/main/wallet_section/collectibles/models/collectibles_item.nim
+++ b/src/app/modules/main/wallet_section/collectibles/models/collectibles_item.nim
@@ -7,6 +7,8 @@ type
address: string
tokenId: UInt256
name: string
+ mediaUrl: string
+ mediaType: string
imageUrl: string
backgroundColor: string
description: string
@@ -25,6 +27,8 @@ proc initItem*(
address: string,
tokenId: UInt256,
name: string,
+ mediaUrl: string,
+ mediaType: string,
imageUrl: string,
backgroundColor: string,
description: string,
@@ -41,6 +45,8 @@ proc initItem*(
result.address = address
result.tokenId = tokenId
result.name = if (name != ""): name else: ("#" & tokenId.toString())
+ result.mediaUrl = mediaUrl
+ result.mediaType = mediaType
result.imageUrl = imageUrl
result.backgroundColor = if (backgroundColor == ""): "transparent" else: ("#" & backgroundColor)
result.description = description
@@ -55,7 +61,7 @@ proc initItem*(
result.isPinned = isPinned
proc initItem*: Item =
- result = initItem(-1, "", u256(0), "", "", "transparent", "Collectibles", "", @[], @[], @[], "", "", "", false)
+ result = initItem(-1, "", u256(0), "", "", "", "", "transparent", "Collectibles", "", @[], @[], @[], "", "", "", false)
proc initLoadingItem*: Item =
result = initItem()
@@ -67,6 +73,8 @@ proc `$`*(self: Item): string =
address: {self.address},
tokenId: {self.tokenId},
name: {self.name},
+ mediaUrl: {self.mediaUrl},
+ mediaType: {self.mediaType},
imageUrl: {self.imageUrl},
backgroundColor: {self.backgroundColor},
description: {self.description},
@@ -90,6 +98,12 @@ proc getTokenId*(self: Item): UInt256 =
proc getName*(self: Item): string =
return self.name
+proc getMediaUrl*(self: Item): string =
+ return self.mediaUrl
+
+proc getMediaType*(self: Item): string =
+ return self.mediaType
+
proc getImageUrl*(self: Item): string =
return self.imageUrl
diff --git a/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim b/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim
index 34702ead07..188e8bbd9f 100644
--- a/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim
+++ b/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim
@@ -8,6 +8,8 @@ type
Address
TokenId
Name
+ MediaUrl
+ MediaType
ImageUrl
BackgroundColor
Description
@@ -118,6 +120,8 @@ QtObject:
CollectibleRole.Address.int:"address",
CollectibleRole.TokenId.int:"tokenId",
CollectibleRole.Name.int:"name",
+ CollectibleRole.MediaUrl.int:"mediaUrl",
+ CollectibleRole.MediaType.int:"mediaType",
CollectibleRole.ImageUrl.int:"imageUrl",
CollectibleRole.BackgroundColor.int:"backgroundColor",
CollectibleRole.Description.int:"description",
@@ -151,6 +155,10 @@ QtObject:
result = newQVariant(item.getTokenId().toString())
of CollectibleRole.Name:
result = newQVariant(item.getName())
+ of CollectibleRole.MediaUrl:
+ result = newQVariant(item.getMediaUrl())
+ of CollectibleRole.MediaType:
+ result = newQVariant(item.getMediaType())
of CollectibleRole.ImageUrl:
result = newQVariant(item.getImageUrl())
of CollectibleRole.BackgroundColor:
diff --git a/src/app/modules/main/wallet_section/collectibles/models/collectibles_utils.nim b/src/app/modules/main/wallet_section/collectibles/models/collectibles_utils.nim
index 74259f600b..6f1a8bdfc2 100644
--- a/src/app/modules/main/wallet_section/collectibles/models/collectibles_utils.nim
+++ b/src/app/modules/main/wallet_section/collectibles/models/collectibles_utils.nim
@@ -3,11 +3,19 @@ import ../../../../../../app_service/service/collectible/dto
import collectibles_item, collectible_trait_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(
c.id,
c.address,
c.tokenId,
c.name,
+ mediaUrl,
+ mediaType,
c.imageUrl,
c.backgroundColor,
c.description,
diff --git a/src/app_service/service/collectible/dto.nim b/src/app_service/service/collectible/dto.nim
index 693a3683de..edea6dd7b3 100644
--- a/src/app_service/service/collectible/dto.nim
+++ b/src/app_service/service/collectible/dto.nim
@@ -24,7 +24,7 @@ type CollectibleTrait* = ref object
type CollectibleDto* = ref object
id*: int
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]
proc newCollectibleDto*: CollectibleDto =
@@ -51,10 +51,27 @@ proc isNumeric(s: string): bool =
result = false
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 =
- 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] =
var traitList: Table[string, CollectionTrait] = initTable[string, CollectionTrait]()
@@ -98,6 +115,8 @@ proc toCollectibleDto*(jsonAsset: JsonNode): CollectibleDto =
permalink: jsonAsset{"permalink"}.getStr,
imageThumbnailUrl: jsonAsset{"image_thumbnail_url"}.getStr,
imageUrl: jsonAsset{"image_url"}.getStr,
+ animationUrl: jsonAsset{"animation_url"}.getStr,
+ animationMediaType: jsonAsset{"animation_media_type"}.getStr,
backgroundColor: jsonAsset{"background_color"}.getStr,
properties: getTrait(jsonAsset, CollectibleTraitType.Properties),
rankings: getTrait(jsonAsset, CollectibleTraitType.Rankings),
diff --git a/ui/StatusQ/doc/src/statusqcomponents.qdoc b/ui/StatusQ/doc/src/statusqcomponents.qdoc
index 28382c6610..d07ffd11db 100644
--- a/ui/StatusQ/doc/src/statusqcomponents.qdoc
+++ b/ui/StatusQ/doc/src/statusqcomponents.qdoc
@@ -9,6 +9,7 @@
\list
\li \l{StatusAddress}
+ \li \l{StatusAnimatedImage}
\li \l{StatusBadge}
\li \l{StatusChatInfoToolBar}
\li \l{StatusChatListAndCategories}
@@ -22,6 +23,7 @@
\li \l{StatusExpandableItem}
\li \l{StatusFlowSelector}
\li \l{StatusGroupBox}
+ \li \l{StatusImage}
\li \l{StatusImageSettings}
\li \l{StatusItemSelector}
\li \l{StatusLetterIdenticon}
@@ -37,7 +39,9 @@
\li \l{StatusNavigationListItem}
\li \l{StatusNavigationPanelHeadline}
\li \l{StatusRoundIcon}
+ \li \l{StatusRoundedComponent}
\li \l{StatusRoundedImage}
+ \li \l{StatusRoundedMedia}
\li \l{StatusMacWindowButtons}
\li \l{StatusListItemBadge}
\li \l{StatusExpandableItem}
@@ -45,6 +49,7 @@
\li \l{StatusSmartIdenticon}
\li \l{StatusTagSelector}
\li \l{StatusToastMessage}
+ \li \l{StatusVideo}
\li \l{StatusWizardStepper}
\endlist
*/
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusAnimatedImage.qml b/ui/StatusQ/src/StatusQ/Components/StatusAnimatedImage.qml
new file mode 100644
index 0000000000..d1d3959446
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusAnimatedImage.qml
@@ -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
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusImage.qml b/ui/StatusQ/src/StatusQ/Components/StatusImage.qml
new file mode 100644
index 0000000000..183be603e5
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusImage.qml
@@ -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
+ }
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusRoundedComponent.qml b/ui/StatusQ/src/StatusQ/Components/StatusRoundedComponent.qml
new file mode 100644
index 0000000000..f430f6c92a
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusRoundedComponent.qml
@@ -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
+ }
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusRoundedImage.qml b/ui/StatusQ/src/StatusQ/Components/StatusRoundedImage.qml
index 63e2e51229..043fdce23a 100644
--- a/ui/StatusQ/src/StatusQ/Components/StatusRoundedImage.qml
+++ b/ui/StatusQ/src/StatusQ/Components/StatusRoundedImage.qml
@@ -3,53 +3,31 @@ import QtGraphicalEffects 1.0
import StatusQ.Core 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
- 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
- }
- }
+ isLoading: image.isLoading
+ isError: image.isError
- Image {
+ StatusImage {
id: image
-
- 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
- }
+ anchors.fill: parent
}
}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusRoundedMedia.qml b/ui/StatusQ/src/StatusQ/Components/StatusRoundedMedia.qml
new file mode 100644
index 0000000000..55064e662d
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusRoundedMedia.qml
@@ -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": ""
+ });
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusVideo.qml b/ui/StatusQ/src/StatusQ/Components/StatusVideo.qml
new file mode 100644
index 0000000000..c367a2be3b
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusVideo.qml
@@ -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
+ }
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/qmldir b/ui/StatusQ/src/StatusQ/Components/qmldir
index a0b668cbad..dccb3cde5d 100644
--- a/ui/StatusQ/src/StatusQ/Components/qmldir
+++ b/ui/StatusQ/src/StatusQ/Components/qmldir
@@ -2,6 +2,7 @@ module StatusQ.Components
StatusAddress 0.1 StatusAddress.qml
StatusAddressPanel 0.1 StatusAddressPanel.qml
+StatusAnimatedImage 0.1 StatusAnimatedImage.qml
StatusBadge 0.1 StatusBadge.qml
StatusChatInfoToolBar 0.1 StatusChatInfoToolBar.qml
StatusChatList 0.1 StatusChatList.qml
@@ -10,6 +11,7 @@ StatusChatListCategory 0.1 StatusChatListCategory.qml
StatusChatListCategoryItem 0.1 StatusChatListCategoryItem.qml
StatusChatListAndCategories 0.1 StatusChatListAndCategories.qml
StatusCursorDelegate 0.1 StatusCursorDelegate.qml
+StatusImage 0.1 StatusImage.qml
StatusToolBar 0.1 StatusToolBar.qml
StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListItem.qml
StatusEmoji 0.1 StatusEmoji.qml
@@ -26,7 +28,9 @@ StatusMemberListItem 0.1 StatusMemberListItem.qml
StatusNavigationListItem 0.1 StatusNavigationListItem.qml
StatusNavigationPanelHeadline 0.1 StatusNavigationPanelHeadline.qml
StatusRoundIcon 0.1 StatusRoundIcon.qml
+StatusRoundedComponent 0.1 StatusRoundedComponent.qml
StatusRoundedImage 0.1 StatusRoundedImage.qml
+StatusRoundedMedia 0.1 StatusRoundedMedia.qml
StatusMacWindowButtons 0.1 StatusMacWindowButtons.qml
StatusListItemBadge 0.1 StatusListItemBadge.qml
StatusListItemTag 0.1 StatusListItemTag.qml
@@ -40,6 +44,7 @@ StatusMessageDetails 0.1 StatusMessageDetails.qml
StatusMessageSenderDetails 0.1 StatusMessageSenderDetails.qml
StatusTagSelector 0.1 StatusTagSelector.qml
StatusToastMessage 0.1 StatusToastMessage.qml
+StatusVideo 0.1 StatusVideo.qml
StatusWizardStepper 0.1 StatusWizardStepper.qml
StatusImageCropPanel 0.1 StatusImageCropPanel.qml
StatusColorSpace 0.0 StatusColorSpace.qml
diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc
index 6ad91c20be..9dc951deb2 100644
--- a/ui/StatusQ/src/statusq.qrc
+++ b/ui/StatusQ/src/statusq.qrc
@@ -12,6 +12,7 @@
StatusQ/Components/qmldir
StatusQ/Components/StatusAddress.qml
StatusQ/Components/StatusAddressPanel.qml
+ StatusQ/Components/StatusAnimatedImage.qml
StatusQ/Components/StatusBadge.qml
StatusQ/Components/StatusCard.qml
StatusQ/Components/StatusChatInfoToolBar.qml
@@ -19,6 +20,7 @@
StatusQ/Components/StatusChatListAndCategories.qml
StatusQ/Components/StatusChatListCategoryItem.qml
StatusQ/Components/StatusChatListItem.qml
+ StatusQ/Components/StatusImage.qml
StatusQ/Components/StatusToolBar.qml
StatusQ/Components/StatusColorSpace.qml
StatusQ/Components/StatusCommunityCard.qml
@@ -46,11 +48,14 @@
StatusQ/Components/StatusMessageHeader.qml
StatusQ/Components/StatusNavigationListItem.qml
StatusQ/Components/StatusNavigationPanelHeadline.qml
+ StatusQ/Components/StatusRoundedComponent.qml
StatusQ/Components/StatusRoundedImage.qml
+ StatusQ/Components/StatusRoundedMedia.qml
StatusQ/Components/StatusRoundIcon.qml
StatusQ/Components/StatusSmartIdenticon.qml
StatusQ/Components/StatusTagSelector.qml
StatusQ/Components/StatusToastMessage.qml
+ StatusQ/Components/StatusVideo.qml
StatusQ/Components/StatusWizardStepper.qml
StatusQ/Controls/Validators/qmldir
StatusQ/Controls/Validators/StatusAddressOrEnsValidator.qml
diff --git a/ui/app/AppLayouts/Chat/views/communities/CommunityMintedTokensView.qml b/ui/app/AppLayouts/Chat/views/communities/CommunityMintedTokensView.qml
index 968e608b29..db6af9cfa7 100644
--- a/ui/app/AppLayouts/Chat/views/communities/CommunityMintedTokensView.qml
+++ b/ui/app/AppLayouts/Chat/views/communities/CommunityMintedTokensView.qml
@@ -71,7 +71,7 @@ StatusScrollView {
width: gridView.cellWidth
title: model.name ? model.name : "..."
subTitle: d.getStateText(model.deployState)
- imageUrl: model.image ? model.image : ""
+ fallbackImageUrl: model.image ? model.image : ""
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent" // TODO BACKEND
isLoading: false
navigationIconVisible: true
diff --git a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml
index 0d606c2e13..fc7439713c 100644
--- a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml
+++ b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml
@@ -56,7 +56,9 @@ Item {
width: gridView.cellWidth
title: model.name ? model.name : "..."
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"
isLoading: model.isLoading
diff --git a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml
index 7198dee693..e54dcdc8c9 100644
--- a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml
+++ b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml
@@ -51,7 +51,7 @@ Item {
Layout.preferredWidth: parent.width
spacing: 24
- StatusRoundedImage {
+ StatusRoundedMedia {
id: collectibleimage
readonly property int size : root.isNarrowMode ? 132 : 253
width: size
@@ -60,7 +60,9 @@ Item {
color: currentCollectible.backgroundColor
border.color: Theme.palette.directColor8
border.width: 1
- image.source: currentCollectible.imageUrl
+ mediaUrl: currentCollectible.mediaUrl
+ mediaType: currentCollectible.mediaType
+ fallbackImageUrl: currentCollectible.imageUrl
}
Column {
diff --git a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml
index d9d371c83f..b401466b87 100644
--- a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml
+++ b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml
@@ -15,7 +15,9 @@ Control {
property string title: ""
property string subTitle: ""
property string backgroundColor: "transparent"
- property url imageUrl : ""
+ property url mediaUrl : ""
+ property string mediaType: ""
+ property url fallbackImageUrl : ""
property bool isLoading: false
property bool navigationIconVisible: false
@@ -35,7 +37,7 @@ Control {
contentItem: ColumnLayout {
spacing: 0
- StatusRoundedImage {
+ StatusRoundedMedia {
id: image
Layout.alignment: Qt.AlignHCenter
@@ -43,7 +45,9 @@ Control {
Layout.fillWidth: true
Layout.preferredHeight: width
radius: 12
- image.source: root.imageUrl
+ mediaUrl: root.mediaUrl
+ mediaType: root.mediaType
+ fallbackImageUrl: root.fallbackImageUrl
border.color: Theme.palette.baseColor2
border.width: 1
showLoadingIndicator: true