fix(wallet): Update collectibles media management (#16080)
This commit is contained in:
parent
ef9c2598cc
commit
658fe2a5b4
|
@ -348,6 +348,14 @@ QtObject:
|
|||
read = getTwitterHandle
|
||||
notify = twitterHandleChanged
|
||||
|
||||
proc isMetadataValidChanged*(self: CollectiblesEntry) {.signal.}
|
||||
proc getIsMetaDataValid*(self: CollectiblesEntry): bool {.slot.} =
|
||||
return self.hasCollectibleData()
|
||||
|
||||
QtProperty[bool] isMetadataValid:
|
||||
read = getIsMetaDataValid
|
||||
notify = isMetadataValidChanged
|
||||
|
||||
proc updateDataIfSameID*(self: CollectiblesEntry, update: backend.Collectible): bool =
|
||||
if self.id != update.id:
|
||||
return false
|
||||
|
|
|
@ -39,8 +39,8 @@ QtObject:
|
|||
read = getDetailedEntry
|
||||
notify = detailedEntryChanged
|
||||
|
||||
proc getIsDetailedEntryLoading*(self: Controller): QVariant {.slot.} =
|
||||
return newQVariant(self.detailedEntry)
|
||||
proc getIsDetailedEntryLoading*(self: Controller): bool {.slot.} =
|
||||
return self.isDetailedEntryLoading
|
||||
|
||||
proc isDetailedEntryLoadingChanged(self: Controller) {.signal.}
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
import AppLayouts.Communities.stores 1.0 as CommunitiesStores
|
||||
import AppLayouts.Wallet 1.0
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStores
|
||||
import AppLayouts.Wallet.views.collectibles 1.0
|
||||
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0 as SharedStores
|
||||
|
||||
import Models 1.0
|
||||
import utils 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
// QtObject {
|
||||
// function isValidURL(url) {
|
||||
// return true
|
||||
// }
|
||||
|
||||
// Component.onCompleted: {
|
||||
// Utils.globalUtilsInst = this
|
||||
// }
|
||||
// Component.onDestruction: {
|
||||
// Utils.globalUtilsInst = {}
|
||||
// }
|
||||
// }
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property QtObject collectiblesModel: ManageCollectiblesModel {
|
||||
Component.onCompleted: {
|
||||
d.refreshCurrentCollectible()
|
||||
}
|
||||
}
|
||||
property var currentCollectible
|
||||
|
||||
function refreshCurrentCollectible() {
|
||||
currentCollectible = ModelUtils.get(collectiblesModel, collectibleComboBox.currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
SplitView {
|
||||
orientation: Qt.Vertical
|
||||
SplitView.fillWidth: true
|
||||
Item {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: viewLoader
|
||||
anchors.margins: -1
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: "#808080"
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: viewLoader
|
||||
anchors.fill: parent
|
||||
anchors.margins: 50
|
||||
|
||||
active: false
|
||||
|
||||
sourceComponent: CollectibleMedia {
|
||||
backgroundColor: d.currentCollectible.backgroundColor
|
||||
isCollectibleLoading: isLoadingCheckbox.checked
|
||||
isMetadataValid: !d.currentCollectible.isMetadataValid
|
||||
mediaUrl: d.currentCollectible.mediaUrl ?? ""
|
||||
fallbackImageUrl: d.currentCollectible.imageUrl
|
||||
interactive: isInteractiveCheckbox.checked
|
||||
enabled: isEnabledCheckbox.checked
|
||||
}
|
||||
Component.onCompleted: viewLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.preferredHeight: 150
|
||||
|
||||
SplitView.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
SplitView.minimumWidth: 300
|
||||
SplitView.preferredWidth: 300
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
text: "Collectible:"
|
||||
}
|
||||
ComboBox {
|
||||
id: collectibleComboBox
|
||||
Layout.fillWidth: true
|
||||
textRole: "name"
|
||||
model: d.collectiblesModel
|
||||
currentIndex: 0
|
||||
onCurrentIndexChanged: d.refreshCurrentCollectible()
|
||||
}
|
||||
CheckBox { // Loading state when model is loading, it doesn't affect internal image loading state
|
||||
id: isLoadingCheckbox
|
||||
text: "isLoading"
|
||||
checked: false
|
||||
}
|
||||
CheckBox {
|
||||
id: isInteractiveCheckbox
|
||||
text: "isInteractive"
|
||||
checked: true
|
||||
}
|
||||
CheckBox {
|
||||
id: isEnabledCheckbox
|
||||
text: "isEnabled"
|
||||
checked: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Wallet
|
|
@ -134,7 +134,7 @@ Item {
|
|||
const lvOther = findChild(controlUnderTest, "otherTokensListView")
|
||||
verify(!!lvOther)
|
||||
|
||||
tryCompare(lvOther, "count", 9)
|
||||
tryCompare(lvOther, "count", 10)
|
||||
const delegate0 = findChild(lvOther, "manageTokensDelegate-0")
|
||||
verify(!!delegate0)
|
||||
const title = delegate0.title
|
||||
|
@ -144,7 +144,7 @@ Item {
|
|||
tryCompare(notificationSpy, "count", 1)
|
||||
|
||||
// verify we now have -1 regular tokens after the "hide" operation
|
||||
tryCompare(lvOther, "count", 8)
|
||||
tryCompare(lvOther, "count", 9)
|
||||
}
|
||||
|
||||
function test_showHideCommunityGroup() {
|
||||
|
|
|
@ -64,7 +64,8 @@ ListModel {
|
|||
],
|
||||
tokenId: "403",
|
||||
twitterHandle: "@punxNotDead",
|
||||
website: "www.punxnotdead.com"
|
||||
website: "www.punxnotdead.com",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "pp23",
|
||||
|
@ -109,7 +110,8 @@ ListModel {
|
|||
],
|
||||
tokenId: "123",
|
||||
twitterHandle: "@pepepunks",
|
||||
website: "www.pepepunks.com"
|
||||
website: "www.pepepunks.com",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "34545656768",
|
||||
|
@ -154,7 +156,8 @@ ListModel {
|
|||
],
|
||||
tokenId: "7123",
|
||||
twitterHandle: "@kitties",
|
||||
website: "www.kitties.com"
|
||||
website: "www.kitties.com",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "123456",
|
||||
|
@ -202,7 +205,8 @@ ListModel {
|
|||
],
|
||||
tokenId: "403123",
|
||||
twitterHandle: "",
|
||||
website: "www.kitties.com"
|
||||
website: "www.kitties.com",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "12345645459537432",
|
||||
|
@ -252,7 +256,8 @@ ListModel {
|
|||
],
|
||||
tokenId: "1",
|
||||
twitterHandle: "@kitties",
|
||||
website: ""
|
||||
website: "",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "pp21",
|
||||
|
@ -289,7 +294,8 @@ ListModel {
|
|||
],
|
||||
tokenId: "12568",
|
||||
twitterHandle: "@pepepunks",
|
||||
website: "www.pepepunks.com"
|
||||
website: "www.pepepunks.com",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "lp#666a",
|
||||
|
@ -326,8 +332,42 @@ ListModel {
|
|||
],
|
||||
tokenId: "1445",
|
||||
twitterHandle: "@lonelyPanda",
|
||||
website: "www.lonelyPanda.com"
|
||||
website: "www.lonelyPanda.com",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "invalid#123",
|
||||
chainId: 421613,
|
||||
userHas: 0,
|
||||
name: "",
|
||||
collectionUid: "",
|
||||
collectionName: "",
|
||||
collectionImageUrl: "",
|
||||
communityId: "",
|
||||
communityName: "",
|
||||
communityImage: "",
|
||||
imageUrl: "",
|
||||
isLoading: false,
|
||||
backgroundColor: "",
|
||||
permalink:"",
|
||||
domain:"",
|
||||
ownership: [
|
||||
{
|
||||
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
|
||||
balance: "1",
|
||||
txTimestamp: 19
|
||||
},
|
||||
],
|
||||
networkShortName: "OPT",
|
||||
networkColor: "red",
|
||||
networkIconUrl: ModelsData.networks.optimism,
|
||||
description: "",
|
||||
traits: [],
|
||||
tokenId: "2121",
|
||||
twitterHandle: "",
|
||||
website: "",
|
||||
isMetadataValid: false
|
||||
}
|
||||
]
|
||||
|
||||
readonly property var communityData: [
|
||||
|
@ -356,7 +396,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.optimism,
|
||||
description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)",
|
||||
traits: [],
|
||||
tokenId: "4"
|
||||
tokenId: "4",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "691",
|
||||
|
@ -383,7 +424,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.optimism,
|
||||
description: "Bearz is a community for all the ferocious Bearz! Welcome onboard and enjoy :)",
|
||||
traits: [],
|
||||
tokenId: "3"
|
||||
tokenId: "3",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "8876",
|
||||
|
@ -410,7 +452,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.ethereum,
|
||||
description: "Bearz is a community for all the ferocious Bearz! Welcome onboard and enjoy :)",
|
||||
traits: [],
|
||||
tokenId: "341"
|
||||
tokenId: "341",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "fp#3195",
|
||||
|
@ -437,7 +480,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.ethereum,
|
||||
description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)",
|
||||
traits: [],
|
||||
tokenId: "765"
|
||||
tokenId: "765",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "fp#4297",
|
||||
|
@ -464,7 +508,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.ethereum,
|
||||
description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)",
|
||||
traits: [],
|
||||
tokenId: "166"
|
||||
tokenId: "166",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "fp#909",
|
||||
|
@ -491,7 +536,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.optimism,
|
||||
description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)",
|
||||
traits: [],
|
||||
tokenId: "1111"
|
||||
tokenId: "1111",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "lb#666",
|
||||
|
@ -523,7 +569,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.optimism,
|
||||
description: "Bearz is a community for all the ferocious Bearz! Welcome onboard and enjoy",
|
||||
traits: [],
|
||||
tokenId: "6"
|
||||
tokenId: "6",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "lb#777",
|
||||
|
@ -550,7 +597,8 @@ ListModel {
|
|||
networkIconUrl: ModelsData.networks.optimism,
|
||||
description: "Lonely Turtle is a community for all of us to talk and communicate! Welcome onboard and enjoy",
|
||||
traits: [],
|
||||
tokenId: "7"
|
||||
tokenId: "7",
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "ID-Custom",
|
||||
|
@ -575,7 +623,8 @@ ListModel {
|
|||
communityId: "",
|
||||
networkShortName: "ARB",
|
||||
networkColor: "blue",
|
||||
networkIconUrl: ModelsData.networks.arbitrum
|
||||
networkIconUrl: ModelsData.networks.arbitrum,
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "ID-MissingMetadata",
|
||||
|
@ -600,7 +649,8 @@ ListModel {
|
|||
communityId: "",
|
||||
networkShortName: "OPT",
|
||||
networkColor: "red",
|
||||
networkIconUrl: ModelsData.networks.optimism
|
||||
networkIconUrl: ModelsData.networks.optimism,
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "ID-Community1",
|
||||
|
@ -625,7 +675,8 @@ ListModel {
|
|||
communityId: "community-id-1",
|
||||
networkShortName: "OPT",
|
||||
networkColor: "red",
|
||||
networkIconUrl: ModelsData.networks.optimism
|
||||
networkIconUrl: ModelsData.networks.optimism,
|
||||
isMetadataValid: true
|
||||
},
|
||||
{
|
||||
uid: "ID-Community-Unknown",
|
||||
|
@ -650,7 +701,8 @@ ListModel {
|
|||
communityId: "community-id-unknown",
|
||||
networkShortName: "OPT",
|
||||
networkColor: "red",
|
||||
networkIconUrl: ModelsData.networks.optimism
|
||||
networkIconUrl: ModelsData.networks.optimism,
|
||||
isMetadataValid: true
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -238,7 +238,7 @@ StatusScrollView {
|
|||
delegate: CollectibleView {
|
||||
height: collectiblesGrid.cellHeight
|
||||
width: collectiblesGrid.cellWidth
|
||||
title: model.name ? model.name : "..."
|
||||
title: model.name ?? ""
|
||||
subTitle: deployState === Constants.ContractTransactionStatus.Completed ?
|
||||
d.getRemainingInfo(model.privilegesLevel === Constants.TokenPrivilegesLevel.Owner,
|
||||
model.privilegesLevel === Constants.TokenPrivilegesLevel.TMaster,
|
||||
|
@ -251,6 +251,7 @@ StatusScrollView {
|
|||
fallbackImageUrl: model.image ? model.image : ""
|
||||
backgroundColor: "transparent"
|
||||
isLoading: false
|
||||
isMetadataValid: true
|
||||
navigationIconVisible: false
|
||||
privilegesLevel: model.privilegesLevel
|
||||
ornamentColor: model.color
|
||||
|
|
|
@ -92,7 +92,7 @@ ColumnLayout {
|
|||
clear()
|
||||
if (d.isLoading) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
append({ isLoading: true })
|
||||
append({ isLoading: true, name: qsTr("Loading collectible...") })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -497,13 +497,14 @@ ColumnLayout {
|
|||
CollectibleView {
|
||||
width: d.cellWidth
|
||||
height: isCommunityCollectible ? d.communityCellHeight : d.cellHeight
|
||||
title: model.name ? model.name : "..."
|
||||
title: model.name ?? ""
|
||||
subTitle: model.collectionName ? model.collectionName : model.collectionUid ? model.collectionUid : ""
|
||||
mediaUrl: model.mediaUrl ?? ""
|
||||
mediaType: model.mediaType ?? ""
|
||||
fallbackImageUrl: model.imageUrl ?? ""
|
||||
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent"
|
||||
isLoading: !!model.isLoading
|
||||
isMetadataValid: !!model.isMetadataValid
|
||||
privilegesLevel: model.communityPrivilegesLevel ?? Constants.TokenPrivilegesLevel.Community
|
||||
ornamentColor: model.communityColor ?? "transparent"
|
||||
communityId: model.communityId ?? ""
|
||||
|
|
|
@ -361,11 +361,11 @@ Item {
|
|||
|
||||
Component {
|
||||
id: collectibleimageComponent
|
||||
StatusRoundedMedia {
|
||||
CollectibleMedia {
|
||||
id: collectibleImage
|
||||
readonly property bool isEmpty: !mediaUrl.toString() && !fallbackImageUrl.toString()
|
||||
radius: Style.current.radius
|
||||
color: isError || isEmpty ? Theme.palette.baseColor5 : collectible.backgroundColor
|
||||
backgroundColor: collectible.backgroundColor
|
||||
isCollectibleLoading: root.isCollectibleLoading
|
||||
isMetadataValid: !collectible.isMetadataValid
|
||||
mediaUrl: collectible.mediaUrl ?? ""
|
||||
mediaType: !!collectible ? (modelIndex > 0 && collectible.mediaType.startsWith("video")) ? "" : collectible.mediaType: ""
|
||||
fallbackImageUrl: collectible.imageUrl
|
||||
|
@ -377,29 +377,6 @@ Item {
|
|||
onOpenImageContextMenu: (url, isGif) => Global.openMenu(imageContextMenu, collectibleImage, { imageSource: url, isGif: isGif, isVideo: false })
|
||||
onOpenVideoContextMenu: (url) => Global.openMenu(imageContextMenu, collectibleImage, { imageSource: url, url: url, isVideo: true, isGif: false })
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: collectibleImage.isLoading
|
||||
sourceComponent: LoadingComponent {radius: collectibleImage.radius}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: collectibleImage.isError || collectibleImage.isEmpty
|
||||
sourceComponent: LoadingErrorComponent {
|
||||
radius: collectibleImage.radius
|
||||
text: {
|
||||
if (collectibleImage.isError && collectibleImage.componentMediaType === StatusRoundedMedia.MediaType.Unkown) {
|
||||
return qsTr("Unsupported\nfile format")
|
||||
}
|
||||
if (!collectible.description && !collectible.name) {
|
||||
return qsTr("Info can't\nbe fetched")
|
||||
}
|
||||
return qsTr("Failed\nto load")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: imageContextMenu
|
||||
ImageContextMenu {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import QtQuick 2.14
|
||||
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
StatusRoundedMedia {
|
||||
id: root
|
||||
|
||||
readonly property bool isEmpty: !mediaUrl.toString() && !fallbackImageUrl.toString()
|
||||
property color backgroundColor: Theme.palette.baseColor5
|
||||
property bool isCollectibleLoading: false
|
||||
property bool isMetadataValid: false
|
||||
|
||||
radius: Style.current.radius
|
||||
color: isError || isEmpty ? Theme.palette.baseColor5 : backgroundColor
|
||||
|
||||
Loader {
|
||||
id: loadingCompLoader
|
||||
anchors.fill: parent
|
||||
active: root.isCollectibleLoading || root.isLoading
|
||||
sourceComponent: LoadingComponent {radius: root.radius}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: (root.isError || root.isEmpty) && !loadingCompLoader.active
|
||||
sourceComponent: LoadingErrorComponent {
|
||||
radius: root.radius
|
||||
text: {
|
||||
if (root.isError && root.componentMediaType === StatusRoundedMedia.MediaType.Unkown) {
|
||||
return qsTr("Unsupported\nfile format")
|
||||
}
|
||||
if (!root.isMetadataValid) {
|
||||
return qsTr("Info can't\nbe fetched")
|
||||
}
|
||||
return qsTr("Failed\nto load")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ Control {
|
|||
id: root
|
||||
|
||||
property string title: ""
|
||||
property string unknownTitle: "..."
|
||||
property string subTitle: ""
|
||||
property alias subTitleColor: subTitleItem.customColor
|
||||
property string backgroundColor: "transparent"
|
||||
|
@ -24,6 +25,7 @@ Control {
|
|||
property url fallbackImageUrl : ""
|
||||
property bool isLoading: false
|
||||
property bool navigationIconVisible: false
|
||||
property bool isMetadataValid: false
|
||||
property string communityId: ""
|
||||
property string communityName
|
||||
property string communityImage
|
||||
|
@ -77,7 +79,7 @@ Control {
|
|||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
StatusRoundedMedia {
|
||||
CollectibleMedia {
|
||||
id: image
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
@ -85,21 +87,16 @@ Control {
|
|||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width
|
||||
|
||||
backgroundColor: root.isLoading ? "transparent" : root.backgroundColor
|
||||
visible: !specialCollectible.visible
|
||||
radius: Style.current.radius
|
||||
isMetadataValid: root.isMetadataValid
|
||||
mediaUrl: root.mediaUrl
|
||||
mediaType: root.mediaType
|
||||
fallbackImageUrl: root.fallbackImageUrl
|
||||
showLoadingIndicator: true
|
||||
color: root.isLoading ? "transparent" : root.backgroundColor
|
||||
isCollectibleLoading: root.isLoading
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: root.isLoading
|
||||
sourceComponent: LoadingComponent {radius: image.radius}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
@ -150,7 +147,7 @@ Control {
|
|||
customColor: Theme.palette.directColor1
|
||||
font.weight: Font.DemiBold
|
||||
elide: Text.ElideRight
|
||||
text: root.isLoading ? Constants.dummyText : root.title
|
||||
text: root.isLoading ? Constants.dummyText : root.title || root.unknownTitle
|
||||
loading: root.isLoading
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
CollectibleDetailView 1.0 CollectibleDetailView.qml
|
||||
CollectibleView 1.0 CollectibleView.qml
|
||||
CollectibleMedia 1.0 CollectibleMedia.qml
|
||||
|
|
Loading…
Reference in New Issue