feat(ProfileShowcase): Implement custom showcase position when searcher text is filtering items

This commit is contained in:
Alex Jbanca 2024-04-19 12:46:23 +03:00 committed by Alex Jbanca
parent e9127f9cd7
commit f3dc7b9918
10 changed files with 149 additions and 108 deletions

View File

@ -32,6 +32,7 @@ SplitView {
emoji: "🇨🇿"
walletType: ""
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
preferredSharingChainShortNames: "eth:opt:"
}
ListElement {
name: "testing (no emoji, colored, seed)"
@ -41,6 +42,7 @@ SplitView {
emoji: ""
walletType: "seed"
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
preferredSharingChainShortNames: "arb:xdai:"
}
ListElement {
name: "My Bro's Account"
@ -50,6 +52,7 @@ SplitView {
emoji: "🇸🇰"
walletType: "watch"
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
preferredSharingChainShortNames: "eth:opt:"
}
}
@ -65,6 +68,7 @@ SplitView {
walletType: ""
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcasePosition: 0
preferredSharingChainShortNames: "eth:opt:"
}
ListElement {
name: "testing (no emoji, colored, seed)"
@ -75,6 +79,7 @@ SplitView {
walletType: "seed"
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcasePosition: 1
preferredSharingChainShortNames: "eth:opt:arb:"
}
ListElement {
name: "My Bro's Account"
@ -85,6 +90,7 @@ SplitView {
walletType: "watch"
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcasePosition: 2
preferredSharingChainShortNames: "eth:opt:"
}
}

View File

@ -27,6 +27,7 @@ SplitView {
id: hiddenModelItem
readonly property var data: [
{
uid: "1234",
showcaseKey: "1234",
name: "SNTT",
collectionName: "Super Nitro Toluen (with pink bg)",
@ -34,18 +35,22 @@ SplitView {
imageUrl: ModelsData.collectibles.custom,
isLoading: false,
communityId: "ddls",
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
showcaseVisibility: Constants.ShowcaseVisibility.NoOne,
maxVisibility: Constants.ShowcaseVisibility.Everyone
},
{
uid: "34545656768",
showcaseKey: "3454565676",
name: "Kitty 3",
collectionName: "Kitties",
backgroundColor: "",
imageUrl: ModelsData.collectibles.kitty1Big,
isLoading: false,
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
showcaseVisibility: Constants.ShowcaseVisibility.NoOne,
maxVisibility: Constants.ShowcaseVisibility.Everyone
},
{
uid: "123456",
showcaseKey: "12345",
name: "Kitty 4",
collectionName: "",
@ -53,9 +58,11 @@ SplitView {
imageUrl: ModelsData.collectibles.kitty2Big,
isLoading: false,
communityId: "sox",
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
showcaseVisibility: Constants.ShowcaseVisibility.NoOne,
maxVisibility: Constants.ShowcaseVisibility.Everyone
},
{
uid: "12345645459537432",
showcaseKey: "123456454595374",
name: "",
collectionName: "Super Kitties",
@ -63,18 +70,22 @@ SplitView {
imageUrl: ModelsData.collectibles.kitty3Big,
isLoading: false,
communityId: "ast",
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
showcaseVisibility: Constants.ShowcaseVisibility.NoOne,
maxVisibility: Constants.ShowcaseVisibility.Everyone
},
{
uid: "6912",
showcaseKey: "6912",
name: "KILLABEAR",
collectionName: "KILLABEARS",
backgroundColor: "#807c56",
imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png",
isLoading: true,
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
showcaseVisibility: Constants.ShowcaseVisibility.NoOne,
maxVisibility: Constants.ShowcaseVisibility.IdVerifiedContacts
},
{
uid: "8876",
showcaseKey: "8876",
name: "AIORBIT",
description: "",
@ -82,7 +93,8 @@ SplitView {
backgroundColor: "",
imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg",
isLoading: false,
showcaseVisibility: Constants.ShowcaseVisibility.NoOne
showcaseVisibility: Constants.ShowcaseVisibility.NoOne,
maxVisibility: Constants.ShowcaseVisibility.Contacts
}
]
Component.onCompleted: append(data)
@ -101,7 +113,8 @@ SplitView {
imageUrl: ModelsData.collectibles.custom,
isLoading: false,
communityId: "ddls",
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcaseVisibility: Constants.ShowcaseVisibility.Contacts,
maxVisibility: Constants.ShowcaseVisibility.Contacts
},
{
uid: "34545656768",
@ -111,7 +124,8 @@ SplitView {
backgroundColor: "",
imageUrl: ModelsData.collectibles.kitty1Big,
isLoading: false,
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcaseVisibility: Constants.ShowcaseVisibility.Contacts,
maxVisibility: Constants.ShowcaseVisibility.Contacts
},
{
uid: "123456",
@ -122,7 +136,8 @@ SplitView {
imageUrl: ModelsData.collectibles.kitty2Big,
isLoading: false,
communityId: "sox",
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcaseVisibility: Constants.ShowcaseVisibility.IdVerifiedContacts,
maxVisibility: Constants.ShowcaseVisibility.Contacts
},
{
uid: "12345645459537432",
@ -133,7 +148,8 @@ SplitView {
imageUrl: ModelsData.collectibles.kitty3Big,
isLoading: false,
communityId: "ast",
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcaseVisibility: Constants.ShowcaseVisibility.Everyone,
maxVisibility: Constants.ShowcaseVisibility.Everyone
},
{
uid: "691",
@ -143,7 +159,8 @@ SplitView {
backgroundColor: "#807c56",
imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png",
isLoading: true,
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcaseVisibility: Constants.ShowcaseVisibility.Everyone,
maxVisibility: Constants.ShowcaseVisibility.Everyone
},
{
uid: "8876",
@ -154,7 +171,8 @@ SplitView {
backgroundColor: "",
imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg",
isLoading: false,
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
showcaseVisibility: Constants.ShowcaseVisibility.Everyone,
maxVisibility: Constants.ShowcaseVisibility.Everyone
}
]
Component.onCompleted: append(data)

View File

@ -23,7 +23,7 @@ QObject {
* Model holding elements from 'sourceModel' intended to be visible in the
* showcase, sorted by 'position' role. Includes roles from both input models.
*/
readonly property alias visibleModel: visible
readonly property alias visibleModel: visibleSFPM
/**
* Model holding elements from 'sourceModel' intended to be hidden, no
@ -36,13 +36,7 @@ QObject {
*/
readonly property bool dirty: writable.dirty
/**
* It sets up a searcher filter on top of both the visible and hidden models.
*/
property FastExpressionFilter searcherFilter
function revert() {
visible.syncOrder()
writable.revert()
}
@ -55,19 +49,8 @@ QObject {
}
function changePosition(from, to) {
visible.move(from, to)
// Sync writable with movable new positions:
const newOrder = visible.order()
let writableIndexes = []
for (var i = 0; i < newOrder.length; i++) {
writableIndexes.push(visibleSFPM.mapToSource(newOrder[i]))
}
for (var i = 0; i < newOrder.length; i++) {
writable.set(writableIndexes[i], { "showcasePosition": i})
}
const writableIndex = d.visibleIndexToWritable(from)
writable.changePosition(writableIndex, to)
}
function append(obj) {
@ -114,32 +97,10 @@ QObject {
sorters: RoleSorter { roleName: "showcasePosition" }
}
SortFilterProxyModel {
id: searcherVisibleSFPM
sourceModel: visibleSFPM
delayed: true
filters: root.searcherFilter
}
MovableModel {
id: visible
sourceModel: searcherVisibleSFPM
}
SortFilterProxyModel {
id: searcherHiddenSFPM
sourceModel: writable
delayed: true
filters: root.searcherFilter
}
SortFilterProxyModel {
id: hidden
sourceModel: searcherHiddenSFPM
sourceModel: writable
delayed: true
filters: HiddenFilter {}
@ -149,10 +110,7 @@ QObject {
id: d
function visibleIndexToWritable(index) {
const newOrder = visible.order()
const sfpmIndex = newOrder[index]
return visibleSFPM.mapToSource(sfpmIndex)
return visibleSFPM.mapToSource(index)
}
}
}

View File

@ -24,7 +24,6 @@ QObject {
// Input models
property alias communitiesSourceModel: communities.sourceModel
property string communitiesSearcherText
// Output models
readonly property alias communitiesVisibleModel: communities.visibleModel
@ -39,15 +38,14 @@ QObject {
communities.setVisibility(key, visibility)
}
function changeCommunityPosition(key, to) {
communities.changePosition(key, to)
function changeCommunityPosition(from, to) {
communities.changePosition(from, to)
}
// ACCOUNTS
// Input models
property alias accountsSourceModel: accounts.sourceModel
property string accountsSearcherText
// Output models
readonly property alias accountsVisibleModel: accounts.visibleModel
@ -62,8 +60,8 @@ QObject {
accounts.setVisibility(key, visibility)
}
function changeAccountPosition(key, to) {
accounts.changePosition(key, to)
function changeAccountPosition(from, to) {
accounts.changePosition(from, to)
}
// Other
@ -77,7 +75,6 @@ QObject {
// Input models
property alias collectiblesSourceModel: collectiblesFilter.sourceModel
property string collectiblesSearcherText
// Output models
readonly property alias collectiblesVisibleModel: collectibles.visibleModel
@ -138,49 +135,16 @@ QObject {
ProfileShowcaseDirtyState {
id: communities
function getMemberRole(memberRole) {
return ProfileUtils.getMemberRoleText(memberRole)
}
searcherFilter: FastExpressionFilter {
expression: {
root.communitiesSearcherText
return (name.toLowerCase().includes(root.communitiesSearcherText.toLowerCase()) ||
communities.getMemberRole(memberRole).toLowerCase().includes(root.communitiesSearcherText.toLowerCase()))
}
expectedRoles: ["name", "memberRole"]
}
}
ProfileShowcaseDirtyState {
id: accounts
searcherFilter: FastExpressionFilter {
expression: {
root.accountsSearcherText
return (address.toLowerCase().includes(root.accountsSearcherText.toLowerCase()) ||
name.toLowerCase().includes(root.accountsSearcherText.toLowerCase()) ||
preferredSharingChainShortNames.toLowerCase().includes(root.accountsSearcherText.toLowerCase()))
}
expectedRoles: ["address", "name", "preferredSharingChainShortNames"]
}
}
ProfileShowcaseDirtyState {
id: collectibles
sourceModel: collectiblesFilter
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"]
}
}
ProfileShowcaseDirtyState {

View File

@ -66,6 +66,30 @@ WritableProxyModel {
set(sourceIdx, { showcaseVisibility: visibility })
}
/* Sets the showcasePosition of the item. The "toShowcasePosition" is the
* new position of the item. All items in between the previous and the
* new position are moved accordingly.
*/
function changePosition(index, toShowcasePosition) {
// changing the position of the items in between
const fromShowcasePosition = get(index).showcasePosition
const minPosition = Math.min(fromShowcasePosition, toShowcasePosition)
const maxPosition = Math.max(fromShowcasePosition, toShowcasePosition)
const visible = d.getVisibleEntries()
visible.sort((a, b) => a.showcasePosition - b.showcasePosition)
.filter(e => e.showcasePosition >= minPosition && e.showcasePosition <= maxPosition && e.index !== index)
.forEach(e => {
e.showcasePosition += (fromShowcasePosition > toShowcasePosition ? 1 : -1)
set(e.index, { showcasePosition: e.showcasePosition })
})
//changing the position of the item
set(index, { showcasePosition: toShowcasePosition })
}
syncedRemovals: true
readonly property QtObject d_: QtObject {
@ -83,6 +107,7 @@ WritableProxyModel {
roleNames.push("showcaseVisibility")
const keysAndPos = ModelUtils.modelToArray(root, roleNames)
keysAndPos.forEach((e, i) => e.index = i)
return keysAndPos.filter(p => p.showcaseVisibility
&& p.showcaseVisibility !== root.visibilityHidden)

View File

@ -30,4 +30,14 @@ ProfileShowcasePanel {
icon.color: model && model.colorId ? Utils.getColorForId(model.colorId) : Theme.palette.primaryColor3
highlighted: model ? model.address === root.currentWallet : false
}
filter: FastExpressionFilter {
readonly property string lowerCaseSearchText: root.searcherText.toLowerCase()
expression: {
lowerCaseSearchText
return (address.toLowerCase().includes(lowerCaseSearchText) ||
name.toLowerCase().includes(lowerCaseSearchText) ||
preferredSharingChainShortNames.toLowerCase().includes(lowerCaseSearchText))
}
expectedRoles: ["address", "name", "preferredSharingChainShortNames"]
}
}

View File

@ -53,6 +53,18 @@ ProfileShowcasePanel {
}
}
filter: FastExpressionFilter {
readonly property string lowerCaseSearchText: root.searcherText.toLowerCase()
expression: {
lowerCaseSearchText
return (name.toLowerCase().includes(lowerCaseSearchText) ||
uid.toLowerCase().includes(lowerCaseSearchText) ||
(!!communityName && communityName.toLowerCase().includes(lowerCaseSearchText)) ||
(!!collectionName && collectionName.toLowerCase().includes(lowerCaseSearchText)))
}
expectedRoles: ["name", "uid", "collectionName", "communityName"]
}
Component {
id: addMoreAccountsComponent

View File

@ -21,4 +21,19 @@ ProfileShowcasePanel {
icon.source: model ? model.image : ""
icon.color: model ? model.color : ""
}
filter: FastExpressionFilter {
readonly property string lowerCaseSearchText: root.searcherText.toLowerCase()
function getMemberRole(memberRole) {
return ProfileUtils.getMemberRoleText(memberRole)
}
expression: {
lowerCaseSearchText
return (name.toLowerCase().includes(lowerCaseSearchText) ||
getMemberRole(memberRole).toLowerCase().includes(lowerCaseSearchText))
}
expectedRoles: ["name", "memberRole"]
}
}

View File

@ -39,7 +39,10 @@ DoubleFlickableWithFolding {
// Searcher related properties:
property string searchPlaceholderText
property string searcherText: ""
readonly property alias searcherText: d.searcherText
//SFPM filters to apply to both in showcase and hidden models
property FastExpressionFilter filter
// Signal to request position change of the visible items
signal changePositionRequested(int from, int to)
@ -57,11 +60,19 @@ DoubleFlickableWithFolding {
readonly property bool limitReached: root.showcaseLimit === inShowcaseCounterTracker.count
readonly property bool searchActive: root.searcherText !== ""
property string searcherText: ""
readonly property var dragHiddenItemKey: ["x-status-draggable-showcase-item-hidden"]
readonly property var dragShowcaseItemKey: ["x-status-draggable-showcase-item"]
property bool isAnyShowcaseDragActive: false
onIsAnyShowcaseDragActiveChanged: {
if(!isAnyShowcaseDragActive) {
// Sync the order of the visible items when the drag is finished
// MovableModel is used only for DND operations. No interference needed when the DND is not active
visibleModel.syncOrder()
}
}
property bool isAnyHiddenDragActive: false
property int additionalHeaderComponentWidth: 350 // by design
@ -91,12 +102,34 @@ DoubleFlickableWithFolding {
model: root.inShowcaseModel
}
SortFilterProxyModel {
id: inShowcaseSFPM
sourceModel: root.inShowcaseModel
delayed: true
filters: root.filter
}
SortFilterProxyModel {
id: hiddenSFPM
sourceModel: root.hiddenModel
delayed: true
filters: root.filter
}
MovableModel {
id: visibleModel
sourceModel: inShowcaseSFPM
}
clip: true
flickable1: EmptyShapeRectangleFooterListView {
id: inShowcaseListView
model: root.inShowcaseModel
model: visibleModel
width: root.width
placeholderText: d.searchActive ? root.emptySearchPlaceholderText : root.emptyInShowcasePlaceholderText
footerHeight: ProfileUtils.defaultDelegateHeight
@ -137,7 +170,7 @@ DoubleFlickableWithFolding {
]
Binding {
target: root
target: d
property: "searcherText"
value: searcher.text
restoreMode: Binding.RestoreBindingOrValue
@ -254,7 +287,7 @@ DoubleFlickableWithFolding {
flickable2: EmptyShapeRectangleFooterListView {
id: hiddenListView
model: root.hiddenModel
model: hiddenSFPM
width: root.width
placeholderText: d.searchActive ? root.emptySearchPlaceholderText : root.emptyHiddenPlaceholderText
footerHeight: ProfileUtils.defaultDelegateHeight
@ -442,7 +475,11 @@ DoubleFlickableWithFolding {
var to = visualIndex
if (to === from)
return
root.changePositionRequested(drag.source.visualIndex, to)
const sourceIndex = inShowcaseSFPM.mapToSource(visibleModel.order()[from])
const targetPosition = showcaseDelegateRoot.model.showcasePosition
visibleModel.move(from, to)
root.changePositionRequested(sourceIndex, targetPosition)
}
drag.accept()
}

View File

@ -138,10 +138,6 @@ SettingsContentBase {
property ProfileShowcaseModels showcaseModels: ProfileShowcaseModels {
id: showcaseModels
communitiesSearcherText: profileShowcaseCommunitiesPanel.searcherText
accountsSearcherText: profileShowcaseAccountsPanel.searcherText
collectiblesSearcherText: profileShowcaseCollectiblesPanel.searcherText
}
// Used to track which are the expected backend responses (they can be 0, 1 or 2) depending on the dirty changes