feat(ProfileShowcase): Add search input in all tabs
- Added searcher input in header. - Added 2 filter proxies for hidden and inshowcase models depending on search text. - Added filter expression per showcase tab (accounts, collectibles and communities). - Added specific placeholder when search empty. - Added specific logic when search active. - Added search validation. Closes #13508
This commit is contained in:
parent
418d6bcc35
commit
a9b5d8fcf7
|
@ -63,14 +63,23 @@ SplitView {
|
|||
}
|
||||
}
|
||||
|
||||
ProfileShowcasePanel {
|
||||
inShowcaseModel: inShowcaseModelItem
|
||||
hiddenModel: hiddenModelItem
|
||||
Item {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
ProfileShowcasePanel {
|
||||
id: panel
|
||||
|
||||
inShowcaseModel: inShowcaseModelItem
|
||||
hiddenModel: hiddenModelItem
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 16
|
||||
height: parent.height - 16
|
||||
emptyInShowcasePlaceholderText: "No items in showcase"
|
||||
emptyHiddenPlaceholderText: "No hidden items"
|
||||
showcaseLimit: limitCounter.value
|
||||
searchPlaceholderText: qsTr("Search not available in storybook")
|
||||
|
||||
onChangePositionRequested: function (from, to) {
|
||||
inShowcaseModelItem.move(from, to, 1)
|
||||
}
|
||||
|
@ -123,6 +132,7 @@ SplitView {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
id: logsAndControlsPanel
|
||||
|
|
|
@ -16,6 +16,8 @@ import utils 1.0
|
|||
* position, second one containing hidden items.
|
||||
*/
|
||||
QObject {
|
||||
id: root
|
||||
|
||||
property alias sourceModel: joined.leftModel
|
||||
property alias showcaseModel: joined.rightModel
|
||||
|
||||
|
@ -36,6 +38,11 @@ QObject {
|
|||
*/
|
||||
readonly property bool dirty: writable.dirty || !visibleModel.synced
|
||||
|
||||
/**
|
||||
* It sets up a searcher filter on top of both the visible and hidden models.
|
||||
*/
|
||||
property FastExpressionFilter searcherFilter
|
||||
|
||||
function revert() {
|
||||
visible.syncOrder()
|
||||
writable.revert()
|
||||
|
@ -105,16 +112,32 @@ QObject {
|
|||
sorters: RoleSorter { roleName: "showcasePosition" }
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: searcherVisibleSFPM
|
||||
|
||||
sourceModel: visibleSFPM
|
||||
delayed: true
|
||||
filters: root.searcherFilter
|
||||
}
|
||||
|
||||
MovableModel {
|
||||
id: visible
|
||||
|
||||
sourceModel: visibleSFPM
|
||||
sourceModel: searcherVisibleSFPM
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: searcherHiddenSFPM
|
||||
|
||||
sourceModel: writable
|
||||
delayed: true
|
||||
filters: root.searcherFilter
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: hidden
|
||||
|
||||
sourceModel: writable
|
||||
sourceModel: searcherHiddenSFPM
|
||||
delayed: true
|
||||
|
||||
filters: HiddenFilter {}
|
||||
|
|
|
@ -24,6 +24,7 @@ QObject {
|
|||
// Input models
|
||||
property alias communitiesSourceModel: modelAdapter.communitiesSourceModel
|
||||
property alias communitiesShowcaseModel: modelAdapter.communitiesShowcaseModel
|
||||
property string communitiesSearcherText
|
||||
|
||||
// Output models
|
||||
readonly property alias communitiesVisibleModel: communities.visibleModel
|
||||
|
@ -47,6 +48,7 @@ QObject {
|
|||
// Input models
|
||||
property alias accountsSourceModel: modelAdapter.accountsSourceModel
|
||||
property alias accountsShowcaseModel: modelAdapter.accountsShowcaseModel
|
||||
property string accountsSearcherText
|
||||
|
||||
// Output models
|
||||
readonly property alias accountsVisibleModel: accounts.visibleModel
|
||||
|
@ -77,6 +79,7 @@ QObject {
|
|||
// Input models
|
||||
property alias collectiblesSourceModel: modelAdapter.collectiblesSourceModel
|
||||
property alias collectiblesShowcaseModel: modelAdapter.collectiblesShowcaseModel
|
||||
property string collectiblesSearcherText
|
||||
|
||||
// Output models
|
||||
readonly property alias collectiblesVisibleModel: collectibles.visibleModel
|
||||
|
@ -102,8 +105,20 @@ QObject {
|
|||
ProfileShowcaseDirtyState {
|
||||
id: communities
|
||||
|
||||
function getMemberRole(memberRole) {
|
||||
return ProfileUtils.getMemberRoleText(memberRole)
|
||||
}
|
||||
|
||||
sourceModel: modelAdapter.adaptedCommunitiesSourceModel
|
||||
showcaseModel: modelAdapter.adaptedCommunitiesShowcaseModel
|
||||
searcherFilter: FastExpressionFilter {
|
||||
expression: {
|
||||
root.communitiesSearcherText
|
||||
return (name.toLowerCase().includes(root.communitiesSearcherText.toLowerCase()) ||
|
||||
communities.getMemberRole(memberRole).toLowerCase().includes(root.communitiesSearcherText.toLowerCase()))
|
||||
}
|
||||
expectedRoles: ["name", "memberRole"]
|
||||
}
|
||||
}
|
||||
|
||||
ProfileShowcaseDirtyState {
|
||||
|
@ -111,6 +126,14 @@ QObject {
|
|||
|
||||
sourceModel: modelAdapter.adaptedAccountsSourceModel
|
||||
showcaseModel: modelAdapter.adaptedAccountsShowcaseModel
|
||||
searcherFilter: FastExpressionFilter {
|
||||
expression: {
|
||||
root.accountsSearcherText
|
||||
return (address.toLowerCase().includes(root.accountsSearcherText.toLowerCase()) ||
|
||||
name.toLowerCase().includes( root.accountsSearcherText.toLowerCase()))
|
||||
}
|
||||
expectedRoles: ["address", "name"]
|
||||
}
|
||||
}
|
||||
|
||||
ProfileShowcaseDirtyState {
|
||||
|
@ -118,6 +141,16 @@ QObject {
|
|||
|
||||
sourceModel: collectiblesFilter
|
||||
showcaseModel: modelAdapter.adaptedCollectiblesShowcaseModel
|
||||
searcherFilter: FastExpressionFilter {
|
||||
expression: {
|
||||
root.collectiblesSearcherText
|
||||
return (name.toLowerCase().includes(root.collectiblesSearcherText.toLowerCase()) ||
|
||||
uid.toLowerCase().includes(root.collectiblesSearcherText.toLowerCase()) ||
|
||||
communityName.toLowerCase().includes(root.collectiblesSearcherText.toLowerCase()) ||
|
||||
collectionName.toLowerCase().includes(root.collectiblesSearcherText.toLowerCase()))
|
||||
}
|
||||
expectedRoles: ["name", "uid", "collectionName", "communityName"]
|
||||
}
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
|
|
|
@ -7,6 +7,8 @@ import AppLayouts.Wallet 1.0
|
|||
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import StatusQ 0.1
|
||||
|
||||
ProfileShowcasePanel {
|
||||
id: root
|
||||
|
||||
|
@ -14,7 +16,8 @@ ProfileShowcasePanel {
|
|||
|
||||
emptyInShowcasePlaceholderText: qsTr("Accounts here will show on your profile")
|
||||
emptyHiddenPlaceholderText: qsTr("Accounts here will be hidden from your profile")
|
||||
|
||||
emptySearchPlaceholderText: qsTr("No accounts matching search")
|
||||
searchPlaceholderText: qsTr("Search account name or address")
|
||||
delegate: ProfileShowcasePanelDelegate {
|
||||
title: model ? model.name : ""
|
||||
secondaryTitle: WalletUtils.addressToDisplay(model ? model.address ?? "" : "", "", true, containsMouse)
|
||||
|
|
|
@ -21,7 +21,8 @@ ProfileShowcasePanel {
|
|||
|
||||
emptyInShowcasePlaceholderText: qsTr("Assets here will show on your profile")
|
||||
emptyHiddenPlaceholderText: qsTr("Assets here will be hidden from your profile")
|
||||
|
||||
emptySearchPlaceholderText: qsTr("No assets matching search")
|
||||
searchPlaceholderText: qsTr("Search asset name, symbol or community")
|
||||
delegate: ProfileShowcasePanelDelegate {
|
||||
|
||||
readonly property double totalValue: !!model && !!model.decimals ? balancesAggregator.value/(10 ** model.decimals): 0
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -19,7 +20,8 @@ ProfileShowcasePanel {
|
|||
|
||||
emptyInShowcasePlaceholderText: qsTr("Collectibles here will show on your profile")
|
||||
emptyHiddenPlaceholderText: qsTr("Collectibles here will be hidden from your profile")
|
||||
|
||||
emptySearchPlaceholderText: qsTr("No collectibles matching search")
|
||||
searchPlaceholderText: qsTr("Search collectible name, number, collection or community")
|
||||
additionalFooterComponent: root.addAccountsButtonVisible ? addMoreAccountsComponent : null
|
||||
|
||||
delegate: ProfileShowcasePanelDelegate {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import utils 1.0
|
||||
|
||||
import AppLayouts.Profile.controls 1.0
|
||||
|
@ -9,12 +10,11 @@ ProfileShowcasePanel {
|
|||
|
||||
emptyInShowcasePlaceholderText: qsTr("Drag communities here to display in showcase")
|
||||
emptyHiddenPlaceholderText: qsTr("Communities here will be hidden from your Profile")
|
||||
|
||||
emptySearchPlaceholderText: qsTr("No communities matching search")
|
||||
searchPlaceholderText: qsTr("Search community name or role")
|
||||
delegate: ProfileShowcasePanelDelegate {
|
||||
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")
|
||||
secondaryTitle: (model && model.memberRole) ? ProfileUtils.getMemberRoleText(model.memberRole) : qsTr("Member")
|
||||
hasImage: model && !!model.image
|
||||
|
||||
icon.name: model ? model.name : ""
|
||||
|
|
|
@ -4,16 +4,20 @@ import QtQuick.Layouts 1.15
|
|||
import QtQml 2.15
|
||||
import QtQml.Models 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
import utils 1.0
|
||||
|
||||
import AppLayouts.Profile.controls 1.0
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
DoubleFlickableWithFolding {
|
||||
id: root
|
||||
|
||||
|
@ -29,9 +33,14 @@ DoubleFlickableWithFolding {
|
|||
// Placeholder text to be shown when the list is empty
|
||||
property string emptyInShowcasePlaceholderText
|
||||
property string emptyHiddenPlaceholderText
|
||||
property string emptySearchPlaceholderText
|
||||
|
||||
property int showcaseLimit: ProfileUtils.showcaseLimit
|
||||
|
||||
// Searcher related properties:
|
||||
property string searchPlaceholderText
|
||||
property string searcherText: ""
|
||||
|
||||
// Signal to request position change of the visible items
|
||||
signal changePositionRequested(int from, int to)
|
||||
|
||||
|
@ -47,6 +56,7 @@ DoubleFlickableWithFolding {
|
|||
id: d
|
||||
|
||||
readonly property bool limitReached: root.showcaseLimit === inShowcaseCounterTracker.count
|
||||
readonly property bool searchActive: root.searcherText !== ""
|
||||
|
||||
readonly property var dragHiddenItemKey: ["x-status-draggable-showcase-item-hidden"]
|
||||
readonly property var dragShowcaseItemKey: ["x-status-draggable-showcase-item"]
|
||||
|
@ -84,18 +94,59 @@ DoubleFlickableWithFolding {
|
|||
flickable1: EmptyShapeRectangleFooterListView {
|
||||
id: inShowcaseListView
|
||||
|
||||
model: root.inShowcaseModel
|
||||
width: root.width
|
||||
placeholderText: root.emptyInShowcasePlaceholderText
|
||||
placeholderText: d.searchActive ? root.emptySearchPlaceholderText : root.emptyInShowcasePlaceholderText
|
||||
footerHeight: ProfileUtils.defaultDelegateHeight
|
||||
footerContentVisible: !dropAreaRow.visible
|
||||
spacing: Style.current.halfPadding
|
||||
delegate: delegateWrapper
|
||||
model: root.inShowcaseModel
|
||||
header: ColumnLayout {
|
||||
width: ListView.view.width
|
||||
spacing: 0
|
||||
|
||||
header: FoldableHeader {
|
||||
SearchBox {
|
||||
id: searcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: root.searchPlaceholderText
|
||||
validators: [
|
||||
StatusValidator {
|
||||
property bool isEmoji: false
|
||||
|
||||
name: "check-for-no-emojis"
|
||||
validate: (value) => {
|
||||
if (!value) {
|
||||
return true
|
||||
}
|
||||
|
||||
isEmoji = Constants.regularExpressions.emoji.test(value)
|
||||
if (isEmoji){
|
||||
return false
|
||||
}
|
||||
|
||||
return Constants.regularExpressions.alphanumericalExpanded1.test(value)
|
||||
}
|
||||
errorMessage: isEmoji ?
|
||||
qsTr("Your search is too cool (use A-Z and 0-9, hyphens and underscores only)")
|
||||
: qsTr("Your search contains invalid characters (use A-Z and 0-9, hyphens and underscores only)")
|
||||
}
|
||||
]
|
||||
|
||||
Binding {
|
||||
target: root
|
||||
property: "searcherText"
|
||||
value: searcher.text
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
}
|
||||
|
||||
FoldableHeader {
|
||||
readonly property bool isDropAreaVisible: root.flickable1Folded && d.isAnyHiddenDragActive
|
||||
|
||||
width: ListView.view.width
|
||||
Layout.fillWidth: true
|
||||
|
||||
title: qsTr("In showcase")
|
||||
folded: root.flickable1Folded
|
||||
rightAdditionalComponent: isDropAreaVisible && d.limitReached ? limitReachedHeaderButton :
|
||||
|
@ -109,6 +160,7 @@ DoubleFlickableWithFolding {
|
|||
width: d.additionalHeaderComponentWidth
|
||||
height: d.additionalHeaderComponentHeight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: "%1 / %2".arg(inShowcaseCounterTracker.count).arg(root.showcaseLimit)
|
||||
font.pixelSize: Style.current.tertiaryTextFontSize
|
||||
color: Theme.palette.baseColor1
|
||||
|
@ -155,6 +207,7 @@ DoubleFlickableWithFolding {
|
|||
|
||||
onToggleFolding: root.flip1Folding()
|
||||
}
|
||||
}
|
||||
|
||||
// Overlaid showcase listview content drop area:
|
||||
DropArea {
|
||||
|
@ -199,14 +252,14 @@ DoubleFlickableWithFolding {
|
|||
flickable2: EmptyShapeRectangleFooterListView {
|
||||
id: hiddenListView
|
||||
|
||||
model: root.hiddenModel
|
||||
width: root.width
|
||||
placeholderText: root.emptyHiddenPlaceholderText
|
||||
placeholderText: d.searchActive ? root.emptySearchPlaceholderText : root.emptyHiddenPlaceholderText
|
||||
footerHeight: ProfileUtils.defaultDelegateHeight
|
||||
footerContentVisible: !hiddenDropAreaButton.visible
|
||||
additionalFooterComponent: root.additionalFooterComponent
|
||||
spacing: Style.current.halfPadding
|
||||
delegate: delegateWrapper
|
||||
model: root.hiddenModel
|
||||
|
||||
header: FoldableHeader {
|
||||
width: ListView.view.width
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtQml 2.15
|
||||
|
||||
import utils 1.0
|
||||
import shared 1.0
|
||||
|
@ -129,12 +130,15 @@ SettingsContentBase {
|
|||
property ProfileShowcaseModels showcaseModels: ProfileShowcaseModels {
|
||||
communitiesSourceModel: root.communitiesModel
|
||||
communitiesShowcaseModel: root.profileStore.profileShowcaseCommunitiesModel
|
||||
communitiesSearcherText: profileShowcaseCommunitiesPanel.searcherText
|
||||
|
||||
accountsSourceModel: root.walletStore.accounts
|
||||
accountsShowcaseModel: root.profileStore.profileShowcaseAccountsModel
|
||||
accountsSearcherText: profileShowcaseAccountsPanel.searcherText
|
||||
|
||||
collectiblesSourceModel: root.profileStore.collectiblesModel
|
||||
collectiblesShowcaseModel: root.profileStore.profileShowcaseCollectiblesModel
|
||||
collectiblesSearcherText: profileShowcaseCollectiblesPanel.searcherText
|
||||
}
|
||||
|
||||
function reset() {
|
||||
|
|
|
@ -424,8 +424,6 @@ QtObject {
|
|||
readonly property QtObject memberRole: QtObject{
|
||||
readonly property int none: 0
|
||||
readonly property int owner: 1
|
||||
readonly property int manageUsers: 2
|
||||
readonly property int moderateContent: 3
|
||||
readonly property int admin: 4
|
||||
readonly property int tokenMaster: 5
|
||||
}
|
||||
|
|
|
@ -93,4 +93,17 @@ QtObject {
|
|||
return "hide"
|
||||
}
|
||||
}
|
||||
|
||||
// Member role names:
|
||||
function getMemberRoleText(memberRole) {
|
||||
switch(memberRole) {
|
||||
case Constants.memberRole.owner:
|
||||
return qsTr("Owner")
|
||||
case Constants.memberRole.admin:
|
||||
return qsTr("Admin")
|
||||
case Constants.memberRole.tokenMaster:
|
||||
return qsTr("TokenMaster")
|
||||
}
|
||||
return qsTr("Member")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue