feat(ProfileShowcase): Create dirty state component for the web tab and integrate the UI
This commit is contained in:
parent
282c4e56cd
commit
4ef30a91a3
|
@ -24,6 +24,18 @@ ColumnLayout {
|
|||
ListElement { address: "4"; name: "Other Stuff" }
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: socialLinksModel
|
||||
|
||||
ListElement { uuid: "1"; text: "Twitter"; url: "https://twitter.com/status" }
|
||||
ListElement { uuid: "2"; text: "Personal Site"; url: "https://status.im" }
|
||||
ListElement { uuid: "3"; text: "Github"; url: "https://github.com" }
|
||||
ListElement { uuid: "4"; text: "Youtube"; url: "https://youtube.com" }
|
||||
ListElement { uuid: "5"; text: "Discord"; url: "https://discord.com" }
|
||||
ListElement { uuid: "6"; text: "Telegram"; url: "https://t.me/status" }
|
||||
ListElement { uuid: "7"; text: "Custom"; url: "https://status.im" }
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: accountsShowcaseModel
|
||||
ListElement {
|
||||
|
@ -173,6 +185,8 @@ ColumnLayout {
|
|||
|
||||
collectiblesSourceModel: collectiblesModel
|
||||
collectiblesShowcaseModel: collectiblesShowcaseModel
|
||||
|
||||
socialLinksSourceModel: socialLinksModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
|
@ -199,208 +213,277 @@ ColumnLayout {
|
|||
valueRole: "value"
|
||||
}
|
||||
|
||||
Flickable {
|
||||
StackView {
|
||||
id: stackView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: 10
|
||||
|
||||
contentWidth: grid.width
|
||||
contentHeight: grid.height
|
||||
initialItem: collectiblesView
|
||||
|
||||
Component {
|
||||
id: collectiblesView
|
||||
Flickable {
|
||||
|
||||
clip: true
|
||||
contentWidth: grid.width
|
||||
contentHeight: grid.height
|
||||
|
||||
Grid {
|
||||
id: grid
|
||||
clip: true
|
||||
|
||||
rows: 3
|
||||
columns: 4
|
||||
Grid {
|
||||
id: grid
|
||||
|
||||
spacing: 10
|
||||
rows: 3
|
||||
columns: 4
|
||||
|
||||
flow: Grid.TopToBottom
|
||||
spacing: 10
|
||||
|
||||
Label {
|
||||
text: "Backend models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
flow: Grid.TopToBottom
|
||||
|
||||
GenericListView {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
model: accountsModel
|
||||
label: "ACCOUNTS MODEL"
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
model: accountsShowcaseModel
|
||||
label: "SHOWCASE MODEL"
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Display models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 420
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.accountsVisibleModel
|
||||
label: "IN SHOWCASE"
|
||||
movable: true
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
|
||||
onMoveRequested: {
|
||||
showcaseModels.changeAccountPosition(from, to);
|
||||
}
|
||||
|
||||
insetComponent: RowLayout {
|
||||
readonly property var topModel: model
|
||||
|
||||
RoundButton {
|
||||
text: "❌"
|
||||
onClicked: showcaseModels.setAccountVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.NoOne)
|
||||
Label {
|
||||
text: "Backend models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
VisibilityComboBox {
|
||||
property bool completed: false
|
||||
GenericListView {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
onCurrentValueChanged: {
|
||||
if (!completed || topModel.index < 0)
|
||||
return
|
||||
model: accountsModel
|
||||
label: "ACCOUNTS MODEL"
|
||||
}
|
||||
|
||||
showcaseModels.setAccountVisibility(
|
||||
topModel.showcaseKey, currentValue)
|
||||
GenericListView {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
model: accountsShowcaseModel
|
||||
label: "SHOWCASE MODEL"
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Display models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 420
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.accountsVisibleModel
|
||||
label: "IN SHOWCASE"
|
||||
movable: true
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
|
||||
onMoveRequested: {
|
||||
showcaseModels.changeAccountPosition(from, to);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
currentIndex = indexOfValue(topModel.showcaseVisibility)
|
||||
completed = true
|
||||
insetComponent: RowLayout {
|
||||
readonly property var topModel: model
|
||||
|
||||
RoundButton {
|
||||
text: "❌"
|
||||
onClicked: showcaseModels.setAccountVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.NoOne)
|
||||
}
|
||||
|
||||
VisibilityComboBox {
|
||||
property bool completed: false
|
||||
|
||||
onCurrentValueChanged: {
|
||||
if (!completed || topModel.index < 0)
|
||||
return
|
||||
|
||||
showcaseModels.setAccountVisibility(
|
||||
topModel.showcaseKey, currentValue)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
currentIndex = indexOfValue(topModel.showcaseVisibility)
|
||||
completed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 420
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.accountsHiddenModel
|
||||
|
||||
label: "HIDDEN"
|
||||
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
|
||||
insetComponent: Button {
|
||||
text: "unhide"
|
||||
|
||||
onClicked:
|
||||
showcaseModels.setAccountVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.IdVerifiedContacts)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Backend models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 270
|
||||
height: 300
|
||||
|
||||
model: collectiblesModel
|
||||
label: "COLLECTIBLES MODEL"
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 270
|
||||
height: 300
|
||||
|
||||
model: collectiblesShowcaseModel
|
||||
label: "SHOWCASE MODEL"
|
||||
roles: ["uid", "showcaseVisibility", "order"]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Display models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 610
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.collectiblesVisibleModel
|
||||
label: "IN SHOWCASE"
|
||||
movable: true
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
|
||||
onMoveRequested: {
|
||||
showcaseModels.changeCollectiblePosition(from, to);
|
||||
}
|
||||
|
||||
insetComponent: RowLayout {
|
||||
readonly property var topModel: model
|
||||
|
||||
RoundButton {
|
||||
text: "❌"
|
||||
onClicked: showcaseModels.setCollectibleVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.NoOne)
|
||||
}
|
||||
|
||||
VisibilityComboBox {
|
||||
property bool completed: false
|
||||
|
||||
onCurrentValueChanged: {
|
||||
if (!completed || topModel.index < 0)
|
||||
return
|
||||
|
||||
showcaseModels.setCollectibleVisibility(
|
||||
topModel.showcaseKey, currentValue)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
currentIndex = indexOfValue(topModel.showcaseVisibility)
|
||||
completed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 610
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.collectiblesHiddenModel
|
||||
|
||||
label: "HIDDEN"
|
||||
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition",
|
||||
"accounts", "maxVisibility"]
|
||||
|
||||
insetComponent: Button {
|
||||
text: "unhide"
|
||||
|
||||
onClicked:
|
||||
showcaseModels.setCollectibleVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.IdVerifiedContacts)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: webView
|
||||
Flickable {
|
||||
|
||||
GenericListView {
|
||||
width: 420
|
||||
height: 300
|
||||
contentWidth: webGrid.implicitWidth
|
||||
contentHeight: webGrid.implicitHeight
|
||||
|
||||
model: showcaseModels.accountsHiddenModel
|
||||
Grid {
|
||||
id: webGrid
|
||||
|
||||
label: "HIDDEN"
|
||||
rows: 3
|
||||
columns: 4
|
||||
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
spacing: 10
|
||||
|
||||
insetComponent: Button {
|
||||
text: "unhide"
|
||||
flow: Grid.TopToBottom
|
||||
|
||||
onClicked:
|
||||
showcaseModels.setAccountVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.IdVerifiedContacts)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Backend models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 270
|
||||
height: 300
|
||||
|
||||
model: collectiblesModel
|
||||
label: "COLLECTIBLES MODEL"
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 270
|
||||
height: 300
|
||||
|
||||
model: collectiblesShowcaseModel
|
||||
label: "SHOWCASE MODEL"
|
||||
roles: ["uid", "showcaseVisibility", "order"]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Display models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 610
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.collectiblesVisibleModel
|
||||
label: "IN SHOWCASE"
|
||||
movable: true
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition"]
|
||||
|
||||
onMoveRequested: {
|
||||
showcaseModels.changeCollectiblePosition(from, to);
|
||||
}
|
||||
|
||||
insetComponent: RowLayout {
|
||||
readonly property var topModel: model
|
||||
|
||||
RoundButton {
|
||||
text: "❌"
|
||||
onClicked: showcaseModels.setCollectibleVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.NoOne)
|
||||
Label {
|
||||
text: "Backend models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
VisibilityComboBox {
|
||||
property bool completed: false
|
||||
GenericListView {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
onCurrentValueChanged: {
|
||||
if (!completed || topModel.index < 0)
|
||||
return
|
||||
model: socialLinksModel
|
||||
label: "SOCIAL LINKS MODEL"
|
||||
}
|
||||
|
||||
showcaseModels.setCollectibleVisibility(
|
||||
topModel.showcaseKey, currentValue)
|
||||
}
|
||||
Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
currentIndex = indexOfValue(topModel.showcaseVisibility)
|
||||
completed = true
|
||||
width: 300
|
||||
height: 300
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Display models"
|
||||
font.pixelSize: 22
|
||||
padding: 10
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 610
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.socialLinksVisibleModel
|
||||
|
||||
label: "IN SHOWCASE"
|
||||
movable: true
|
||||
|
||||
onMoveRequested: {
|
||||
showcaseModels.changeSocialLinkPosition(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
width: 610
|
||||
height: 300
|
||||
|
||||
model: showcaseModels.collectiblesHiddenModel
|
||||
|
||||
label: "HIDDEN"
|
||||
|
||||
roles: ["showcaseKey", "showcaseVisibility", "showcasePosition",
|
||||
"accounts", "maxVisibility"]
|
||||
|
||||
insetComponent: Button {
|
||||
text: "unhide"
|
||||
|
||||
onClicked:
|
||||
showcaseModels.setCollectibleVisibility(
|
||||
model.showcaseKey,
|
||||
Constants.ShowcaseVisibility.IdVerifiedContacts)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,6 +500,28 @@ ColumnLayout {
|
|||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Button {
|
||||
text: "Collectibles tab"
|
||||
onClicked: {
|
||||
stackView.replace(webView, collectiblesView)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Web tab"
|
||||
onClicked: {
|
||||
stackView.replace(collectiblesView, webView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Button {
|
||||
text: "SAVE"
|
||||
//TODO: enable when showcaseModels backend APIs is integrated
|
||||
|
|
|
@ -25,56 +25,46 @@ SplitView {
|
|||
communityTokensStore: CommunityTokensStore {}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: emptyModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: linksModel
|
||||
ListElement {
|
||||
uuid: "0001"
|
||||
text: "__github"
|
||||
url: "https://github.com/caybro"
|
||||
linkType: 3 // Constants.socialLinkType.github
|
||||
icon: "github"
|
||||
}
|
||||
ListElement {
|
||||
uuid: "0002"
|
||||
text: "__twitter"
|
||||
url: "https://twitter.com/caybro"
|
||||
linkType: 1 // Constants.socialLinkType.twitter
|
||||
icon: "twitter"
|
||||
}
|
||||
ListElement {
|
||||
uuid: "0003"
|
||||
text: "__personal_site"
|
||||
url: "https://status.im"
|
||||
linkType: 2 // Constants.socialLinkType.personalSite
|
||||
icon: "language"
|
||||
}
|
||||
ListElement {
|
||||
uuid: "0004"
|
||||
text: "__youtube"
|
||||
url: "https://www.youtube.com/@LukasTinkl"
|
||||
linkType: 4 // Constants.socialLinkType.youtube
|
||||
icon: "youtube"
|
||||
}
|
||||
ListElement { // NB: empty on purpose, for testing
|
||||
uuid: ""
|
||||
text: ""
|
||||
url: ""
|
||||
linkType: -1
|
||||
icon: ""
|
||||
}
|
||||
ListElement {
|
||||
uuid: "0005"
|
||||
text: "Figma design very long URL link text that should elide"
|
||||
url: "https://www.figma.com/file/idUoxN7OIW2Jpp3PMJ1Rl8/%E2%9A%99%EF%B8%8F-Settings-%7C-Desktop?node-id=1223%3A124882&t=qvYeJ8grsZLyUS0V-0"
|
||||
linkType: 0 // Constants.socialLinkType.custom
|
||||
icon: "link"
|
||||
}
|
||||
ListElement {
|
||||
uuid: "0006"
|
||||
text: "__telegram"
|
||||
url: "https://t.me/ltinkl"
|
||||
linkType: 6 // Constants.socialLinkType.telegram
|
||||
icon: "telegram"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,47 +79,31 @@ SplitView {
|
|||
SplitView.fillWidth: true
|
||||
SplitView.preferredHeight: 300
|
||||
ProfileSocialLinksPanel {
|
||||
id: socialLinksPanel
|
||||
property var linksModel: emptyModelCheck.checked ? emptyModel : linksModel
|
||||
width: 500
|
||||
profileStore: QtObject {
|
||||
function createLink(text, url, linkType, icon) {
|
||||
logs.logEvent("ProfileStore::createLink", ["text", "url", "linkType", "icon"], arguments)
|
||||
linksModel.append({text, url, linkType, icon})
|
||||
}
|
||||
|
||||
function removeLink(uuid) {
|
||||
logs.logEvent("ProfileStore::removeLink", ["uuid"], arguments)
|
||||
const idx = CoreUtils.ModelUtils.indexOf(linksModel, "uuid", uuid)
|
||||
if (idx === -1)
|
||||
return
|
||||
linksModel.remove(idx, 1)
|
||||
}
|
||||
|
||||
function updateLink(uuid, text, url) {
|
||||
logs.logEvent("ProfileStore::updateLink", ["uuid", "text", "url"], arguments)
|
||||
const idx = CoreUtils.ModelUtils.indexOf(linksModel, "uuid", uuid)
|
||||
if (idx === -1)
|
||||
return
|
||||
if (!!text)
|
||||
linksModel.setProperty(idx, "text", text)
|
||||
if (!!url)
|
||||
linksModel.setProperty(idx, "url", url)
|
||||
}
|
||||
|
||||
function moveLink(fromRow, toRow, count) {
|
||||
logs.logEvent("ProfileStore::moveLink", ["fromRow", "toRow", "count"], arguments)
|
||||
linksModel.move(fromRow, toRow, 1)
|
||||
}
|
||||
|
||||
function resetSocialLinks() {
|
||||
logs.logEvent("ProfileStore::resetSocialLinks")
|
||||
}
|
||||
|
||||
function saveSocialLinks(silent = false) {
|
||||
logs.logEvent("ProfileStore::saveSocialLinks", ["silent"], arguments)
|
||||
}
|
||||
onAddSocialLink: {
|
||||
logs.logEvent("ProfileSocialLinksPanel::addSocialLink", ["url", "text"], arguments)
|
||||
socialLinksPanel.linksModel.append({text: text, url: url})
|
||||
}
|
||||
onUpdateSocialLink: {
|
||||
logs.logEvent("ProfileSocialLinksPanel::updateSocialLink", ["index", "url", "text"], arguments)
|
||||
if (!!text)
|
||||
socialLinksPanel.linksModel.setProperty(index, "text", text)
|
||||
if (!!url)
|
||||
socialLinksPanel.linksModel.setProperty(index, "url", url)
|
||||
}
|
||||
onRemoveSocialLink: {
|
||||
logs.logEvent("ProfileSocialLinksPanel::removeSocialLink", ["index"], arguments)
|
||||
socialLinksPanel.linksModel.remove(index, 1)
|
||||
}
|
||||
onChangePosition: {
|
||||
logs.logEvent("ProfileSocialLinksPanel::changePosition", ["from", "to"], arguments)
|
||||
socialLinksPanel.linksModel.move(from, to, 1)
|
||||
}
|
||||
|
||||
socialLinksModel: linksModel
|
||||
socialLinksModel: socialLinksPanel.linksModel
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,6 +114,13 @@ SplitView {
|
|||
SplitView.preferredHeight: 200
|
||||
|
||||
logsView.logText: logs.logText
|
||||
|
||||
|
||||
CheckBox {
|
||||
id: emptyModelCheck
|
||||
text: "emptyModel"
|
||||
checked: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -122,17 +122,22 @@ Item {
|
|||
property bool isInputValidWord: false
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: seedWordInput
|
||||
|
||||
implicitWidth: parent.width
|
||||
input.leftComponent: StatusBaseText {
|
||||
Component {
|
||||
id: seedInputLeftComponent
|
||||
StatusBaseText {
|
||||
rightPadding: 6
|
||||
text: root.leftComponentText
|
||||
color: seedWordInput.input.edit.activeFocus ?
|
||||
Theme.palette.primaryColor1 : Theme.palette.baseColor1
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: seedWordInput
|
||||
|
||||
implicitWidth: parent.width
|
||||
input.leftComponent: seedInputLeftComponent
|
||||
input.acceptReturn: true
|
||||
onTextChanged: {
|
||||
d.isInputValidWord = false
|
||||
|
|
|
@ -21,6 +21,13 @@ QObject {
|
|||
property alias sourceModel: joined.leftModel
|
||||
property alias showcaseModel: joined.rightModel
|
||||
|
||||
/**
|
||||
* True if the showcase model is in single model mode, i.e. the showcase
|
||||
* model is part of the source model. False if the showcase model is a
|
||||
* separate model.
|
||||
*/
|
||||
property bool singleModelMode: !joined.rightModel
|
||||
|
||||
/**
|
||||
* Model holding elements from 'sourceModel' intended to be visible in the
|
||||
* showcase, sorted by 'position' role. Includes roles from both input models.
|
||||
|
@ -48,8 +55,8 @@ QObject {
|
|||
writable.revert()
|
||||
}
|
||||
|
||||
function currentState() {
|
||||
return writable.currentState()
|
||||
function currentState(roleNames = []) {
|
||||
return writable.currentState(roleNames)
|
||||
}
|
||||
|
||||
function setVisibility(key, visibility) {
|
||||
|
@ -67,11 +74,25 @@ QObject {
|
|||
writableIndexes.push(visibleSFPM.mapToSource(newOrder[i]))
|
||||
}
|
||||
|
||||
for (var j = 0; j < newOrder.length; j++) {
|
||||
writable.set(writableIndexes[j], { "showcasePosition": j})
|
||||
for (var i = 0; i < newOrder.length; i++) {
|
||||
writable.set(writableIndexes[i], { "showcasePosition": i})
|
||||
}
|
||||
}
|
||||
|
||||
function append(obj) {
|
||||
writable.append(obj)
|
||||
}
|
||||
|
||||
function remove(index) {
|
||||
const writableIndex = d.visibleIndexToWritable(index)
|
||||
writable.remove(writableIndex)
|
||||
}
|
||||
|
||||
function update(index, obj) {
|
||||
const writableIndex = d.visibleIndexToWritable(index)
|
||||
writable.set(writableIndex, obj)
|
||||
}
|
||||
|
||||
// internals, debug purpose only
|
||||
readonly property alias writable_: writable
|
||||
readonly property alias joined_: joined
|
||||
|
@ -96,7 +117,7 @@ QObject {
|
|||
VisibilityAndPositionDirtyStateModel {
|
||||
id: writable
|
||||
|
||||
sourceModel: joined
|
||||
sourceModel: root.singleModelMode ? root.sourceModel : joined
|
||||
visibilityHidden: Constants.ShowcaseVisibility.NoOne
|
||||
}
|
||||
|
||||
|
@ -140,4 +161,15 @@ QObject {
|
|||
|
||||
filters: HiddenFilter {}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
function visibleIndexToWritable(index) {
|
||||
const newOrder = visible.order()
|
||||
const sfpmIndex = newOrder[index]
|
||||
|
||||
return visibleSFPM.mapToSource(sfpmIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import QtQml 2.15
|
||||
import QtQml.Models 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import utils 1.0
|
||||
|
||||
QObject {
|
||||
id: root
|
||||
|
||||
|
@ -32,6 +35,11 @@ QObject {
|
|||
readonly property alias adaptedCollectiblesSourceModel: collectiblesSFPM
|
||||
readonly property alias adaptedCollectiblesShowcaseModel: collectiblesRenamingShowcase
|
||||
|
||||
// Social links input models
|
||||
property alias socialLinksSourceModel: socialLinksRoleRenaming.sourceModel
|
||||
|
||||
// adapted models
|
||||
readonly property alias adaptedSocialLinksSourceModel: socialLinksSFPM
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: communitySFPM
|
||||
|
@ -192,4 +200,32 @@ QObject {
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
RolesRenamingModel {
|
||||
id: socialLinksRoleRenaming
|
||||
mapping: [
|
||||
RoleRename {
|
||||
from: "uuid"
|
||||
to: "showcaseKey"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: socialLinksSFPM
|
||||
sourceModel: socialLinksRoleRenaming
|
||||
proxyRoles: [
|
||||
FastExpressionRole {
|
||||
name: "showcasePosition"
|
||||
expression: index
|
||||
},
|
||||
FastExpressionRole {
|
||||
name: "showcaseVisibility"
|
||||
expression: getShowcaseVisibility()
|
||||
function getShowcaseVisibility() {
|
||||
return Constants.ShowcaseVisibility.Everyone
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@ QObject {
|
|||
id: root
|
||||
|
||||
// GENERAL
|
||||
readonly property bool dirty: communities.dirty || accounts.dirty || collectibles.dirty
|
||||
readonly property bool dirty: communities.dirty || accounts.dirty || collectibles.dirty || socialLinks.dirty
|
||||
|
||||
function revert() {
|
||||
communities.revert()
|
||||
accounts.revert()
|
||||
collectibles.revert()
|
||||
socialLinks.revert()
|
||||
}
|
||||
|
||||
// COMMUNITIES
|
||||
|
@ -98,14 +99,43 @@ QObject {
|
|||
collectibles.changePosition(from, to)
|
||||
}
|
||||
|
||||
// SOCIAL LINKS
|
||||
|
||||
// Input models
|
||||
property alias socialLinksSourceModel: modelAdapter.socialLinksSourceModel
|
||||
|
||||
// Output models
|
||||
readonly property alias socialLinksVisibleModel: socialLinks.visibleModel
|
||||
|
||||
// Methods
|
||||
function appendSocialLink(obj) {
|
||||
socialLinks.append(obj)
|
||||
}
|
||||
|
||||
function updateSocialLink(index, obj) {
|
||||
socialLinks.update(index, obj)
|
||||
}
|
||||
|
||||
function removeSocialLink(index) {
|
||||
socialLinks.remove(index)
|
||||
}
|
||||
|
||||
function changeSocialLinkPosition(from, to) {
|
||||
socialLinks.changePosition(from, to)
|
||||
}
|
||||
|
||||
function socialLinksCurrentState(roleNames) {
|
||||
return socialLinks.currentState(roleNames)
|
||||
}
|
||||
|
||||
// The complete preferences models json current state:
|
||||
function buildJSONModelsCurrentState() {
|
||||
return JSON.stringify({
|
||||
"communities": communitiesCurrentState(),
|
||||
"accounts": accountsCurrentState(),
|
||||
"collectibles": collectiblesCurrentState()
|
||||
"collectibles": collectiblesCurrentState(),
|
||||
"socialLinks": socialLinksCurrentState(["url", "text", "showcaseVisibility", "showcasePosition"])
|
||||
// TODO: Assets --> Issue #13492
|
||||
// TODO: Web --> Issue #13495
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -164,6 +194,14 @@ QObject {
|
|||
}
|
||||
}
|
||||
|
||||
ProfileShowcaseDirtyState {
|
||||
id: socialLinks
|
||||
|
||||
sourceModel: modelAdapter.adaptedSocialLinksSourceModel
|
||||
singleModelMode: true
|
||||
}
|
||||
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: collectiblesFilter
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ WritableProxyModel {
|
|||
*
|
||||
* The entries with visibility 0 (hidden) are not included in the list.
|
||||
*/
|
||||
function currentState() {
|
||||
const visible = d.getVisibleEntries()
|
||||
function currentState(roleNames) {
|
||||
const visible = d.getVisibleEntries(roleNames)
|
||||
const minPos = Math.min(...visible.map(e => e.showcasePosition))
|
||||
|
||||
return visible.map(e => { e.showcasePosition -= minPos; return e })
|
||||
|
@ -75,9 +75,14 @@ WritableProxyModel {
|
|||
return ModelUtils.indexOf(root, "showcaseKey", key)
|
||||
}
|
||||
|
||||
function getVisibleEntries() {
|
||||
const roles = ["showcaseKey", "showcasePosition", "showcaseVisibility"]
|
||||
const keysAndPos = ModelUtils.modelToArray(root, roles)
|
||||
function getVisibleEntries(roleNames = ["showcaseKey", "showcasePosition", "showcaseVisibility"]) {
|
||||
if (roleNames.length === 0)
|
||||
roleNames = ["showcaseKey", "showcasePosition", "showcaseVisibility"]
|
||||
|
||||
if (!roleNames.includes("showcaseVisibility"))
|
||||
roleNames.push("showcaseVisibility")
|
||||
|
||||
const keysAndPos = ModelUtils.modelToArray(root, roleNames)
|
||||
|
||||
return keysAndPos.filter(p => p.showcaseVisibility
|
||||
&& p.showcaseVisibility !== root.visibilityHidden)
|
||||
|
|
|
@ -3,6 +3,7 @@ import QtQuick.Controls 2.15
|
|||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
@ -17,27 +18,40 @@ import SortFilterProxyModel 0.2
|
|||
|
||||
Control {
|
||||
id: root
|
||||
|
||||
property var profileStore
|
||||
|
||||
property var socialLinksModel
|
||||
property int showcaseLimit: 20
|
||||
|
||||
background: null
|
||||
|
||||
signal addSocialLink(string url, string text)
|
||||
signal updateSocialLink(int index, string url, string text)
|
||||
signal removeSocialLink(int index)
|
||||
signal changePosition(int from, int to)
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
function containsSocialLink(text, url) {
|
||||
return ModelUtils.contains(socialLinksModel, "text", text, Qt.CaseInsensitive) &&
|
||||
ModelUtils.contains(socialLinksModel, "url", url, Qt.CaseInsensitive)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: addSocialLinkModalComponent
|
||||
AddSocialLinkModal {
|
||||
containsSocialLink: root.profileStore.containsSocialLink
|
||||
onAddLinkRequested: root.profileStore.createLink(linkText, linkUrl, linkType, linkIcon)
|
||||
containsSocialLink: d.containsSocialLink
|
||||
onAddLinkRequested: root.addSocialLink(linkUrl, linkText)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: modifySocialLinkModal
|
||||
ModifySocialLinkModal {
|
||||
containsSocialLink: root.profileStore.containsSocialLink
|
||||
onUpdateLinkRequested: root.profileStore.updateLink(uuid, linkText, linkUrl)
|
||||
onRemoveLinkRequested: root.profileStore.removeLink(uuid)
|
||||
containsSocialLink: d.containsSocialLink
|
||||
onUpdateLinkRequested: root.updateSocialLink(index, linkUrl, linkText)
|
||||
onRemoveLinkRequested: root.removeSocialLink(index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +68,7 @@ Control {
|
|||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
StatusBaseText {
|
||||
text: qsTr("%1 / %2").arg(root.profileStore.temporarySocialLinksModel.count).arg(root.showcaseLimit)
|
||||
text: qsTr("%1 / %2").arg(linksView.count).arg(root.showcaseLimit)
|
||||
color: Theme.palette.baseColor1
|
||||
font.pixelSize: Theme.tertiaryTextFontSize
|
||||
}
|
||||
|
@ -62,7 +76,7 @@ Control {
|
|||
|
||||
// empty placeholder when no links; dashed rounded rectangle
|
||||
ShapeRectangle {
|
||||
readonly property bool maxReached: root.profileStore.temporarySocialLinksModel.count === root.showcaseLimit
|
||||
readonly property bool maxReached: linksView.count === root.showcaseLimit
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: parent.width - 4 // the rectangular path is rendered outside
|
||||
|
@ -115,18 +129,16 @@ Control {
|
|||
const to = draggableDelegate.visualIndex
|
||||
if (to === from)
|
||||
return
|
||||
root.profileStore.moveLink(from, to, 1)
|
||||
root.changePosition(from, to)
|
||||
drag.accept()
|
||||
}
|
||||
|
||||
onDropped: function(drop) {
|
||||
root.profileStore.saveSocialLinks(true /*silent*/)
|
||||
}
|
||||
|
||||
StatusDraggableListItem {
|
||||
id: draggableDelegate
|
||||
|
||||
readonly property string asideText: ProfileUtils.stripSocialLinkPrefix(model.url, model.linkType)
|
||||
readonly property string asideText: ProfileUtils.stripSocialLinkPrefix(model.url, draggableDelegate.linkType)
|
||||
readonly property int linkType: ProfileUtils.linkTextToType(model.text)
|
||||
readonly property string iconName: ProfileUtils.linkTypeToIcon(draggableDelegate.linkType)
|
||||
|
||||
visible: !!asideText
|
||||
width: parent.width
|
||||
|
@ -142,11 +154,11 @@ Control {
|
|||
dragParent: linksView
|
||||
visualIndex: delegateRoot.visualIndex
|
||||
draggable: linksView.count > 1
|
||||
title: ProfileUtils.linkTypeToShortText(model.linkType) || model.text
|
||||
title: ProfileUtils.linkTypeToShortText(draggableDelegate.linkType) || model.text
|
||||
hasIcon: true
|
||||
icon.name: model.icon
|
||||
icon.color: ProfileUtils.linkTypeColor(model.linkType)
|
||||
assetBgColor: ProfileUtils.linkTypeBgColor(model.linkType)
|
||||
icon.name: draggableDelegate.iconName
|
||||
icon.color: ProfileUtils.linkTypeColor(draggableDelegate.linkType)
|
||||
assetBgColor: ProfileUtils.linkTypeBgColor(draggableDelegate.linkType)
|
||||
actions: [
|
||||
StatusLinkText {
|
||||
Layout.fillWidth: true
|
||||
|
@ -167,7 +179,7 @@ Control {
|
|||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Edit link")
|
||||
onClicked: Global.openPopup(modifySocialLinkModal,
|
||||
{linkType: model.linkType, icon: model.icon, uuid: model.uuid,
|
||||
{linkType: draggableDelegate.linkType, icon: draggableDelegate.iconName, index: delegateRoot.visualIndex,
|
||||
linkText: model.text, linkUrl: draggableDelegate.asideText})
|
||||
}
|
||||
]
|
||||
|
|
|
@ -21,12 +21,12 @@ StatusDialog {
|
|||
property int linkType: -1
|
||||
property string icon
|
||||
|
||||
property string uuid
|
||||
property int index
|
||||
property string linkText
|
||||
property string linkUrl
|
||||
|
||||
signal updateLinkRequested(string uuid, string linkText, string linkUrl)
|
||||
signal removeLinkRequested(string uuid)
|
||||
signal updateLinkRequested(string index, string linkText, string linkUrl)
|
||||
signal removeLinkRequested(string index)
|
||||
|
||||
implicitWidth: 480 // design
|
||||
|
||||
|
@ -38,7 +38,7 @@ StatusDialog {
|
|||
type: StatusButton.Danger
|
||||
text: qsTr("Delete")
|
||||
onClicked: {
|
||||
root.removeLinkRequested(root.uuid)
|
||||
root.removeLinkRequested(root.index)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ StatusDialog {
|
|||
text: qsTr("Update")
|
||||
enabled: linkTarget.valid && (!customTitle.visible || customTitle.valid)
|
||||
onClicked: {
|
||||
root.updateLinkRequested(root.uuid, customTitle.text, ProfileUtils.addSocialLinkPrefix(linkTarget.text, root.linkType))
|
||||
root.updateLinkRequested(root.index, customTitle.text, ProfileUtils.addSocialLinkPrefix(linkTarget.text, root.linkType))
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,8 @@ SettingsContentBase {
|
|||
collectiblesSourceModel: root.profileStore.collectiblesModel
|
||||
collectiblesShowcaseModel: root.profileStore.profileShowcaseCollectiblesModel
|
||||
collectiblesSearcherText: profileShowcaseCollectiblesPanel.searcherText
|
||||
|
||||
socialLinksSourceModel: root.profileStore.socialLinksModel
|
||||
}
|
||||
|
||||
function reset() {
|
||||
|
@ -290,9 +292,24 @@ SettingsContentBase {
|
|||
|
||||
// web
|
||||
ProfileSocialLinksPanel {
|
||||
profileStore: root.profileStore
|
||||
socialLinksModel: root.profileStore.temporarySocialLinksModel
|
||||
showcaseLimit: root.profileStore.getProfileShowcaseSocialLinksLimit()
|
||||
socialLinksModel: priv.showcaseModels.socialLinksVisibleModel
|
||||
|
||||
onAddSocialLink: function(url, text) {
|
||||
priv.showcaseModels.appendSocialLink({ showcaseKey: "", text: text, url: url })
|
||||
}
|
||||
|
||||
onUpdateSocialLink: function(index, url, text) {
|
||||
priv.showcaseModels.updateSocialLink(index, { text: text, url: url })
|
||||
}
|
||||
|
||||
onRemoveSocialLink: function(index) {
|
||||
priv.showcaseModels.removeSocialLink(index)
|
||||
}
|
||||
|
||||
onChangePosition: function(from, to) {
|
||||
priv.showcaseModels.changeSocialLinkPosition(from, to)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
|
|
|
@ -78,6 +78,16 @@ QtObject {
|
|||
return Constants.socialLinkType.custom
|
||||
}
|
||||
|
||||
function linkTypeToIcon(linkType) {
|
||||
if (linkType === Constants.socialLinkType.twitter) return "twitter"
|
||||
if (linkType === Constants.socialLinkType.personalSite) return "language"
|
||||
if (linkType === Constants.socialLinkType.github) return "github"
|
||||
if (linkType === Constants.socialLinkType.youtube) return "youtube"
|
||||
if (linkType === Constants.socialLinkType.discord) return "discord"
|
||||
if (linkType === Constants.socialLinkType.telegram) return "telegram"
|
||||
return "link"
|
||||
}
|
||||
|
||||
// showcase
|
||||
function visibilityIcon(showcaseVisibility) {
|
||||
switch (showcaseVisibility) {
|
||||
|
|
Loading…
Reference in New Issue