From 07484cb15c4ab5337e8e05c86c6c68d588b0f56c Mon Sep 17 00:00:00 2001 From: Alex Jbanca Date: Wed, 28 Feb 2024 14:19:14 +0200 Subject: [PATCH] feat(ProfileShowcase): Simplify ProfileShowcasePanel API and connect to the new models 1. Fix `EmptyShapeRectangleFooterListView` visibility and positioning after ListView items are animated out of view 2. Update `ShowcaseDelegate` to remove modelObject dependency. The UI properties needed will be received from parent 3. Update `ProfileShowcasePanel` to remove the previous logic involving rows move and visibility change. There is no need now for two specialised delegate components (one for in showcase and one for hidden). The delegate is still configured outside of this component because the model rolenames need to be mapped to the delegate properties. The common delegate logic is implemented in the `ProfileShowcasePanel` and only the model mapping is needed from parent component. 3. Update all `ProfileShowcase*Panel` to impement the new API 4. Remove all `*ShowcaseDelegate`. The delegate is simple enough to be configured at once in the Panel --- .../ProfileShowcaseAccountsPanelPage.qml | 145 ++++----- .../pages/ProfileShowcaseAssetsPanelPage.qml | 100 +++--- .../ProfileShowcaseCollectiblesPanelPage.qml | 190 +++++++----- .../ProfileShowcaseCommunitiesPanelPage.qml | 195 ++++++------ storybook/pages/ProfileShowcasePanelPage.qml | 222 +++++++++++++ ui/StatusQ/src/statusq.qrc | 1 + .../controls/AccountShowcaseDelegate.qml | 16 - .../controls/AssetShowcaseDelegate.qml | 26 -- .../controls/CollectibleShowcaseDelegate.qml | 13 - .../controls/CommunityShowcaseDelegate.qml | 15 - .../Profile/controls/ShowcaseDelegate.qml | 7 +- ui/app/AppLayouts/Profile/controls/qmldir | 5 +- .../panels/ProfileShowcaseAccountsPanel.qml | 40 +-- .../panels/ProfileShowcaseAssetsPanel.qml | 47 ++- .../ProfileShowcaseCollectiblesPanel.qml | 43 +-- .../ProfileShowcaseCommunitiesPanel.qml | 37 +-- .../Profile/panels/ProfileShowcasePanel.qml | 292 ++++++++---------- ui/app/AppLayouts/Profile/panels/qmldir | 7 +- .../Profile/views/MyProfileView.qml | 79 +++-- .../EmptyShapeRectangleFooterListView.qml | 7 +- 20 files changed, 775 insertions(+), 712 deletions(-) create mode 100644 storybook/pages/ProfileShowcasePanelPage.qml delete mode 100644 ui/app/AppLayouts/Profile/controls/AccountShowcaseDelegate.qml delete mode 100644 ui/app/AppLayouts/Profile/controls/AssetShowcaseDelegate.qml delete mode 100644 ui/app/AppLayouts/Profile/controls/CollectibleShowcaseDelegate.qml delete mode 100644 ui/app/AppLayouts/Profile/controls/CommunityShowcaseDelegate.qml diff --git a/storybook/pages/ProfileShowcaseAccountsPanelPage.qml b/storybook/pages/ProfileShowcaseAccountsPanelPage.qml index a6170f2b99..6b62c3340e 100644 --- a/storybook/pages/ProfileShowcaseAccountsPanelPage.qml +++ b/storybook/pages/ProfileShowcaseAccountsPanelPage.qml @@ -5,7 +5,6 @@ import QtQuick.Layouts 1.15 import StatusQ.Core 0.1 import StatusQ.Core.Utils 0.1 as CoreUtils -import mainui 1.0 import AppLayouts.Profile.panels 1.0 import shared.stores 1.0 @@ -21,91 +20,76 @@ SplitView { orientation: Qt.Vertical - Popups { - popupParent: root - rootStore: QtObject {} - communityTokensStore: CommunityTokensStore {} + readonly property string currentWallet: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7420" + + ListModel { + id: hiddenModelItem + ListElement { + name: "My Status Account" + key: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7420" + colorId: "primary" + emoji: "πŸ‡¨πŸ‡Ώ" + walletType: "" + visibility: 0 + } + ListElement { + name: "testing (no emoji, colored, seed)" + key: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7000" + colorId: "" + emoji: "" + walletType: "seed" + visibility: 0 + } + ListElement { + name: "My Bro's Account" + key: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7421" + colorId: "orange" + emoji: "πŸ‡ΈπŸ‡°" + walletType: "watch" + visibility: 0 + } } - readonly property string currentWallet: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7420" + ListModel { + id: inShowcaseModelItem + + ListElement { + name: "My Status Account" + key: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7420" + colorId: "primary" + emoji: "πŸ‡¨πŸ‡Ώ" + walletType: "" + visibility: 1 + } + ListElement { + name: "testing (no emoji, colored, seed)" + key: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7000" + colorId: "" + emoji: "" + walletType: "seed" + visibility: 1 + } + ListElement { + name: "My Bro's Account" + key: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7421" + colorId: "orange" + emoji: "πŸ‡ΈπŸ‡°" + walletType: "watch" + visibility: 1 + } + } ListModel { id: emptyModel } - ListModel { - id: accountsModel - - ListElement { - name: "My Status Account" - address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7420" - colorId: "primary" - emoji: "πŸ‡¨πŸ‡Ώ" - walletType: "" - } - ListElement { - name: "testing (no emoji, colored, seed)" - address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7000" - colorId: "" - emoji: "" - walletType: "seed" - } - ListElement { - name: "My Bro's Account" - address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7421" - colorId: "orange" - emoji: "πŸ‡ΈπŸ‡°" - walletType: "watch" - } - } - - ListModel { - id: inShowcaseAccountsModel - - property int hiddenCount: emptyModelChecker.checked ? 0 : accountsModel.count - count - - signal baseModelFilterConditionsMayHaveChanged() - - function setVisibilityByIndex(index, visibility) { - if (visibility === Constants.ShowcaseVisibility.NoOne) { - remove(index) - } else { - get(index).showcaseVisibility = visibility - } - } - - function setVisibility(address, visibility) { - for (let i = 0; i < count; ++i) { - if (get(i).address === address) { - setVisibilityByIndex(i, visibility) - } - } - } - - function hasItemInShowcase(address) { - for (let i = 0; i < count; ++i) { - if (get(i).address === address) { - return true - } - } - return false - } - - function upsertItemJson(item) { - append(JSON.parse(item)) - } - } - - StatusScrollView { // wrapped in a ScrollView on purpose; to simulate SettingsContentBase.qml + ProfileShowcaseAccountsPanel { + id: showcasePanel SplitView.fillWidth: true SplitView.preferredHeight: 500 - ProfileShowcaseAccountsPanel { - id: showcasePanel - width: 500 - baseModel: emptyModelChecker.checked ? emptyModel : accountsModel - showcaseModel: inShowcaseAccountsModel - currentWallet: root.currentWallet - } + inShowcaseModel: emptyModelChecker.checked ? emptyModel : inShowcaseModelItem + hiddenModel: emptyModelChecker.checked ? emptyModel : hiddenModelItem + currentWallet: root.currentWallet } LogsAndControlsPanel { @@ -117,9 +101,8 @@ SplitView { logsView.logText: logs.logText ColumnLayout { - Button { - text: "Reset (clear settings)" - onClicked: showcasePanel.reset() + Label { + text: "β“˜ Shwcase interaction implemented in ProfileShowcasePanelPage" } CheckBox { @@ -127,8 +110,6 @@ SplitView { text: "Empty model" checked: false - - onClicked: showcasePanel.reset() } } } diff --git a/storybook/pages/ProfileShowcaseAssetsPanelPage.qml b/storybook/pages/ProfileShowcaseAssetsPanelPage.qml index c4be7ef0c7..ff20b68907 100644 --- a/storybook/pages/ProfileShowcaseAssetsPanelPage.qml +++ b/storybook/pages/ProfileShowcaseAssetsPanelPage.qml @@ -2,9 +2,12 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Layouts 1.15 +import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Utils 0.1 as CoreUtils +import SortFilterProxyModel 0.2 + import mainui 1.0 import AppLayouts.Profile.panels 1.0 import shared.stores 1.0 @@ -23,77 +26,58 @@ SplitView { orientation: Qt.Vertical - Popups { - popupParent: root - rootStore: QtObject {} - communityTokensStore: CommunityTokensStore {} - } - readonly property WalletAssetsStore walletAssetStore: WalletAssetsStore { assetsWithFilteredBalances: walletAssetStore.groupedAccountsAssetsModel } - ListModel { - id: emptyModel + SortFilterProxyModel { + id: inShowcaseModelItem + sourceModel: !emptyModelChecker.checked ? walletAssetStore.groupedAccountAssetsModel : null + proxyRoles: [ + FastExpressionRole { + name: "key" + expression: "Asset 1" + index + }, + FastExpressionRole { + name: "visibility" + expression: 1 + } + ] } - ListModel { - id: inShowcaseAssetsModel - - property int hiddenCount: emptyModelChecker.checked ? 0 : walletAssetStore.groupedAccountsAssetsModel.count - count - - signal baseModelFilterConditionsMayHaveChanged() - - function setVisibilityByIndex(index, visibility) { - if (visibility === Constants.ShowcaseVisibility.NoOne) { - remove(index) - } else { - get(index).showcaseVisibility = visibility + SortFilterProxyModel { + id: hiddenShowcaseModelItem + sourceModel: !emptyModelChecker.checked ? walletAssetStore.groupedAccountAssetsModel : null + proxyRoles: [ + FastExpressionRole { + name: "key" + expression: "Asset 2" + index + }, + FastExpressionRole { + name: "visibility" + expression: 0 } - } - - function setVisibility(symbol, visibility) { - for (let i = 0; i < count; ++i) { - if (get(i).symbol === symbol) { - setVisibilityByIndex(i, visibility) - } - } - } - - function hasItemInShowcase(symbol) { - for (let i = 0; i < count; ++i) { - if (get(i).symbol === symbol) { - return true - } - } - return false - } - - function upsertItemJson(item) { - append(JSON.parse(item)) - } + ] } - StatusScrollView { // wrapped in a ScrollView on purpose; to simulate SettingsContentBase.qml + ProfileShowcaseAssetsPanel { + id: showcasePanel SplitView.fillWidth: true SplitView.preferredHeight: 500 + inShowcaseModel: inShowcaseModelItem + hiddenModel: hiddenShowcaseModelItem - ProfileShowcaseAssetsPanel { - id: showcasePanel - width: 500 - baseModel: emptyModelChecker.checked ? emptyModel : walletAssetStore.groupedAccountAssetsModel - showcaseModel: inShowcaseAssetsModel - addAccountsButtonVisible: !hasAllAccountsChecker.checked + addAccountsButtonVisible: !hasAllAccountsChecker.checked - formatCurrencyAmount: function (amount, symbol) { - return ({amount: amount, - symbol: symbol.toUpperCase(), - displayDecimals: 4, - stripTrailingZeroes: false}) - } - - onNavigateToAccountsTab: logs.logEvent("ProfileShowcaseAssetsPanel::onNavigateToAccountsTab") + formatCurrencyAmount: function (amount, symbol) { + const currencyAmount = ({amount: amount, + symbol: symbol.toUpperCase(), + displayDecimals: 4, + stripTrailingZeroes: false}) + return LocaleUtils.currencyAmountToLocaleString(currencyAmount) } + + onNavigateToAccountsTab: logs.logEvent("ProfileShowcaseAssetsPanel::onNavigateToAccountsTab") } LogsAndControlsPanel { @@ -123,8 +107,6 @@ SplitView { text: "Empty model" checked: false - - onClicked: showcasePanel.reset() } } } diff --git a/storybook/pages/ProfileShowcaseCollectiblesPanelPage.qml b/storybook/pages/ProfileShowcaseCollectiblesPanelPage.qml index 82a578c9cd..a9d92c2269 100644 --- a/storybook/pages/ProfileShowcaseCollectiblesPanelPage.qml +++ b/storybook/pages/ProfileShowcaseCollectiblesPanelPage.qml @@ -23,71 +23,132 @@ SplitView { orientation: Qt.Vertical - Popups { - popupParent: root - rootStore: QtObject {} - communityTokensStore: CommunityTokensStore {} - } - ListModel { - id: emptyModel - } - - ListModel { - id: collectiblesModel - + id: hiddenModelItem readonly property var data: [ { - uid: "123", - name: "SNT", + key: "1234", + name: "SNTT", collectionName: "Super Nitro Toluen (with pink bg)", backgroundColor: "pink", imageUrl: ModelsData.collectibles.custom, isLoading: false, - communityId: "ddls" + communityId: "ddls", + visibility: Constants.ShowcaseVisibility.NoOne }, { - uid: "34545656768", - name: "Kitty 1", + key: "3454565676", + name: "Kitty 3", collectionName: "Kitties", backgroundColor: "", imageUrl: ModelsData.collectibles.kitty1Big, - isLoading: false + isLoading: false, + visibility: Constants.ShowcaseVisibility.NoOne }, { - uid: "123456", - name: "Kitty 2", + key: "12345", + name: "Kitty 4", collectionName: "", backgroundColor: "", imageUrl: ModelsData.collectibles.kitty2Big, isLoading: false, - communityId: "sox" + communityId: "sox", + visibility: Constants.ShowcaseVisibility.NoOne }, { - uid: "12345645459537432", + key: "123456454595374", name: "", collectionName: "Super Kitties", backgroundColor: "oink", imageUrl: ModelsData.collectibles.kitty3Big, isLoading: false, - communityId: "ast" + communityId: "ast", + visibility: Constants.ShowcaseVisibility.NoOne }, { - uid: "691", + key: "6912", name: "KILLABEAR", collectionName: "KILLABEARS", backgroundColor: "#807c56", imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png", - isLoading: true + isLoading: true, + visibility: Constants.ShowcaseVisibility.NoOne }, { - uid: "8876", + key: "8876", name: "AIORBIT", description: "", collectionName: "AIORBIT (Animated SVG)", backgroundColor: "", imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg", - isLoading: false + isLoading: false, + visibility: Constants.ShowcaseVisibility.NoOne + } + ] + Component.onCompleted: append(data) + } + + ListModel { + id: inShowcaseModelItem + + readonly property var data: [ + { + key: "123", + name: "SNT", + collectionName: "Super Nitro Toluen (with pink bg)", + backgroundColor: "pink", + imageUrl: ModelsData.collectibles.custom, + isLoading: false, + communityId: "ddls", + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "34545656768", + name: "Kitty 1", + collectionName: "Kitties", + backgroundColor: "", + imageUrl: ModelsData.collectibles.kitty1Big, + isLoading: false, + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "123456", + name: "Kitty 2", + collectionName: "", + backgroundColor: "", + imageUrl: ModelsData.collectibles.kitty2Big, + isLoading: false, + communityId: "sox", + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "12345645459537432", + name: "", + collectionName: "Super Kitties", + backgroundColor: "oink", + imageUrl: ModelsData.collectibles.kitty3Big, + isLoading: false, + communityId: "ast", + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "691", + name: "KILLABEAR", + collectionName: "KILLABEARS", + backgroundColor: "#807c56", + imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png", + isLoading: true, + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "8876", + name: "AIORBIT", + description: "", + collectionName: "AIORBIT (Animated SVG)", + backgroundColor: "", + imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg", + isLoading: false, + visibility: Constants.ShowcaseVisibility.Everyone } ] Component.onCompleted: append(data) @@ -117,63 +178,37 @@ SplitView { } LeftJoinModel { - id: leftJoinModel + id: joinedInShowcase - leftModel: collectiblesModel + leftModel: inShowcaseModelItem rightModel: communityModel joinRole: "communityId" } ListModel { - id: inShowcaseCollectiblesModel - - property int hiddenCount: emptyModelChecker.checked ? 0 : collectiblesModel.count - count - - signal baseModelFilterConditionsMayHaveChanged() - - function setVisibilityByIndex(index, visibility) { - if (visibility === Constants.ShowcaseVisibility.NoOne) { - remove(index) - } else { - get(index).showcaseVisibility = visibility - } - } - - function setVisibility(uid, visibility) { - for (let i = 0; i < count; ++i) { - if (get(i).uid === uid) { - setVisibilityByIndex(i, visibility) - } - } - } - - function hasItemInShowcase(uid) { - for (let i = 0; i < count; ++i) { - if (get(i).uid === uid) { - return true - } - } - return false - } - - function upsertItemJson(item) { - append(JSON.parse(item)) - } + id: emptyModelItem } - StatusScrollView { // wrapped in a ScrollView on purpose; to simulate SettingsContentBase.qml + LeftJoinModel { + id: joinedHiddenModel + + leftModel: hiddenModelItem + rightModel: communityModel + + joinRole: "communityId" + } + + ProfileShowcaseCollectiblesPanel { + id: showcasePanel SplitView.fillWidth: true SplitView.preferredHeight: 500 - ProfileShowcaseCollectiblesPanel { - id: showcasePanel - width: 500 - baseModel: emptyModelChecker.checked ? emptyModel : leftJoinModel - showcaseModel: inShowcaseCollectiblesModel - addAccountsButtonVisible: !hasAllAccountsChecker.checked + inShowcaseModel: emptyModelChecker.checked ? emptyModelItem : joinedInShowcase + hiddenModel: emptyModelChecker.checked ? emptyModelItem : joinedHiddenModel - onNavigateToAccountsTab: logs.logEvent("ProfileShowcaseCollectiblesPanel::onNavigateToAccountsTab") - } + addAccountsButtonVisible: !hasAllAccountsChecker.checked + + onNavigateToAccountsTab: logs.logEvent("ProfileShowcaseCollectiblesPanel::onNavigateToAccountsTab") } LogsAndControlsPanel { @@ -185,12 +220,10 @@ SplitView { logsView.logText: logs.logText ColumnLayout { - Button { - text: "Reset (clear settings)" - - onClicked: showcasePanel.settings.reset() + Label { + text: "β“˜ Shwcase interaction implemented in ProfileShowcasePanelPage" } - + CheckBox { id: hasAllAccountsChecker @@ -203,10 +236,7 @@ SplitView { text: "Empty model" checked: false - - onClicked: showcasePanel.reset() } - } } } diff --git a/storybook/pages/ProfileShowcaseCommunitiesPanelPage.qml b/storybook/pages/ProfileShowcaseCommunitiesPanelPage.qml index 736844b3b3..a16e06bd01 100644 --- a/storybook/pages/ProfileShowcaseCommunitiesPanelPage.qml +++ b/storybook/pages/ProfileShowcaseCommunitiesPanelPage.qml @@ -5,7 +5,6 @@ import QtQuick.Layouts 1.15 import StatusQ.Core 0.1 import StatusQ.Core.Utils 0.1 as CoreUtils -import mainui 1.0 import AppLayouts.Profile.panels 1.0 import AppLayouts.Profile.controls 1.0 import shared.stores 1.0 @@ -22,106 +21,110 @@ SplitView { orientation: Qt.Vertical - Popups { - popupParent: root - rootStore: QtObject {} - communityTokensStore: CommunityTokensStore {} + ListModel { + id: hiddenModelItem + Component.onCompleted: + append([{ + key: "0x0006", + name: "Test community 6", + joined: true, + memberRole: Constants.memberRole.owner, + isControlNode: true, + image: ModelsData.icons.dribble, + color: "yellow", + visibility: Constants.ShowcaseVisibility.NoOne + }, + { + key: "0x0007", + name: "Test community 7", + joined: true, + memberRole: Constants.memberRole.none, + isControlNode: false, + image: ModelsData.collectibles.custom, + color: "peach", + visibility: Constants.ShowcaseVisibility.NoOne + }, + { + key: "0x0008", + name: "Test community 8", + joined: true, + memberRole: Constants.memberRole.none, + isControlNode: false, + image: "", + color: "whitesmoke", + visibility: Constants.ShowcaseVisibility.NoOne + }, + { + key: "0x0009", + name: "Test community 9", + joined: true, + memberRole: Constants.memberRole.admin, + isControlNode: false, + image: ModelsData.icons.spotify, + color: "green", + visibility: Constants.ShowcaseVisibility.NoOne + }, + ]) + } + + ListModel { + id: inShowcaseModelItem + + Component.onCompleted: + append([{ + key: "0x0001", + name: "Test community", + joined: true, + memberRole: Constants.memberRole.owner, + isControlNode: true, + image: ModelsData.icons.dribble, + color: "yellow", + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "0x0002", + name: "Test community 2", + joined: true, + memberRole: Constants.memberRole.none, + isControlNode: false, + image: ModelsData.collectibles.custom, + color: "peach", + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "0x0004", + name: "Test community 3", + joined: true, + memberRole: Constants.memberRole.none, + isControlNode: false, + image: "", + color: "whitesmoke", + visibility: Constants.ShowcaseVisibility.Everyone + }, + { + key: "0x0005", + name: "Test community 4", + joined: true, + memberRole: Constants.memberRole.admin, + isControlNode: false, + image: ModelsData.icons.spotify, + color: "green", + visibility: Constants.ShowcaseVisibility.Everyone + }, + ]) } ListModel { id: emptyModel } - ListModel { - id: communitiesModel - - Component.onCompleted: - append([{ - id: "0x0001", - name: "Test community", - joined: true, - memberRole: Constants.memberRole.owner, - isControlNode: true, - image: ModelsData.icons.dribble, - color: "yellow" - }, - { - id: "0x0002", - name: "Test community 2", - joined: true, - memberRole: Constants.memberRole.none, - isControlNode: false, - image: ModelsData.collectibles.custom, - color: "peach" - }, - { - id: "0x0004", - name: "Test community 3", - joined: true, - memberRole: Constants.memberRole.none, - isControlNode: false, - image: "", - color: "whitesmoke" - }, - { - id: "0x0005", - name: "Test community 4", - joined: true, - memberRole: Constants.memberRole.admin, - isControlNode: false, - image: ModelsData.icons.spotify, - color: "green" - }, - ]) - } - - ListModel { - id: inShowcaseCommunitiesModel - - property int hiddenCount: emptyModelChecker.checked ? 0 : communitiesModel.count - count - - signal baseModelFilterConditionsMayHaveChanged() - - function setVisibilityByIndex(index, visibility) { - if (visibility === Constants.ShowcaseVisibility.NoOne) { - remove(index) - } else { - get(index).showcaseVisibility = visibility - } - } - - function setVisibility(id, visibility) { - for (let i = 0; i < count; ++i) { - if (get(i).id === id) { - setVisibilityByIndex(i, visibility) - } - } - } - - function hasItemInShowcase(id) { - for (let i = 0; i < count; ++i) { - if (get(i).id === id) { - return true - } - } - return false - } - - function upsertItemJson(item) { - append(JSON.parse(item)) - } - } - - StatusScrollView { // wrapped in a ScrollView on purpose; to simulate SettingsContentBase.qml + ProfileShowcaseCommunitiesPanel { + id: showcasePanel SplitView.fillWidth: true SplitView.preferredHeight: 500 - ProfileShowcaseCommunitiesPanel { - id: showcasePanel - width: 500 - baseModel: emptyModelChecker.checked ? emptyModel : communitiesModel - showcaseModel: inShowcaseCommunitiesModel - } + inShowcaseModel: emptyModelChecker.checked ? emptyModel : inShowcaseModelItem + hiddenModel: emptyModelChecker.checked ? emptyModel : hiddenModelItem } LogsAndControlsPanel { @@ -133,9 +136,8 @@ SplitView { logsView.logText: logs.logText ColumnLayout { - Button { - text: "Reset (clear settings)" - onClicked: showcasePanel.reset() + Label { + text: "β“˜ Shwcase interaction implemented in ProfileShowcasePanelPage" } CheckBox { @@ -143,11 +145,10 @@ SplitView { text: "Empty model" checked: false - - onClicked: showcasePanel.reset() } } } + } // category: Panels diff --git a/storybook/pages/ProfileShowcasePanelPage.qml b/storybook/pages/ProfileShowcasePanelPage.qml new file mode 100644 index 0000000000..82e4ad5fa7 --- /dev/null +++ b/storybook/pages/ProfileShowcasePanelPage.qml @@ -0,0 +1,222 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import AppLayouts.Profile.panels 1.0 +import AppLayouts.Profile.controls 1.0 +import StatusQ.Components 0.1 + +import utils 1.0 + +import Storybook 1.0 + +SplitView { + //id: root + + property int inShowcaseModelCount: inShowcaseCounter.value + property int hiddenModelCount: hiddenCounter.value + + orientation: Qt.Vertical + + Logs { id: logs } + + ListModel { + id: inShowcaseModelItem + ListElement { + key: 1 + title: "Item 1" + secondaryTitle: "Description 1" + hasImage: true + image: "https://picsum.photos/200/300?random=1" + iconName: "https://picsum.photos/40/40?random=1" + visibility: 1 + name: "Test community" + joined: true + isControlNode: true + color: "yellow" + hasTag: true + tagText: "New" + tagAsset: "https://picsum.photos/40/40?random=1" + tagLoading: true + } + } + + ListModel { + id: hiddenModelItem + ListElement { + key: 2 + title: "Item 1" + secondaryTitle: "Description 1" + hasImage: true + image: "https://picsum.photos/200/300?random=1" + iconName: "https://picsum.photos/40/40?random=1" + visibility: 0 + name: "Test community" + joined: true + isControlNode: true + color: "yellow" + tagVisible: true + tagText: "New" + tagAsset: "https://picsum.photos/40/40?random=1" + tagLoading: true + } + } + + ProfileShowcasePanel { + id: root + inShowcaseModel: inShowcaseModelItem + hiddenModel: hiddenModelItem + SplitView.fillWidth: true + SplitView.fillHeight: true + emptyInShowcasePlaceholderText: "No items in showcase" + emptyHiddenPlaceholderText: "No hidden items" + onChangePositionRequested: function (key, to) { + for (var i = 0; i < inShowcaseModelItem.count; i++) { + if (inShowcaseModelItem.get(i).key === key) { + inShowcaseModelItem.move(i, to, 1) + break + } + } + + for (var i = 0; i < hiddenModelItem.count; i++) { + if (hiddenModelItem.get(i).key === key) { + hiddenModelItem.move(from, to, 1) + break + } + } + } + onSetVisibilityRequested: function (key, toVisibility) { + for (var i = 0; i < inShowcaseModelItem.count; i++) { + if (inShowcaseModelItem.get(i).key === key) { + inShowcaseModelItem.setProperty(i, "visibility", toVisibility) + if(toVisibility === 0) { + let item = inShowcaseModelItem.get(i) + hiddenModelItem.append(item) + inShowcaseModelItem.remove(i, 1) + } + return + } + } + + for (var i = 0; i < hiddenModelItem.count; i++) { + if (hiddenModelItem.get(i).key === key) { + hiddenModelItem.setProperty(i, "visibility", toVisibility) + if(toVisibility !== 0) { + let item = hiddenModelItem.get(i) + inShowcaseModelItem.append(item) + hiddenModelItem.remove(i, 1) + } + return + } + } + } + + delegate: ProfileShowcasePanel.Delegate { + title: model ? model.title : "" + secondaryTitle: model ? model.secondaryTitle : "" + hasImage: model ? model.hasImage : false + icon.name: model ? model.iconName : "" + icon.source: model ? model.image : "" + icon.color: model ? model.color : "" + + tag.visible: model ? model.hasTag : false + tag.text: model ? model.tagText : "" + tag.asset.name: model ? model.tagAsset : "" + tag.loading: model ? model.tagLoading : false + } + } + + LogsAndControlsPanel { + id: logsAndControlsPanel + SplitView.fillWidth: true + SplitView.preferredHeight: 200 + + RowLayout { + anchors.fill: parent + spacing: 10 + + ColumnLayout { + Label { + text: "In showcase: " + inShowcaseCounter.value + } + Slider { + id: inShowcaseCounter + from: 0 + to: 200 + stepSize: 1 + value: 25 + } + + Label { + text: "Hidden: " + hiddenCounter.value + } + Slider { + id: hiddenCounter + from: 0 + to: 200 + stepSize: 1 + value: 25 + } + } + } + } + + onInShowcaseModelCountChanged: { + let count = inShowcaseModelCount - inShowcaseModelItem.count; + let operation = count > 0 ? (i) =>{ + inShowcaseModelItem.append({ + key: Math.random() * Math.random() * Math.random() * 1000, + title: "Item " + i, + secondaryTitle: "Description " + i, + hasImage: true, + image: "https://picsum.photos/200/300?random=" + i, + iconName: "https://picsum.photos/40/40?random=" + i, + visibility: Math.ceil(Math.random() * 3), + name: "Test community", + joined: true, + isControlNode: true, + color: "yellow", + hasTag: Math.random() > 0.5, + tagText: "New " + 1, + tagAsset: "https://picsum.photos/40/40?random=" + i, + tagLoading: Math.random() > 0.5 + })} : (i) => { + inShowcaseModelItem.remove(inShowcaseModelItem.count - 1); + } + + for (var i = 0; i < Math.abs(count); i++) { + operation(i) + } + } + + onHiddenModelCountChanged: { + let count = hiddenModelCount - hiddenModelItem.count; + let operation = count > 0 ? (i) =>{ + hiddenModelItem.append({ + key: Math.random() * Math.random() * Math.random() * 1000, + title: "Item " + i, + secondaryTitle: "Description " + i, + hasImage: true, + image: "https://picsum.photos/200/300?random=" + i, + iconName: "https://picsum.photos/40/40?random=" + i, + visibility: 0, + name: "Test community", + joined: true, + memberRole: Constants.memberRole.owner, + isControlNode: true, + color: "yellow", + hasTag: Math.random() > 0.5, + tagText: "New " + i, + tagAsset: "https://picsum.photos/40/40?random=" + i, + tagLoading: Math.random() > 0.8 + })} : (i) => { + hiddenModelItem.remove(hiddenModelItem.count - 1); + } + + for (var i = 0; i < Math.abs(count); i++) { + operation(i) + } + } +} + +// category: Panels diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc index c41eef1ea5..18680190dd 100644 --- a/ui/StatusQ/src/statusq.qrc +++ b/ui/StatusQ/src/statusq.qrc @@ -191,6 +191,7 @@ StatusQ/Core/Utils/ModelUtils.qml StatusQ/Core/Utils/ModelsComparator.qml StatusQ/Core/Utils/OperatorsUtils.qml + StatusQ/Core/Utils/QObject.qml StatusQ/Core/Utils/StackViewStates.qml StatusQ/Core/Utils/StatesStack.qml StatusQ/Core/Utils/StringUtils.qml diff --git a/ui/app/AppLayouts/Profile/controls/AccountShowcaseDelegate.qml b/ui/app/AppLayouts/Profile/controls/AccountShowcaseDelegate.qml deleted file mode 100644 index a6a1d21590..0000000000 --- a/ui/app/AppLayouts/Profile/controls/AccountShowcaseDelegate.qml +++ /dev/null @@ -1,16 +0,0 @@ -import QtQuick 2.15 - -import StatusQ.Core.Theme 0.1 - -import AppLayouts.Wallet 1.0 - -import utils 1.0 - -ShowcaseDelegate { - title: !!showcaseObj && !!showcaseObj.name ? showcaseObj.name : "" - secondaryTitle: WalletUtils.addressToDisplay(!!showcaseObj && !!showcaseObj.address ? showcaseObj.address : "", "", true, containsMouse) - hasEmoji: !!showcaseObj && !!showcaseObj.emoji - hasIcon: !hasEmoji - icon.name: hasEmoji ? showcaseObj.emoji : "filled-account" - icon.color: !!showcaseObj && showcaseObj.colorId ? Utils.getColorForId(showcaseObj.colorId) : Theme.palette.primaryColor3 -} diff --git a/ui/app/AppLayouts/Profile/controls/AssetShowcaseDelegate.qml b/ui/app/AppLayouts/Profile/controls/AssetShowcaseDelegate.qml deleted file mode 100644 index 4f9d0473a9..0000000000 --- a/ui/app/AppLayouts/Profile/controls/AssetShowcaseDelegate.qml +++ /dev/null @@ -1,26 +0,0 @@ -import QtQuick 2.15 - -import StatusQ 0.1 -import StatusQ.Core 0.1 - -import utils 1.0 - -ShowcaseDelegate { - id: root - - property var formatCurrencyAmount: function(amount, symbol){} - property double totalValue: !!showcaseObj && !!showcaseObj.decimals ? balancesAggregator.value/(10 ** showcaseObj.decimals): 0 - - title: !!showcaseObj && !!showcaseObj.name ? showcaseObj.name : "" - secondaryTitle: !!showcaseObj && !!showcaseObj.enabledNetworkBalance ? - LocaleUtils.currencyAmountToLocaleString(showcaseObj.enabledNetworkBalance) : - !!showcaseObj && !!showcaseObj.symbol ? formatCurrencyAmount(totalValue, showcaseObj.symbol): Qt.locale().zeroDigit - hasImage: true - icon.source: !!showcaseObj ? Constants.tokenIcon(showcaseObj.symbol) : "" - - SumAggregator { - id: balancesAggregator - model: !!showcaseObj && !!showcaseObj.balances ? showcaseObj.balances: null - roleName: "balance" - } -} diff --git a/ui/app/AppLayouts/Profile/controls/CollectibleShowcaseDelegate.qml b/ui/app/AppLayouts/Profile/controls/CollectibleShowcaseDelegate.qml deleted file mode 100644 index 2c4a7227bf..0000000000 --- a/ui/app/AppLayouts/Profile/controls/CollectibleShowcaseDelegate.qml +++ /dev/null @@ -1,13 +0,0 @@ -import QtQuick 2.15 - -import utils 1.0 - -ShowcaseDelegate { - title: !!showcaseObj ? `${showcaseObj.name}` || `#${showcaseObj.id}` : "" - secondaryTitle: !!showcaseObj && !!showcaseObj.collectionName ? showcaseObj.collectionName : "" - hasImage: !!showcaseObj && !!showcaseObj.imageUrl - - icon.source: hasImage ? showcaseObj.imageUrl : "" - bgRadius: Style.current.radius - assetBgColor: !!showcaseObj && !!showcaseObj.backgroundColor ? showcaseObj.backgroundColor : "transparent" -} diff --git a/ui/app/AppLayouts/Profile/controls/CommunityShowcaseDelegate.qml b/ui/app/AppLayouts/Profile/controls/CommunityShowcaseDelegate.qml deleted file mode 100644 index 590301ea86..0000000000 --- a/ui/app/AppLayouts/Profile/controls/CommunityShowcaseDelegate.qml +++ /dev/null @@ -1,15 +0,0 @@ -import QtQuick 2.15 - -import utils 1.0 - -ShowcaseDelegate { - title: !!showcaseObj && !!showcaseObj.name ? showcaseObj.name : "" - secondaryTitle: !!showcaseObj && (showcaseObj.memberRole === Constants.memberRole.owner || - showcaseObj.memberRole === Constants.memberRole.admin || - showcaseObj.memberRole === Constants.memberRole.tokenMaster) ? qsTr("Admin") : qsTr("Member") - hasImage: !!showcaseObj && !!showcaseObj.image - - icon.name: !!showcaseObj ? showcaseObj.name : "" - icon.source: !!showcaseObj ? showcaseObj.image : "" - icon.color: !!showcaseObj ? showcaseObj.color : "transparent" -} diff --git a/ui/app/AppLayouts/Profile/controls/ShowcaseDelegate.qml b/ui/app/AppLayouts/Profile/controls/ShowcaseDelegate.qml index 4ecde5b80e..553c0ca0bc 100644 --- a/ui/app/AppLayouts/Profile/controls/ShowcaseDelegate.qml +++ b/ui/app/AppLayouts/Profile/controls/ShowcaseDelegate.qml @@ -17,7 +17,7 @@ import utils 1.0 StatusDraggableListItem { id: root - property var showcaseObj + property alias tag: tagItem property int showcaseVisibility: Constants.ShowcaseVisibility.NoOne property bool blurState: false @@ -46,10 +46,9 @@ StatusDraggableListItem { actions: [ ManageTokensCommunityTag { + id: tagItem Layout.maximumWidth: root.width *.4 - visible: showcaseObj && !!showcaseObj.communityId - text: showcaseObj && !! showcaseObj.communityName ? showcaseObj.communityName : "" - asset.name: showcaseObj && !!showcaseObj.communityImage ? showcaseObj.communityImage : "" + visible: false }, StatusRoundButton { icon.name: ProfileUtils.visibilityIcon(root.showcaseVisibility) diff --git a/ui/app/AppLayouts/Profile/controls/qmldir b/ui/app/AppLayouts/Profile/controls/qmldir index f32d2b8c09..ffc5eb10b5 100644 --- a/ui/app/AppLayouts/Profile/controls/qmldir +++ b/ui/app/AppLayouts/Profile/controls/qmldir @@ -1,8 +1,5 @@ AddMoreAccountsLink 1.0 AddMoreAccountsLink.qml -CommunityShowcaseDelegate 1.0 CommunityShowcaseDelegate.qml -CollectibleShowcaseDelegate 1.0 CollectibleShowcaseDelegate.qml -AccountShowcaseDelegate 1.0 AccountShowcaseDelegate.qml -AssetShowcaseDelegate 1.0 AssetShowcaseDelegate.qml WalletAccountDelegate 1.0 WalletAccountDelegate.qml +ShowcaseDelegate 1.0 ShowcaseDelegate.qml StaticSocialLinkInput 1.0 StaticSocialLinkInput.qml WalletKeyPairDelegate 1.0 WalletKeyPairDelegate.qml diff --git a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAccountsPanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAccountsPanel.qml index 920b85b34d..c439806054 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAccountsPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAccountsPanel.qml @@ -3,43 +3,25 @@ import QtQuick 2.15 import utils 1.0 import AppLayouts.Profile.controls 1.0 +import AppLayouts.Wallet 1.0 + +import StatusQ.Core.Theme 0.1 ProfileShowcasePanel { id: root property string currentWallet - keyRole: "address" - roleNames: ["address", "name", "walletType", "emoji", "colorId"].concat(showcaseRoles) - filterFunc: (modelData) => modelData.walletType !== Constants.keyWalletType && !showcaseModel.hasItemInShowcase(modelData.address) emptyInShowcasePlaceholderText: qsTr("Accounts here will show on your profile") emptyHiddenPlaceholderText: qsTr("Accounts here will be hidden from your profile") - hiddenDraggableDelegateComponent: AccountShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item-hidden"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - highlighted: !!modelData && modelData.address === root.currentWallet - onShowcaseVisibilityRequested: { - var tmpObj = Object() - root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role]) - tmpObj.showcaseVisibility = value - showcaseModel.upsertItemJson(JSON.stringify(tmpObj)) - root.showcaseEntryChanged() - } - } - showcaseDraggableDelegateComponent: AccountShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - highlighted: !!modelData && modelData.address === root.currentWallet - dragAxis: Drag.YAxis - showcaseVisibility: !!modelData ? modelData.showcaseVisibility : Constants.ShowcaseVisibility.NoOne - onShowcaseVisibilityRequested: { - showcaseModel.setVisibility(showcaseObj.address, value) - root.showcaseEntryChanged() - } + delegate: ProfileShowcasePanel.Delegate { + title: model ? model.name : "" + secondaryTitle: WalletUtils.addressToDisplay(model ? model.key : "", "", true, containsMouse) + hasEmoji: model && !!model.emoji + hasIcon: !hasEmoji + icon.name: hasEmoji ? model.emoji : "filled-account" + icon.color: model && model.colorId ? Utils.getColorForId(model.colorId) : Theme.palette.primaryColor3 + highlighted: model ? model.key === root.currentWallet : false } } diff --git a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAssetsPanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAssetsPanel.qml index 3093363aee..c78393d454 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAssetsPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseAssetsPanel.qml @@ -1,6 +1,7 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 +import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Controls 0.1 @@ -18,40 +19,28 @@ ProfileShowcasePanel { signal navigateToAccountsTab() - keyRole: "symbol" - roleNames: ["symbol", "name", "address", "communityId", "enabledNetworkBalance", "decimals"].concat(showcaseRoles) - filterFunc: (modelData) => modelData.symbol !== "" && !showcaseModel.hasItemInShowcase(modelData.symbol) emptyInShowcasePlaceholderText: qsTr("Assets here will show on your profile") emptyHiddenPlaceholderText: qsTr("Assets here will be hidden from your profile") - hiddenDraggableDelegateComponent: AssetShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item-hidden"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - formatCurrencyAmount: function(amount, symbol) { - return root.formatCurrencyAmount(amount, symbol) - } - onShowcaseVisibilityRequested: { - var tmpObj = Object() - root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role]) - tmpObj.showcaseVisibility = value - showcaseModel.upsertItemJson(JSON.stringify(tmpObj)) - root.showcaseEntryChanged() - } - } - showcaseDraggableDelegateComponent: AssetShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - dragAxis: Drag.YAxis - showcaseVisibility: !!modelData ? modelData.showcaseVisibility : Constants.ShowcaseVisibility.NoOne - onShowcaseVisibilityRequested: { - showcaseModel.setVisibility(showcaseObj.symbol, value) - root.showcaseEntryChanged() + delegate: ProfileShowcasePanel.Delegate { + + property double totalValue: !!model && !!model.decimals ? balancesAggregator.value/(10 ** model.decimals): 0 + + title: !!model && !!model.name ? model.name : "" + secondaryTitle: !!model && !!model.enabledNetworkBalance ? + LocaleUtils.currencyAmountToLocaleString(model.enabledNetworkBalance) : + !!model && !!model.symbol ? root.formatCurrencyAmount(totalValue, model.symbol) : Qt.locale().zeroDigit + + hasImage: true + icon.source: !!model ? Constants.tokenIcon(model.symbol) : "" + + SumAggregator { + id: balancesAggregator + model: !!model && !!model.balances ? model.balances: null + roleName: "balance" } } + additionalFooterComponent: root.addAccountsButtonVisible ? addMoreAccountsComponent : null Component { diff --git a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCollectiblesPanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCollectiblesPanel.qml index e535be7720..63f661a93d 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCollectiblesPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCollectiblesPanel.qml @@ -16,39 +16,26 @@ ProfileShowcasePanel { signal navigateToAccountsTab() - keyRole: "uid" - roleNames: ["uid", "chainId", "tokenId", "contractAddress", "communityId", "name", "collectionName", "backgroundColor", "imageUrl"].concat(showcaseRoles) - filterFunc: (modelData) => !showcaseModel.hasItemInShowcase(modelData.uid) emptyInShowcasePlaceholderText: qsTr("Collectibles here will show on your profile") emptyHiddenPlaceholderText: qsTr("Collectibles here will be hidden from your profile") - hiddenDraggableDelegateComponent: CollectibleShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item-hidden"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - onShowcaseVisibilityRequested: { - var tmpObj = Object() - root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role]) - tmpObj.showcaseVisibility = value - showcaseModel.upsertItemJson(JSON.stringify(tmpObj)) - root.showcaseEntryChanged() - } - } - showcaseDraggableDelegateComponent: CollectibleShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - dragAxis: Drag.YAxis - showcaseVisibility: !!modelData ? modelData.showcaseVisibility : Constants.ShowcaseVisibility.NoOne - onShowcaseVisibilityRequested: { - showcaseModel.setVisibility(showcaseObj.uid, value) - root.showcaseEntryChanged() - } - } additionalFooterComponent: root.addAccountsButtonVisible ? addMoreAccountsComponent : null + delegate: ProfileShowcasePanel.Delegate { + title: !!model ? `${model.name}` || `#${model.id}` : "" + secondaryTitle: !!model && !!model.collectionName ? model.collectionName : "" + hasImage: !!model && !!model.imageUrl + + icon.source: hasImage ? model.imageUrl : "" + bgRadius: Style.current.radius + assetBgColor: !!model && !!model.backgroundColor ? model.backgroundColor : "transparent" + + tag.visible: model && !!model.communityId + tag.text: model && !!model.communityName ? model.communityName : "" + tag.asset.name: model && !!model.communityImage ? model.communityImage : "" + tag.loading: model && !!model.communityImageLoading ? model.communityImageLoading : false + } + Component { id: addMoreAccountsComponent diff --git a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCommunitiesPanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCommunitiesPanel.qml index bc53c6fe73..fee577182a 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCommunitiesPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileShowcaseCommunitiesPanel.qml @@ -7,35 +7,18 @@ import AppLayouts.Profile.controls 1.0 ProfileShowcasePanel { id: root - keyRole: "id" - roleNames: ["id", "name", "memberRole", "image", "color"].concat(showcaseRoles) - filterFunc: (modelData) => modelData.joined && !root.showcaseModel.hasItemInShowcase(modelData.id) emptyInShowcasePlaceholderText: qsTr("Drag communities here to display in showcase") emptyHiddenPlaceholderText: qsTr("Communities here will be hidden from your Profile") - hiddenDraggableDelegateComponent: CommunityShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item-hidden"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - onShowcaseVisibilityRequested: { - var tmpObj = Object() - root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role]) - tmpObj.showcaseVisibility = value - root.showcaseModel.upsertItemJson(JSON.stringify(tmpObj)) - root.showcaseEntryChanged() - } - } - showcaseDraggableDelegateComponent: CommunityShowcaseDelegate { - Drag.keys: ["x-status-draggable-showcase-item"] - showcaseObj: modelData - dragParent: dragParentData - visualIndex: visualIndexData - dragAxis: Drag.YAxis - showcaseVisibility: !!modelData ? modelData.showcaseVisibility : Constants.ShowcaseVisibility.NoOne - onShowcaseVisibilityRequested: { - root.showcaseModel.setVisibility(showcaseObj.id, value) - root.showcaseEntryChanged() - } + delegate: ProfileShowcasePanel.Delegate { + title: model ? model.name : "" + secondaryTitle: model && (model.memberRole === Constants.memberRole.owner || + model.memberRole === Constants.memberRole.admin || + model.memberRole === Constants.memberRole.tokenMaster) ? qsTr("Admin") : qsTr("Member") + hasImage: model && !!model.image + + icon.name: model ? model.name : "" + icon.source: model ? model.image : "" + icon.color: model ? model.color : "" } } diff --git a/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml b/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml index 2efc1ac665..f05515513f 100644 --- a/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml @@ -2,6 +2,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQml 2.15 +import QtQml.Models 2.15 import StatusQ.Controls 0.1 import StatusQ.Core 0.1 @@ -16,44 +17,51 @@ import AppLayouts.Profile.controls 1.0 DoubleFlickableWithFolding { id: root - readonly property var showcaseRoles: ["showcaseVisibility", "order"] + property Component delegate: Delegate {} - required property string keyRole - required property var roleNames - required property var filterFunc - - property var baseModel - property var showcaseModel - - property Component showcaseDraggableDelegateComponent - property Component hiddenDraggableDelegateComponent + // Expected roles: + // - visibility: int + property var inShowcaseModel + property var hiddenModel property Component additionalFooterComponent + // Placeholder text to be shown when the list is empty property string emptyInShowcasePlaceholderText property string emptyHiddenPlaceholderText - readonly property Connections showcaseUpdateConnections: Connections { - target: root.showcaseModel + // Signal to requst position change of the visible items + signal changePositionRequested(var key, int to) + // Signal to request visibility change of the items + signal setVisibilityRequested(var key, int toVisibility) - function onBaseModelFilterConditionsMayHaveChanged() { - root.updateBaseModelFilters() + // Public delegate component. Implements the minimal ShowcaseDelegate interface needed for DND + component Delegate: ShowcaseDelegate { + id: showcaseDelegate + + property var model: modelData + property var dragKeys: dragKeysData + + readonly property var key: model ? model.key : null + + Drag.keys: dragKeys + + dragParent: root + visualIndex: visualIndexData + dragAxis: Drag.YAxis + showcaseVisibility: model ? model.visibility ?? Constants.ShowcaseVisibility.NoOne : + Constants.ShowcaseVisibility.NoOne + + onShowcaseVisibilityRequested: function (toVisibility){ + root.setVisibilityRequested(key, toVisibility) } } - function reset() { - root.showcaseModel.clear() - updateBaseModelFilters() + ScrollBar.vertical: StatusScrollBar { + policy: ScrollBar.AsNeeded + visible: resolveVisibility(policy, root.height, root.contentHeight) } - function updateBaseModelFilters() { - // Reset base model to update filter conditions - hiddenListView.model = null - hiddenListView.model = root.baseModel - } - - signal showcaseEntryChanged() - QtObject { id: d @@ -69,20 +77,16 @@ DoubleFlickableWithFolding { clip: true - ScrollBar.vertical: StatusScrollBar { - policy: ScrollBar.AsNeeded - visible: resolveVisibility(policy, root.height, root.contentHeight) - } - flickable1: EmptyShapeRectangleFooterListView { id: inShowcaseListView - model: root.showcaseModel width: root.width placeholderText: root.emptyInShowcasePlaceholderText footerHeight: ProfileUtils.defaultDelegateHeight footerContentVisible: !dropAreaRow.visible spacing: Style.current.halfPadding + delegate: delegateWrapper + model: root.inShowcaseModel header: FoldableHeader { width: ListView.view.width @@ -101,66 +105,7 @@ DoubleFlickableWithFolding { } onToggleFolding: root.flip1Folding() - } - - delegate: DropArea { - id: showcaseDelegateRoot - - property int visualIndex: index - - width: ListView.view.width - height: visible && showcaseDraggableDelegateLoader.item ? showcaseDraggableDelegateLoader.item.height : 0 - - keys: d.dragShowcaseItemKey - visible: model.showcaseVisibility !== Constants.ShowcaseVisibility.NoOne - - onEntered: function(drag) { - const from = drag.source.visualIndex - const to = showcaseDraggableDelegateLoader.item.visualIndex - if (to === from) - return - root.showcaseEntryChanged() - root.showcaseModel.move(from, to, 1) - drag.accept() - } - - // TODO: - // This animation is causing issues when there are no elements in the showcase list. - // Reenable it once the refactor of the models and delegates is done (simplified): #13498 - // ListView.onRemove: SequentialAnimation { - // PropertyAction { target: showcaseDelegateRoot; property: "ListView.delayRemove"; value: true } - // NumberAnimation { target: showcaseDelegateRoot; property: "scale"; to: 0; easing.type: Easing.InOutQuad } - // PropertyAction { target: showcaseDelegateRoot; property: "ListView.delayRemove"; value: false } - // } - - // In showcase delegate item container: - Loader { - id: showcaseDraggableDelegateLoader - - property var modelData: model - property var dragParentData: root - property int visualIndexData: index - - width: parent.width - sourceComponent: root.showcaseDraggableDelegateComponent - } - - // Delegate shadow background when dragging: - ShadowDelegate { - id: showcaseShadow - - visible: showcaseDraggableDelegateLoader.item && showcaseDraggableDelegateLoader.item.dragActive - onVisibleChanged: d.isAnyShowcaseDragActive = visible - } - - Binding { - when: dropAreaRow.visible - target: showcaseDraggableDelegateLoader.item - property: "blurState" - value: true - restoreMode: Binding.RestoreBindingOrValue - } - } + } // Overlaid showcase listview content drop area: DropArea { @@ -188,14 +133,14 @@ DoubleFlickableWithFolding { flickable2: EmptyShapeRectangleFooterListView { id: hiddenListView - model: root.baseModel width: root.width placeholderText: root.emptyHiddenPlaceholderText footerHeight: ProfileUtils.defaultDelegateHeight footerContentVisible: !hiddenDropAreaButton.visible - empty: root.showcaseModel.hiddenCount === 0 && !root.flickable2Folded // TO BE REMOVE: #13498 additionalFooterComponent: root.additionalFooterComponent spacing: Style.current.halfPadding + delegate: delegateWrapper + model: root.hiddenModel header: FoldableHeader { width: ListView.view.width @@ -209,64 +154,12 @@ DoubleFlickableWithFolding { text: qsTr("Hide") dropAreaKeys: d.dragShowcaseItemKey - onDropped: { - root.showcaseModel.setVisibilityByIndex(drop.source.visualIndex, visibility) - root.showcaseEntryChanged() - } + onDropped: root.setVisibilityRequested(drop.source.key, visibility) } onToggleFolding: root.flip2Folding() } - delegate: DropArea { - id: hiddenDelegateRoot - - property int visualIndex: index - - visible: root.filterFunc(model) - width: ListView.view.width - height: visible && hiddenDraggableDelegateLoader.item ? hiddenDraggableDelegateLoader.item.height : 0 - - keys: d.dragShowcaseItemKey - - onEntered: function(drag) { - drag.accept() - } - - onDropped: function(drop) { - root.showcaseModel.setVisibilityByIndex(drop.source.visualIndex, Constants.ShowcaseVisibility.NoOne) - root.showcaseEntryChanged() - } - - // Hidden delegate item container: - Loader { - id: hiddenDraggableDelegateLoader - - property var modelData: model - property var dragParentData: root - property int visualIndexData: hiddenDelegateRoot.visualIndex - - width: parent.width - sourceComponent: root.hiddenDraggableDelegateComponent - } - - // Delegate shadow background when dragging: - ShadowDelegate { - id: hiddenShadow - - visible: hiddenDraggableDelegateLoader.item && hiddenDraggableDelegateLoader.item.dragActive - onVisibleChanged: d.isAnyHiddenDragActive = visible - } - - Binding { - when: hiddenDropAreaButton.visible - target: hiddenDraggableDelegateLoader.item - property: "blurState" - value: true - restoreMode: Binding.RestoreBindingOrValue - } - } - // Overlaid hidden listview content drop area: DropArea { anchors.top: parent.top @@ -288,11 +181,7 @@ DoubleFlickableWithFolding { text: qsTr("Hide") dropAreaKeys: d.dragShowcaseItemKey - onDropped: { - root.showcaseModel.setVisibilityByIndex(drop.source.visualIndex, visibility) - root.showcaseEntryChanged() - root.updateBaseModelFilters() - } + onDropped: root.setVisibilityRequested(drop.source.key, visibility) } } } @@ -366,18 +255,7 @@ DoubleFlickableWithFolding { property int margins: Style.current.halfPadding function dropped(drop, visibility) { - var showcaseObj = drop.source.showcaseObj - - // need to set total balance for an asset - if (drop.source.totalValue !== undefined) { - showcaseObj.enabledNetworkBalance = drop.source.totalValue - } - - var tmpObj = Object() - root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role]) - tmpObj.showcaseVisibility = visibility - root.showcaseModel.upsertItemJson(JSON.stringify(tmpObj)) - root.showcaseEntryChanged() + root.setVisibilityRequested(drop.source.key, visibility) } RowLayout { @@ -427,4 +305,92 @@ DoubleFlickableWithFolding { color: Theme.palette.baseColor5 radius: Style.current.radius } + + Component { + id: delegateWrapper + DropArea { + id: showcaseDelegateRoot + + required property var model + required property int index + readonly property int visualIndex: index + readonly property bool isHiddenShowcaseItem: !model.visibility || model.visibility === Constants.ShowcaseVisibility.NoOne + + function handleEntered(drag) { + if (!showcaseDelegateRoot.isHiddenShowcaseItem) { + var from = drag.source.visualIndex + var to = visualIndex + if (to === from) + return + root.changePositionRequested(drag.source.key, to) + } + drag.accept() + } + function handleDropped(drop) { + if (showcaseDelegateRoot.isHiddenShowcaseItem) { + root.setVisibilityRequested(drop.source.key, Constants.ShowcaseVisibility.NoOne) + } + } + + ListView.onRemove: SequentialAnimation { + PropertyAction { target: showcaseDelegateRoot; property: "ListView.delayRemove"; value: true } + NumberAnimation { target: showcaseDelegateRoot; property: "scale"; to: 0; easing.type: Easing.InOutQuad } + PropertyAction { target: showcaseDelegateRoot; property: "ListView.delayRemove"; value: false } + } + + width: ListView.view.width + height: showcaseDraggableDelegateLoader.item ? showcaseDraggableDelegateLoader.item.height : 0 + keys: d.dragShowcaseItemKey + + onEntered: handleEntered(drag) + onDropped: handleDropped(drop) + + // In showcase delegate item container: + Loader { + id: showcaseDraggableDelegateLoader + + property var modelData: showcaseDelegateRoot.model + property var dragParentData: root + property int visualIndexData: showcaseDelegateRoot.index + property var dragKeysData: showcaseDelegateRoot.isHiddenShowcaseItem ? + d.dragHiddenItemKey : d.dragShowcaseItemKey + + width: parent.width + sourceComponent: root.delegate + } + + Binding { + when: showcaseDelegateRoot.isHiddenShowcaseItem ? d.isAnyShowcaseDragActive : d.isAnyHiddenDragActive + target: showcaseDraggableDelegateLoader.item + property: "blurState" + value: true + restoreMode: Binding.RestoreBindingOrValue + } + + Binding { + when: showcaseShadow.visible + target: d + property: showcaseDelegateRoot.isHiddenShowcaseItem ? "isAnyHiddenDragActive" : "isAnyShowcaseDragActive" + value: true + restoreMode: Binding.RestoreBindingOrValue + } + + // Delegate shadow background when dragging: + ShadowDelegate { + id: showcaseShadow + + visible: showcaseDraggableDelegateLoader.item && showcaseDraggableDelegateLoader.item.dragActive + } + + // Delegate shadow background when dragging: + Rectangle { + width: parent.width + height: d.defaultDelegateHeight + anchors.centerIn: parent + color: Theme.palette.baseColor5 + radius: Style.current.radius + visible: showcaseShadow.visible + } + } + } } diff --git a/ui/app/AppLayouts/Profile/panels/qmldir b/ui/app/AppLayouts/Profile/panels/qmldir index 0349907e16..3ec7580ea9 100644 --- a/ui/app/AppLayouts/Profile/panels/qmldir +++ b/ui/app/AppLayouts/Profile/panels/qmldir @@ -1,7 +1,8 @@ ProfileDescriptionPanel 1.0 ProfileDescriptionPanel.qml -ProfileSocialLinksPanel 1.0 ProfileSocialLinksPanel.qml -ProfileShowcaseCommunitiesPanel 1.0 ProfileShowcaseCommunitiesPanel.qml -ProfileShowcaseCollectiblesPanel 1.0 ProfileShowcaseCollectiblesPanel.qml ProfileShowcaseAccountsPanel 1.0 ProfileShowcaseAccountsPanel.qml ProfileShowcaseAssetsPanel 1.0 ProfileShowcaseAssetsPanel.qml +ProfileShowcaseCollectiblesPanel 1.0 ProfileShowcaseCollectiblesPanel.qml +ProfileShowcaseCommunitiesPanel 1.0 ProfileShowcaseCommunitiesPanel.qml +ProfileShowcasePanel 1.0 ProfileShowcasePanel.qml +ProfileSocialLinksPanel 1.0 ProfileSocialLinksPanel.qml SupportedTokenListsPanel 1.0 SupportedTokenListsPanel.qml diff --git a/ui/app/AppLayouts/Profile/views/MyProfileView.qml b/ui/app/AppLayouts/Profile/views/MyProfileView.qml index 3febe4302c..c5e29fa0b3 100644 --- a/ui/app/AppLayouts/Profile/views/MyProfileView.qml +++ b/ui/app/AppLayouts/Profile/views/MyProfileView.qml @@ -16,9 +16,11 @@ import "./profile" import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 +import StatusQ.Core.Utils 0.1 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 +import AppLayouts.Profile.helpers 1.0 import AppLayouts.Profile.panels 1.0 import AppLayouts.Wallet.stores 1.0 @@ -113,7 +115,18 @@ SettingsContentBase { readonly property var priv: QtObject { id: priv - property bool hasAnyProfileShowcaseChanges: false + property bool hasAnyProfileShowcaseChanges: showcaseModels.dirty + + property ProfileShowcaseModels showcaseModels: ProfileShowcaseModels { + communitiesSourceModel: root.communitiesModel + communitiesShowcaseModel: root.profileStore.profileShowcaseCommunitiesModel + + accountsSourceModel: root.walletStore.accounts + accountsShowcaseModel: root.profileStore.profileShowcaseAccountsModel + + collectiblesSourceModel: root.profileStore.collectiblesModel + collectiblesShowcaseModel: root.profileStore.profileShowcaseCollectiblesModel + } function reset() { descriptionPanel.displayName.text = Qt.binding(() => { return profileStore.displayName }) @@ -121,33 +134,15 @@ SettingsContentBase { profileStore.resetSocialLinks() profileHeader.icon = Qt.binding(() => { return profileStore.profileLargeImage }) - profileShowcaseCommunitiesPanel.reset() - profileShowcaseAccountsPanel.reset() - profileShowcaseCollectiblesPanel.reset() - profileShowcaseAssetsPanel.reset() + priv.showcaseModels.revert() root.profileStore.requestProfileShowcasePreferences() - hasAnyProfileShowcaseChanges = false } function save() { if (hasAnyProfileShowcaseChanges) - profileStore.storeProfileShowcasePreferences() - - if (!descriptionPanel.isEnsName) - profileStore.setDisplayName(descriptionPanel.displayName.text) - profileStore.setBio(descriptionPanel.bio.text.trim()) - profileStore.saveSocialLinks() - if (profileHeader.icon === "") { - root.profileStore.removeImage() - } else { - profileStore.uploadImage(profileHeader.icon, - profileHeader.cropRect.x.toFixed(), - profileHeader.cropRect.y.toFixed(), - (profileHeader.cropRect.x + profileHeader.cropRect.width).toFixed(), - (profileHeader.cropRect.y + profileHeader.cropRect.height).toFixed()); - } - - reset() + print ("Profile showcase changes detected: SAVING") + //TODO: implement save as deschibed here + // https://github.com/status-im/status-desktop/pull/13708 } } @@ -206,32 +201,50 @@ SettingsContentBase { // communities ProfileShowcaseCommunitiesPanel { id: profileShowcaseCommunitiesPanel - baseModel: root.communitiesModel - showcaseModel: root.profileStore.profileShowcaseCommunitiesModel - onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + inShowcaseModel: priv.showcaseModels.communitiesVisibleModel + hiddenModel: priv.showcaseModels.communitiesHiddenModel + + onChangePositionRequested: function (key, to) { + priv.showcaseModels.changeCommunityPosition(key, to) + } + onSetVisibilityRequested: function (key, toVisibility) { + priv.showcaseModels.setCommunityVisibility(key, toVisibility) + } } // accounts ProfileShowcaseAccountsPanel { id: profileShowcaseAccountsPanel - baseModel: root.walletStore.accounts - showcaseModel: root.profileStore.profileShowcaseAccountsModel + inShowcaseModel: priv.showcaseModels.accountsVisibleModel + hiddenModel: priv.showcaseModels.accountsHiddenModel currentWallet: root.walletStore.overview.mixedcaseAddress - onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + + onChangePositionRequested: function (key, to) { + priv.showcaseModels.changeAccountPosition(key, to) + + } + onSetVisibilityRequested: function (key, toVisibility) { + priv.showcaseModels.setAccountVisibility(key, toVisibility) + } } // collectibles ProfileShowcaseCollectiblesPanel { id: profileShowcaseCollectiblesPanel - baseModel: root.profileStore.collectiblesModel - showcaseModel: root.profileStore.profileShowcaseCollectiblesModel - addAccountsButtonVisible: root.profileStore.profileShowcaseAccountsModel.hiddenCount > 0 - onShowcaseEntryChanged: priv.hasAnyProfileShowcaseChanges = true + addAccountsButtonVisible: priv.showcaseModels.accountsHiddenModel > 0 onNavigateToAccountsTab: profileTabBar.currentIndex = MyProfileView.TabIndex.Accounts + inShowcaseModel: priv.showcaseModels.collectiblesVisibleModel + hiddenModel: priv.showcaseModels.collectiblesHiddenModel + onChangePositionRequested: function (key, to) { + priv.showcaseModels.changeCollectiblePosition(key, to) + } + onSetVisibilityRequested: function (key, toVisibility) { + priv.showcaseModels.setCollectibleVisibility(key, toVisibility) + } } // web diff --git a/ui/imports/shared/controls/EmptyShapeRectangleFooterListView.qml b/ui/imports/shared/controls/EmptyShapeRectangleFooterListView.qml index c3ef835501..8ec46ea02e 100644 --- a/ui/imports/shared/controls/EmptyShapeRectangleFooterListView.qml +++ b/ui/imports/shared/controls/EmptyShapeRectangleFooterListView.qml @@ -15,10 +15,9 @@ StatusListView { property bool footerContentVisible: true property Component additionalFooterComponent - // TO BE REMOVE: #13498 - property bool empty: root.model && root.count === 0 - ScrollBar.vertical: null + footerPositioning: ListView.PullBackFooter + footer: ColumnLayout { width: root.width @@ -27,7 +26,7 @@ StatusListView { Layout.preferredHeight: root.footerHeight Layout.fillWidth: true - visible: root.empty// TO BE REPLACE root.empty in (#13498): root.empty = root.model && root.count === 0 + visible: root.model && root.count === 0 ShapeRectangle { id: shapeRectangle