diff --git a/ui/StatusQ b/ui/StatusQ index 01624316c2..db0f8e9fb5 160000 --- a/ui/StatusQ +++ b/ui/StatusQ @@ -1 +1 @@ -Subproject commit 01624316c2349f9179514c06a84e5e941823a2fc +Subproject commit db0f8e9fb53863998403e3f6e7b3a2a62f173b4f diff --git a/ui/app/AppLayouts/Chat/controls/community/ExtendedDropdownContent.qml b/ui/app/AppLayouts/Chat/controls/community/ExtendedDropdownContent.qml new file mode 100644 index 0000000000..1fa40bdc22 --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/ExtendedDropdownContent.qml @@ -0,0 +1,277 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.14 + +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Popups 0.1 +import StatusQ.Core 0.1 + +import shared.panels 1.0 + +ColumnLayout { + id: root + + property var store + property int type: ExtendedDropdownContent.Type.Tokens + + signal goBack() + signal itemClicked(string key, string name, url iconSource) + + enum Type{ + Tokens, + Collectibles + } + + QtObject { + id: d + readonly property int filterItemsHeight: 36 + readonly property int filterPopupWidth: 233 + readonly property int filterPopupHeight: 205 + + // Internal management properties + property bool isFilterOptionVisible: false + readonly property string thumbnailsViewState: "THUMBNAILS" + readonly property string listView_depth1_State: "LIST-DEPTH1" + readonly property string listView_depth2_State: "LIST-DEPTH2" + property var currentModel: root.store.collectiblesModel + property var currentSubitems + property string currentItemName: "" + property url currentItemSource: "" + + function reset() { + d.currentItemName = "" + d.currentItemSource = "" + d.currentModel = root.store.collectiblesModel + d.currentSubitems = undefined + root.state = d.listView_depth1_State + } + } + + spacing: 0 + state: d.listView_depth1_State + states: [ + State { + name: d.thumbnailsViewState + PropertyChanges {target: contentLoader; sourceComponent: thumbnailsView} + PropertyChanges {target: d; isFilterOptionVisible: true} + PropertyChanges {target: d; currentModel: d.currentSubitems} + }, + State { + name: d.listView_depth1_State + PropertyChanges {target: contentLoader; sourceComponent: root.type === ExtendedDropdownContent.Type.Tokens ? tokensListView : collectiblesListView} + PropertyChanges {target: d; isFilterOptionVisible: false} + PropertyChanges {target: d; currentModel: root.type === ExtendedDropdownContent.Type.Tokens ? root.store.tokensModel : root.store.collectiblesModel} + }, + State { + name: d.listView_depth2_State + PropertyChanges {target: contentLoader; sourceComponent: collectiblesListView} + PropertyChanges {target: d; isFilterOptionVisible: true } + PropertyChanges {target: d; currentModel: d.currentSubitems} + } + ] + + // Header + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 8 + Layout.topMargin: 5 + + StatusIconTextButton { + Layout.alignment: Qt.AlignVCenter + spacing: 0 + statusIcon: "next" + icon.width: 12 + icon.height: 12 + iconRotation: 180 + text: qsTr("Back") + onClicked: { + if(root.state == d.listView_depth1_State) { + root.goBack() + } + else { + root.state = d.listView_depth1_State + } + } + } + + // Just a filler to fit layout + Item { Layout.fillWidth: true; height: filterButton.implicitHeight } + + StatusFlatRoundButton { + id: filterButton + implicitWidth: 32 + implicitHeight: 32 + visible: d.isFilterOptionVisible + type: StatusFlatRoundButton.Type.Secondary + icon.name: "filter" + + onClicked: { + filterOptionsPopup.x = filterButton.x + filterButton.width - filterOptionsPopup.width + filterOptionsPopup.y = filterButton.y + filterButton.height + 8 + filterOptionsPopup.open() + } + } + + // Filter options popup: + StatusDropdown { + id: filterOptionsPopup + width: d.filterPopupWidth + height: d.filterPopupHeight + contentItem: ColumnLayout { + anchors.fill: parent + anchors.topMargin: 8 + anchors.bottomMargin: 8 + ListView { + Layout.fillWidth: true + Layout.preferredHeight: model.count * d.filterItemsHeight + model: ListModel { + ListElement { text: qsTr("Most viewed"); selected: true } + ListElement { text: qsTr("Newest first"); selected: false } + ListElement { text: qsTr("Oldest first"); selected: false } + } + delegate: StatusItemPicker { + width: ListView.view.width + height: d.filterItemsHeight + color: sensor1.containsMouse ? Theme.palette.baseColor4 : "transparent" + name: model.text + namePixelSize: 13 + selectorType: StatusItemPicker.SelectorType.RadioButton + radioGroup: filterRadioBtnGroup + radioButtonSize: StatusRadioButton.Size.Small + selected: model.selected + + MouseArea { + id: sensor1 + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: { + selected = !selected + console.log("TODO: Clicked filter option: " + model.text) + filterOptionsPopup.close() + } + } + } + + // Not visual element to control filter options + ButtonGroup { + id: filterRadioBtnGroup + } + } + + Separator { Layout.fillWidth: true } + + ListView { + Layout.fillWidth: true + Layout.preferredHeight: model.count * d.filterItemsHeight + model: ListModel { + ListElement { key: "LIST"; text: qsTr("List"); selected: true } + ListElement { key: "THUMBNAILS"; text: qsTr("Thumbnails"); selected: false } + } + delegate: StatusItemPicker { + width: ListView.view.width + height: d.filterItemsHeight + color: sensor2.containsMouse ? Theme.palette.baseColor4 : "transparent" + name: model.text + namePixelSize: 13 + selectorType: StatusItemPicker.SelectorType.RadioButton + radioGroup: visualizationRadioBtnGroup + radioButtonSize: StatusRadioButton.Size.Small + selected: model.selected + + MouseArea { + id: sensor2 + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: { + selected = !selected + if(model.key === "LIST") { + root.state = d.listView_depth2_State + } + else if(model.key === "THUMBNAILS") { + root.state = d.thumbnailsViewState + } + filterOptionsPopup.close() + } + } + } + + // Not visual element to control visualization options + ButtonGroup { + id: visualizationRadioBtnGroup + } + } + } + } + } + + // List elements content + Loader { + id: contentLoader + Layout.preferredWidth: 289 // by design + Layout.bottomMargin: 5 + Layout.preferredHeight: (item != null && typeof(item) !== 'undefined') ? item.implicitHeight : 0 + } + + Component { + id: tokensListView + + ListDropdownContent { + headerModel: ListModel { + ListElement { key: "MINT"; icon: "add"; iconSize: 16; description: qsTr("Mint token"); rotation: 0; spacing: 8 } + ListElement { key: "IMPORT"; icon: "invite-users"; iconSize: 16; description: qsTr("Import existing token"); rotation: 180; spacing: 8 } + } + model: d.currentModel + onHeaderItemClicked: { + if(key === "MINT") console.log("TODO: Mint token") + else if(key === "IMPORT") console.log("TODO: Import existing token") + } + onItemClicked: root.itemClicked(key, shortName, iconSource) + } + } + + Component { + id: collectiblesListView + + ListDropdownContent { + isHeaderVisible: root.state === d.listView_depth1_State + headerModel: ListModel { + ListElement { key: "MINT"; icon: "add"; iconSize: 16; description: qsTr("Mint collectible"); rotation: 0; spacing: 8 } + } + model: d.currentModel + onHeaderItemClicked: { + if(key === "MINT") console.log("TODO: Mint collectible") + } + onItemClicked: { + if(subItems && root.state === d.listView_depth1_State) { + // One deep navigation + d.currentSubitems = subItems + d.currentItemName = name + d.currentItemSource = iconSource + root.state = d.listView_depth2_State + } + else { + d.reset() + root.itemClicked(key, name, iconSource) + } + } + } + } + + Component { + id: thumbnailsView + + ThumbnailsDropdownContent { + title: d.currentItemName + titleImage: d.currentItemSource + model: d.currentModel + onItemClicked: { + d.reset() + root.itemClicked(key, name, iconSource) + } + } + } +} diff --git a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml index d253148596..81a8277a85 100644 --- a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml +++ b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml @@ -15,35 +15,55 @@ import utils 1.0 StatusDropdown { id: root - property real tokenAmountValue: 0 + property var store + + property var itemKey + property real tokenAmount: 0 property string tokenName: d.defaultTokenNameText property url tokenImage: "" + property real collectibleAmount: 1 + property string collectibleName: d.defaultCollectibleNameText + property url collectibleImage: "" property int operator: SQ.Utils.Operators.None property bool withOperatorSelector: true - signal addToken(string tokenText, url tokenImage, int operator) + signal addItem(var itemKey, string itemText, url itemImage, int operator) function reset() { - root.tokenAmountValue = 0 + d.currentTabIndex = 0 + root.itemKey = undefined + root.tokenAmount = 0 root.tokenName = d.defaultTokenNameText root.tokenImage = "" + root.collectibleAmount = 1 + root.collectibleName = d.defaultCollectibleNameText + root.collectibleImage = "" root.operator = SQ.Utils.Operators.None } QtObject { id: d - readonly property bool ready: root.tokenAmountValue > 0 && root.tokenName !== d.defaultTokenNameText + // Internal management properties: + readonly property bool tokensReady: root.tokenAmount > 0 && root.tokenName !== d.defaultTokenNameText + readonly property bool collectiblesReady: root.collectibleAmount > 0 && root.collectibleName !== d.defaultCollectibleNameText + readonly property string tokensState: "TOKENS" + readonly property string collectiblesState: "COLLECTIBLES" + readonly property string ensState: "ENS" + property bool isTokensLayout: true + property int currentTabIndex: 0 // By design values: readonly property int initialHeight: 232 readonly property int mainHeight: 256 + readonly property int mainExtendedHeight: 276 readonly property int operatorsHeight: 96 readonly property int extendedHeight: 417 readonly property int defaultWidth: 289 readonly property int operatorsWidth: 159 property string defaultTokenNameText: qsTr("Choose token") + property string defaultCollectibleNameText: qsTr("Choose collectible") function selectInitState() { if(root.withOperatorSelector) @@ -53,29 +73,21 @@ StatusDropdown { } } - width: d.defaultWidth - height: d.initialHeight + implicitWidth: d.defaultWidth + implicitHeight: loader.implicitHeight contentItem: Loader { id: loader - anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + height: (item != null && typeof(item) !== 'undefined') ? item.implicitHeight : 0 sourceComponent: root.withOperatorSelector ? operatorsSelectorView : tabsView - onSourceComponentChanged: { - if(sourceComponent == tokensExtendedView) { - root.height = Math.min(item.contentHeight + item.anchors.topMargin + item.anchors.bottomMargin, d.extendedHeight) - root.width = d.defaultWidth - } - else if(sourceComponent == operatorsSelectorView) { - root.height = d.operatorsHeight + if(sourceComponent == operatorsSelectorView) { root.width = d.operatorsWidth } - else if(sourceComponent == tabsView && root.withOperatorSelector) { - root.height = d.mainHeight - root.width = d.defaultWidth - } - else if(sourceComponent == tabsView && !root.withOperatorSelector) { - root.height = d.initialHeight + else { root.width = d.defaultWidth } } @@ -85,123 +97,13 @@ StatusDropdown { onClosed: root.reset() onWithOperatorSelectorChanged: d.selectInitState() - Component { - id: tabsView - - ColumnLayout { - anchors.fill: parent - anchors.margins: 8 - anchors.topMargin: 16 - spacing: 8 - StatusIconTextButton { - visible: root.withOperatorSelector - Layout.leftMargin: 8 - spacing: 0 - statusIcon: "next" - icon.width: 12 - icon.height: 12 - iconRotation: 180 - text: qsTr("Back") - onClicked: loader.sourceComponent = operatorsSelectorView - } - StatusSwitchTabBar { - id: tabBar - Layout.preferredWidth: 273 // by design - Layout.preferredHeight: 36 // by design - StatusSwitchTabButton { - text: qsTr("Token") - fontPixelSize: 13 - } - StatusSwitchTabButton { - text: qsTr("Collectibles") - fontPixelSize: 13 - enabled: false // TODO - } - StatusSwitchTabButton { - text: qsTr("ENS") - fontPixelSize: 13 - enabled: false // TODO - } - } - StackLayout { - Layout.fillWidth: true - currentIndex: tabBar.currentIndex - // Tokens layout definition: - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - StatusPickerButton { - Layout.fillWidth: true - Layout.topMargin: 8 - Layout.preferredHeight: 36 - bgColor: Theme.palette.baseColor5 - contentColor: Theme.palette.directColor1 - text: root.tokenName - font.pixelSize: 13 - image.source: root.tokenImage - onClicked: loader.sourceComponent = tokensExtendedView - } - - StatusInput { - Layout.fillWidth: true - minimumHeight: 36 - maximumHeight: 36 - topPadding: 0 - bottomPadding: 0 - text: root.tokenAmountValue == 0 ? "" : root.tokenAmountValue.toString() - font.pixelSize: 13 - rightPadding: amountText.implicitWidth + amountText.anchors.rightMargin + leftPadding - input.placeholderText: "0" - validationMode: StatusInput.ValidationMode.IgnoreInvalidInput - validators: StatusFloatValidator { bottom: 0 } - - StatusBaseText { - id: amountText - anchors.right: parent.right - anchors.rightMargin: 13 - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Amount") - color: Theme.palette.baseColor1 - font.pixelSize: 13 - } - onTextChanged: root.tokenAmountValue = Number(text) - } - - // Just a filler - Item { Layout.fillHeight: true} - - StatusButton { - enabled: d.ready - text: qsTr("Add") - height: 44 - Layout.alignment: Qt.AlignHCenter - Layout.fillWidth: true - onClicked: { root.addToken(root.tokenAmountValue.toString() + " " + root.tokenName, root.tokenImage, root.operator) } - } - } // End of Tokens Layout definition - - // TODO - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - - // TODO - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - } - } - } - Component { id: operatorsSelectorView - ColumnLayout { - anchors.fill: parent - anchors.margins: 8 + ColumnLayout { StatusPickerButton { - Layout.fillWidth: true + Layout.margins: 8 + Layout.bottomMargin: 0 + Layout.preferredWidth: 143 // by design Layout.preferredHeight: 36 horizontalPadding: 12 spacing: 10 @@ -218,6 +120,8 @@ StatusDropdown { } } StatusPickerButton { + Layout.margins: 8 + Layout.topMargin: 0 Layout.fillWidth: true Layout.preferredHeight: 36 horizontalPadding: 12 @@ -238,40 +142,182 @@ StatusDropdown { } Component { - id: tokensExtendedView + id: tabsView - // TODO: It probabily will be a reusable component for collectibles and channels - TokensListDropdownContent { - anchors.fill: parent - anchors.topMargin: 8 - anchors.bottomMargin: 8 - headerModel: ListModel { - ListElement { index: 0; icon: "next"; iconSize: 12; description: qsTr("Back"); rotation: 180; spacing: 0 } - ListElement { index: 1; icon: "add"; iconSize: 16; description: qsTr("Mint token"); rotation: 0; spacing: 8 } - ListElement { index: 2; icon: "invite-users"; iconSize: 16; description: qsTr("Import existing token"); rotation: 180; spacing: 8 } + ColumnLayout { + spacing: 0 + state: d.currentTabIndex === 0 ? d.tokensState : (d.currentTabIndex === 1 ? d.collectiblesState : d.ensState) + states: [ + State { + name: d.tokensState + PropertyChanges {target: tabsLoader; sourceComponent: tokensCollectiblesLayout} + PropertyChanges {target: d; isTokensLayout: true} + }, + State { + name: d.collectiblesState + PropertyChanges {target: tabsLoader; sourceComponent: tokensCollectiblesLayout} + PropertyChanges {target: d; isTokensLayout: false} + }, + State { + name: d.ensState + PropertyChanges {target: tabsLoader; sourceComponent: ensLayout} + } + ] + StatusIconTextButton { + visible: root.withOperatorSelector + Layout.leftMargin: 16 + Layout.topMargin: 12 + spacing: 0 + statusIcon: "next" + icon.width: 12 + icon.height: 12 + iconRotation: 180 + text: qsTr("Back") + onClicked: loader.sourceComponent = operatorsSelectorView } - // TODO: Replace to real data, now dummy model - model: ListModel { - ListElement {imageSource: "qrc:imports/assets/png/tokens/SOCKS.png"; name: "Unisocks"; shortName: "SOCKS"; selected: false; category: "Community tokens"} - ListElement {imageSource: "qrc:imports/assets/png/tokens/ZRX.png"; name: "Ox"; shortName: "ZRX"; selected: false; category: "Listed tokens"} - ListElement {imageSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "1inch"; shortName: "ZRX"; selected: false; category: "Listed tokens"} - ListElement {imageSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Aave"; shortName: "AAVE"; selected: false; category: "Listed tokens"} - ListElement {imageSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Amp"; shortName: "AMP"; selected: false; category: "Listed tokens"} + StatusSwitchTabBar { + id: tabBar + Layout.preferredWidth: 273 // by design + Layout.margins: 8 + Layout.topMargin: root.withOperatorSelector ? 12 : 16 + Layout.preferredHeight: 36 // by design + currentIndex: d.currentTabIndex + onCurrentIndexChanged: d.currentTabIndex = currentIndex + StatusSwitchTabButton { + text: qsTr("Token") + fontPixelSize: 13 + } + StatusSwitchTabButton { + text: qsTr("Collectible") + fontPixelSize: 13 + } + StatusSwitchTabButton { + text: qsTr("ENS") + fontPixelSize: 13 + enabled: false // TODO + } } - onHeaderItemClicked: { - if(index === 0) loader.sourceComponent = tabsView // Go back - // TODO: - else if(index === 1) console.log("TODO: Mint token") - else if(index === 2) console.log("TODO: Import existing token") + Loader { + id: tabsLoader + Layout.fillWidth: true + Layout.margins: 8 } + } + } + + Component { + id: tokensCollectiblesLayout + + ColumnLayout { + spacing: 0 + StatusPickerButton { + Layout.fillWidth: true + Layout.preferredHeight: 36 + bgColor: Theme.palette.baseColor5 + contentColor: Theme.palette.directColor1 + text: d.isTokensLayout ? root.tokenName : root.collectibleName + font.pixelSize: 13 + image.source: d.isTokensLayout ? root.tokenImage : root.collectibleImage + onClicked: loader.sourceComponent = extendedView + } + + RowLayout { + visible: !d.isTokensLayout + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: 16 + Layout.rightMargin: 6 + Layout.topMargin: 8 + StatusBaseText { + Layout.fillWidth: true + text: qsTr("Specific amount") + font.pixelSize: 13 + wrapMode: Text.WordWrap + elide: Text.ElideRight + clip: true + } + StatusSwitch { id: specificAmountSwitch } + } + + StatusInput { + Layout.fillWidth: true + Layout.topMargin: 8 + visible: d.isTokensLayout ? true : specificAmountSwitch.checked + minimumHeight: 36 + maximumHeight: 36 + topPadding: 0 + bottomPadding: 0 + text: d.isTokensLayout ? (root.tokenAmount === 0 ? "" : root.tokenAmount.toString()) : + (root.collectibleAmount === 0 ? "" : root.collectibleAmount.toString()) + font.pixelSize: 13 + rightPadding: amountText.implicitWidth + amountText.anchors.rightMargin + leftPadding + input.placeholderText: "0" + validationMode: StatusInput.ValidationMode.IgnoreInvalidInput + validators: StatusFloatValidator { bottom: 0 } + + StatusBaseText { + id: amountText + anchors.right: parent.right + anchors.rightMargin: 13 + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Amount") + color: Theme.palette.baseColor1 + font.pixelSize: 13 + } + onTextChanged: { + if(d.isTokensLayout) + root.tokenAmount = Number(text) + else + root.collectibleAmount = Number(text) + } + } + + StatusButton { + enabled: d.isTokensLayout ? d.tokensReady : d.collectiblesReady + text: qsTr("Add") + height: 44 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 32 + onClicked: { + if(d.isTokensLayout) + root.addItem(root.itemKey, root.tokenAmount.toString() + " " + root.tokenName, root.tokenImage, root.operator) + else + root.addItem(root.itemKey, root.collectibleAmount.toString() + " " + root.collectibleName, root.collectibleImage, root.operator) + } + } + } + } + + // TODO + Component { + id: ensLayout + Item {} + } + + Component { + id: extendedView + + ExtendedDropdownContent { + store: root.store + type: d.isTokensLayout ? ExtendedDropdownContent.Type.Tokens : ExtendedDropdownContent.Type.Collectibles + onGoBack: loader.sourceComponent = tabsView onItemClicked: { // Go back loader.sourceComponent = tabsView - // Update new token info - root.tokenName = shortName - root.tokenImage = imageSource + if(d.isTokensLayout) { + // Update new token item info + root.tokenName = name + root.tokenImage = iconSource + } + else { + // Update new collectible item info + root.collectibleName = name + root.collectibleImage = iconSource + } + root.itemKey = key } } - } + } } diff --git a/ui/app/AppLayouts/Chat/controls/community/TokensListDropdownContent.qml b/ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml similarity index 73% rename from ui/app/AppLayouts/Chat/controls/community/TokensListDropdownContent.qml rename to ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml index 0b70251cd0..56790a7632 100644 --- a/ui/app/AppLayouts/Chat/controls/community/TokensListDropdownContent.qml +++ b/ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml @@ -13,19 +13,22 @@ StatusListView { id: root property var headerModel + property bool isHeaderVisible: true + property int maxHeight: 381 // default by design - signal headerItemClicked(int index) - signal itemClicked(string name, string shortName, url imageSource) + signal headerItemClicked(string key) + signal itemClicked(var key, string name, var shortName, url iconSource, var subItems) implicitWidth: 273 + implicitHeight: Math.min(contentHeight, root.maxHeight) currentIndex: -1 clip: true - headerPositioning: ListView.OverlayHeader header: Rectangle { + visible: root.isHeaderVisible z: 3 // Above delegate (z=1) and above section.delegate (z = 2) color: Theme.palette.statusPopupMenu.backgroundColor width: root.width - height: columnHeader.implicitHeight + 2 * columnHeader.anchors.topMargin + height: root.isHeaderVisible ? columnHeader.implicitHeight + 2 * columnHeader.anchors.topMargin : 0 ColumnLayout { id: columnHeader anchors.top: parent.top @@ -52,7 +55,7 @@ StatusListView { }// End of Header delegate: Rectangle { width: ListView.view.width - height: 44 // by design + height: 45 // by design color: mouseArea.containsMouse ? Theme.palette.baseColor4 : "transparent" RowLayout { anchors.fill: parent @@ -60,9 +63,9 @@ StatusListView { spacing: 8 StatusRoundedImage { Layout.alignment: Qt.AlignVCenter - image.source: model.imageSource - visible: model.imageSource.toString() !== "" - Layout.preferredWidth: 28 + image.source: model.iconSource + visible: model.iconSource.toString() !== "" + Layout.preferredWidth: 32 Layout.preferredHeight: Layout.preferredWidth } ColumnLayout { @@ -78,21 +81,31 @@ StatusListView { elide: Text.ElideRight } StatusBaseText { + visible: !!model.shortName Layout.fillWidth: true - text: model.shortName + text: !!model.shortName ? model.shortName : "" color: Theme.palette.baseColor1 font.pixelSize: 12 clip: true elide: Text.ElideRight } } + StatusIcon { + icon: "tiny/chevron-right" + visible: !!model.subItems && model.subItems.count > 0 + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: 16 + color: Theme.palette.baseColor1 + width: 16 + height: 16 + } } MouseArea { id: mouseArea anchors.fill: parent cursorShape: Qt.PointingHandCursor hoverEnabled: true - onClicked: { root.itemClicked(model.name, model.shortName, model.imageSource) } + onClicked: { root.itemClicked(model.key, model.name, model.shortName, model.iconSource, model.subItems) } } }// End of Item section.property: "category" diff --git a/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml b/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml new file mode 100644 index 0000000000..ff4d33a669 --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml @@ -0,0 +1,111 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.14 +import QtQuick.Controls 2.14 + +import StatusQ.Core.Theme 0.1 +import StatusQ.Core 0.1 +import StatusQ.Components 0.1 + +StatusScrollView { + id: root + + property string title: "" + property url titleImage: "" + property string subtitle: "" + property ListModel model + property int maxHeight: 381 // default by design + + signal itemClicked(var key, string name, url iconSource) + + QtObject { + id: d + readonly property int imageSize: 133 + readonly property int columns: 2 + } + + implicitHeight: Math.min(column.implicitHeight, root.maxHeight) + implicitWidth: d.imageSize * d.columns + grid.columnSpacing * (d.columns - 1) + clip: true + flickDeceleration: Flickable.VerticalFlick + + ColumnLayout { + id: column + spacing: 4 + Item { + Layout.fillWidth: true + height: 45 // by design + RowLayout { + anchors.fill: parent + anchors.leftMargin: 8 + spacing: 8 + StatusRoundedImage { + Layout.alignment: Qt.AlignVCenter + image.source: root.titleImage + visible: root.titleImage.toString() !== "" + Layout.preferredWidth: 32 + Layout.preferredHeight: Layout.preferredWidth + } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 0 + StatusBaseText { + Layout.fillWidth: true + text: root.title + color: Theme.palette.directColor1 + font.pixelSize: 13 + clip: true + elide: Text.ElideRight + } + StatusBaseText { + visible: root.subtitle + Layout.fillWidth: true + text: root.subtitle + color: Theme.palette.baseColor1 + font.pixelSize: 12 + clip: true + elide: Text.ElideRight + } + } + } + } + GridLayout { + id: grid + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + columnSpacing: 8 + rowSpacing: 12 + columns: d.columns + Repeater { + model: root.model + delegate: ColumnLayout { + spacing: 4 + Rectangle { + Layout.preferredWidth: 133 + Layout.preferredHeight: 133 + color: "transparent" + Image { + source: model.imageSource + anchors.fill: parent + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: { root.itemClicked(model.key, model.name, model.iconSource) } + } + } + StatusBaseText { + Layout.alignment: Qt.AlignLeft + Layout.leftMargin: 8 + text: model.name + color: Theme.palette.directColor1 + font.pixelSize: 13 + clip: true + elide: Text.ElideRight + } + } + } + } + } +} diff --git a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml index 343d195750..c446ddf6fa 100644 --- a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml @@ -45,6 +45,6 @@ SettingsPageLayout { Component { id: newPermissionView - CommunityNewPermissionView { } + CommunityNewPermissionView {} } } diff --git a/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml b/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml new file mode 100644 index 0000000000..42095e6808 --- /dev/null +++ b/ui/app/AppLayouts/Chat/stores/CommunitiesStore.qml @@ -0,0 +1,77 @@ +import QtQuick 2.0 + +QtObject { + + // TODO: Replace to real data, now dummy model + property var tokensModel: ListModel { + ListElement {key: "socks"; iconSource: "qrc:imports/assets/png/tokens/SOCKS.png"; name: "Unisocks"; shortName: "SOCKS"; category: "Community tokens"} + ListElement {key: "zrx"; iconSource: "qrc:imports/assets/png/tokens/ZRX.png"; name: "Ox"; shortName: "ZRX"; category: "Listed tokens"} + ListElement {key: "1inch"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "1inch"; shortName: "ZRX"; category: "Listed tokens"} + ListElement {key: "Aave"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Aave"; shortName: "AAVE"; category: "Listed tokens"} + ListElement {key: "Amp"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Amp"; shortName: "AMP"; category: "Listed tokens"} + } + + // TODO: Replace to real data, now dummy model + property var collectiblesModel: ListModel { + ListElement { + key: "Anniversary" + iconSource: "qrc:imports/assets/png/collectibles/Anniversary.png" + name: "Anniversary" + category: "Community collectibles" + } + ListElement { + key: "CryptoKitties" + iconSource: "qrc:imports/assets/png/collectibles/CryptoKitties.png" + name: "CryptoKitties" + category: "Your collectibles" + subItems: [ + ListElement { + key: "Kitty1" + iconSource: "qrc:imports/assets/png/collectibles/Furbeard.png" + imageSource: "qrc:imports/assets/png/collectibles/FurbeardBig.png" + name: "Furbeard" + }, + ListElement { + key: "Kitty2" + iconSource: "qrc:imports/assets/png/collectibles/Magicat.png" + imageSource: "qrc:imports/assets/png/collectibles/MagicatBig.png" + name: "Magicat" + }, + ListElement { + key: "Kitty3" + iconSource: "qrc:imports/assets/png/collectibles/HappyMeow.png" + imageSource: "qrc:imports/assets/png/collectibles/HappyMeowBig.png" + name: "Happy Meow" + }, + ListElement { + key: "Kitty4" + iconSource: "qrc:imports/assets/png/collectibles/Furbeard.png" + imageSource: "qrc:imports/assets/png/collectibles/FurbeardBig.png" + name: "Furbeard-2" + }, + ListElement { + key: "Kitty5" + iconSource: "qrc:imports/assets/png/collectibles/Magicat.png" + imageSource: "qrc:imports/assets/png/collectibles/MagicatBig.png" + name: "Magicat-3" + } + ] + } + ListElement { + key: "SuperRare" + iconSource: "qrc:imports/assets/png/collectibles/SuperRare.png"; + name: "SuperRare" + category: "Your collectibles" + } + ListElement { + key: "Custom" + iconSource: "qrc:imports/assets/png/collectibles/SNT.png" + name: "Custom Collectible" + category: "All collectibles" + } + } + + function createPermissions(permissions) { + console.log("TODO: Create permissions - backend call") + } +} diff --git a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml index ae993856f5..0f0ffc593a 100644 --- a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml +++ b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml @@ -10,15 +10,25 @@ import utils 1.0 import shared.panels 1.0 import "../../../Chat/controls/community" +import "../../stores" Flickable { id: root + property var store: CommunitiesStore {} + + signal createPermission() + // TODO: Call this when permissions are stored in backend to start a new permissions flow + function clearPermissions() { + d.permissions.clear() + } + QtObject { id: d property bool isPrivate: false + property ListModel permissions: ListModel{} } contentWidth: mainLayout.width @@ -45,9 +55,11 @@ Flickable { orOperatorText: qsTr("or") popupItem: HoldingsDropdown { id: dropdown + store: root.store withOperatorSelector: tokensSelector.itemsModel.count > 0 - onAddToken: { - tokensSelector.addItem(tokenText, tokenImage, operator) + onAddItem: { + tokensSelector.addItem(itemText, itemImage, operator) + d.permissions.append([{itemKey: itemKey}, {operator: operator}]) dropdown.close() } } @@ -117,11 +129,14 @@ Flickable { StatusButton { Layout.topMargin: 24 text: qsTr("Create permission") - enabled: false + enabled: d.permissions.count > 0 Layout.preferredHeight: 44 Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true - onClicked: root.createPermission() + onClicked: { + root.store.createPermissions(d.permissions) + root.clearPermissions() + } } } } diff --git a/ui/imports/assets/png/collectibles/Anniversary.png b/ui/imports/assets/png/collectibles/Anniversary.png new file mode 100644 index 0000000000..6bbc8a6dd8 Binary files /dev/null and b/ui/imports/assets/png/collectibles/Anniversary.png differ diff --git a/ui/imports/assets/png/collectibles/Furbeard.png b/ui/imports/assets/png/collectibles/Furbeard.png new file mode 100644 index 0000000000..3f1618ec5c Binary files /dev/null and b/ui/imports/assets/png/collectibles/Furbeard.png differ diff --git a/ui/imports/assets/png/collectibles/FurbeardBig.png b/ui/imports/assets/png/collectibles/FurbeardBig.png new file mode 100644 index 0000000000..af675409c3 Binary files /dev/null and b/ui/imports/assets/png/collectibles/FurbeardBig.png differ diff --git a/ui/imports/assets/png/collectibles/HappyMeow.png b/ui/imports/assets/png/collectibles/HappyMeow.png new file mode 100644 index 0000000000..cd6940d064 Binary files /dev/null and b/ui/imports/assets/png/collectibles/HappyMeow.png differ diff --git a/ui/imports/assets/png/collectibles/HappyMeowBig.png b/ui/imports/assets/png/collectibles/HappyMeowBig.png new file mode 100644 index 0000000000..17818e80a4 Binary files /dev/null and b/ui/imports/assets/png/collectibles/HappyMeowBig.png differ diff --git a/ui/imports/assets/png/collectibles/Magicat.png b/ui/imports/assets/png/collectibles/Magicat.png new file mode 100644 index 0000000000..725b24e53a Binary files /dev/null and b/ui/imports/assets/png/collectibles/Magicat.png differ diff --git a/ui/imports/assets/png/collectibles/MagicatBig.png b/ui/imports/assets/png/collectibles/MagicatBig.png new file mode 100644 index 0000000000..c3d8e6b82e Binary files /dev/null and b/ui/imports/assets/png/collectibles/MagicatBig.png differ diff --git a/ui/imports/assets/png/collectibles/SuperRare.png b/ui/imports/assets/png/collectibles/SuperRare.png new file mode 100644 index 0000000000..93bc28b082 Binary files /dev/null and b/ui/imports/assets/png/collectibles/SuperRare.png differ