feat(SocialLinkPreview): links are too short to display even short urls
- rework the way social links are displayed/editted; we now only allow to enter a so called "handle" and then substitute that in the final URL template - move the "icon" model role to NIM backend This has several advantages: - we display only the "handle" and don't have to elide some long URL - we won't let users enter random URLs into their profile and spoof the viewing part into clicking it Additionally, make the social link "button" clickable -> navigate to the target URL, and make the tooltip behave as "usual" (on mouse hover).
This commit is contained in:
parent
0a37716c7c
commit
e13726e4af
|
@ -6,7 +6,7 @@ type
|
|||
Twitter,
|
||||
PersonalSite,
|
||||
Github,
|
||||
Youtbue,
|
||||
Youtube,
|
||||
Discord,
|
||||
Telegram
|
||||
|
||||
|
@ -15,13 +15,15 @@ type
|
|||
text*: string
|
||||
url*: string
|
||||
linkType: LinkType
|
||||
icon*: string
|
||||
|
||||
proc initSocialLinkItem*(text, url: string, linkType: LinkType): SocialLinkItem =
|
||||
proc initSocialLinkItem*(text, url: string, linkType: LinkType, icon: string = ""): SocialLinkItem =
|
||||
result = SocialLinkItem()
|
||||
result.uuid = $genUUID()
|
||||
result.text = text
|
||||
result.url = url
|
||||
result.linkType = linkType
|
||||
result.icon = icon
|
||||
|
||||
proc uuid*(self: SocialLinkItem): string {.inline.} =
|
||||
self.uuid
|
||||
|
|
|
@ -9,11 +9,11 @@ proc toSocialLinkItems*(source: SocialLinks): seq[SocialLinkItem] =
|
|||
if (text == SOCIAL_LINK_TWITTER_ID): return LinkType.Twitter
|
||||
if (text == SOCIAL_LINK_PERSONAL_SITE_ID): return LinkType.PersonalSite
|
||||
if (text == SOCIAL_LINK_GITHUB_ID): return LinkType.Github
|
||||
if (text == SOCIAL_LINK_YOUTUBE_ID): return LinkType.Youtbue
|
||||
if (text == SOCIAL_LINK_YOUTUBE_ID): return LinkType.Youtube
|
||||
if (text == SOCIAL_LINK_DISCORD_ID): return LinkType.Discord
|
||||
if (text == SOCIAL_LINK_TELEGRAM_ID): return LinkType.Telegram
|
||||
return LinkType.Custom
|
||||
result = map(source, x => initSocialLinkItem(x.text, x.url, textToType(x.text)))
|
||||
result = map(source, x => initSocialLinkItem(x.text, x.url, textToType(x.text), x.icon))
|
||||
|
||||
type
|
||||
ModelRole {.pure.} = enum
|
||||
|
@ -21,6 +21,7 @@ type
|
|||
Text
|
||||
Url
|
||||
LinkType
|
||||
Icon
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -91,6 +92,7 @@ QtObject:
|
|||
ModelRole.Text.int: "text",
|
||||
ModelRole.Url.int: "url",
|
||||
ModelRole.LinkType.int: "linkType",
|
||||
ModelRole.Icon.int: "icon"
|
||||
}.toTable
|
||||
|
||||
method data(self: SocialLinksModel, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -112,3 +114,5 @@ QtObject:
|
|||
result = newQVariant(item.url)
|
||||
of ModelRole.LinkType:
|
||||
result = newQVariant(item.linkType.int)
|
||||
of ModelRole.Icon:
|
||||
result = newQVariant(item.icon)
|
||||
|
|
|
@ -11,13 +11,24 @@ type
|
|||
SocialLink* = object
|
||||
text*: string
|
||||
url*: string
|
||||
icon*: string
|
||||
|
||||
SocialLinks* = seq[SocialLink]
|
||||
|
||||
proc socialLinkTextToIcon(text: string): string =
|
||||
if (text == SOCIAL_LINK_TWITTER_ID): return "twitter"
|
||||
if (text == SOCIAL_LINK_PERSONAL_SITE_ID): return "language"
|
||||
if (text == SOCIAL_LINK_GITHUB_ID): return "github"
|
||||
if (text == SOCIAL_LINK_YOUTUBE_ID): return "youtube"
|
||||
if (text == SOCIAL_LINK_DISCORD_ID): return "discord"
|
||||
if (text == SOCIAL_LINK_TELEGRAM_ID): return "telegram"
|
||||
return ""
|
||||
|
||||
proc toSocialLinks*(jsonObj: JsonNode): SocialLinks =
|
||||
result = map(jsonObj.getElems(),
|
||||
node => SocialLink(text: node["text"].getStr(),
|
||||
url: node["url"].getStr())
|
||||
url: node["url"].getStr(),
|
||||
icon: socialLinkTextToIcon(node["text"].getStr()))
|
||||
)
|
||||
return
|
||||
|
||||
|
|
|
@ -66,7 +66,17 @@ SplitView {
|
|||
verificationStatus: Constants.verificationStatus.unverified,
|
||||
incomingVerificationStatus: Constants.verificationStatus.unverified,
|
||||
bio: bio.text,
|
||||
socialLinks: "" // TODO
|
||||
socialLinks: JSON.stringify
|
||||
([{
|
||||
text: "__twitter",
|
||||
url: "https://twitter.com/ethstatus",
|
||||
icon: "twitter"
|
||||
},
|
||||
{
|
||||
text: "__github",
|
||||
url: "https://github.com/status-im",
|
||||
icon: "github"
|
||||
}])
|
||||
})
|
||||
}
|
||||
Component.onCompleted: {
|
||||
|
|
|
@ -61,9 +61,6 @@ MenuItem {
|
|||
readonly property bool isImage: d.originalAssetSettings.isImage
|
||||
readonly property int imgStatus: d.originalAssetSettings.imgStatus
|
||||
readonly property bool imgIsIdenticon: d.originalAssetSettings.imgIsIdenticon
|
||||
|
||||
// crop
|
||||
readonly property rect cropRect: d.originalAssetSettings.cropRect
|
||||
}
|
||||
|
||||
readonly property StatusFontSettings fontSettings: d.isStatusSubMenu
|
||||
|
|
|
@ -9,6 +9,7 @@ StatusInput {
|
|||
id: root
|
||||
|
||||
property int linkType
|
||||
property string icon
|
||||
|
||||
leftPadding: 18 // by design
|
||||
|
||||
|
@ -22,15 +23,7 @@ StatusInput {
|
|||
return ""
|
||||
}
|
||||
input.asset {
|
||||
name: {
|
||||
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 ""
|
||||
}
|
||||
name: root.icon
|
||||
width: 20
|
||||
height: 20
|
||||
}
|
||||
|
|
|
@ -71,9 +71,10 @@ Item {
|
|||
|
||||
Layout.fillWidth: true
|
||||
linkType: model.linkType
|
||||
text: model.url
|
||||
text: Utils.stripSocialLinkPrefix(model.url, model.linkType)
|
||||
icon: model.icon
|
||||
|
||||
onTextChanged: root.socialLinkChanged(model.uuid, model.text, text)
|
||||
onTextChanged: root.socialLinkChanged(model.uuid, model.text, Utils.addSocialLinkPrefix(text, model.linkType))
|
||||
|
||||
input.tabNavItem: {
|
||||
if (index < socialLinksRepeater.count - 1) {
|
||||
|
|
|
@ -81,7 +81,8 @@ StatusDialog {
|
|||
|
||||
Layout.fillWidth: true
|
||||
linkType: model.linkType
|
||||
text: model.url
|
||||
text: Utils.stripSocialLinkPrefix(model.url, model.linkType)
|
||||
icon: model.icon
|
||||
|
||||
input.tabNavItem: {
|
||||
if (index < staticLinksRepeater.count - 1) {
|
||||
|
@ -90,7 +91,7 @@ StatusDialog {
|
|||
return customLinksRepeater.count ? customLinksRepeater.itemAt(0).focusItem : null
|
||||
}
|
||||
|
||||
onTextChanged: root.profileStore.updateLink(model.uuid, model.text, text)
|
||||
onTextChanged: root.profileStore.updateLink(model.uuid, model.text, Utils.addSocialLinkPrefix(text, linkType))
|
||||
|
||||
Component.onCompleted: if (index === 0) {
|
||||
input.edit.forceActiveFocus()
|
||||
|
@ -155,5 +156,4 @@ StatusDialog {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ Rectangle {
|
|||
property string text
|
||||
property string url
|
||||
property int linkType: 1
|
||||
property string icon
|
||||
|
||||
implicitWidth: layout.implicitWidth + 16
|
||||
implicitHeight: layout.implicitHeight + 10
|
||||
|
@ -32,22 +33,14 @@ Rectangle {
|
|||
StatusIcon {
|
||||
Layout.preferredWidth: 20
|
||||
Layout.preferredHeight: 20
|
||||
icon: {
|
||||
if (root.linkType === Constants.socialLinkType.twitter) return "twitter"
|
||||
if (root.linkType === Constants.socialLinkType.personalSite) return "language"
|
||||
if (root.linkType === Constants.socialLinkType.github) return "github"
|
||||
if (root.linkType === Constants.socialLinkType.youtube) return "youtube"
|
||||
if (root.linkType === Constants.socialLinkType.discord) return "discord"
|
||||
if (root.linkType === Constants.socialLinkType.telegram) return "telegram"
|
||||
return ""
|
||||
}
|
||||
icon: root.icon
|
||||
visible: icon !== ""
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.maximumWidth: 150
|
||||
text: root.linkType === Constants.socialLinkType.custom ? root.text : root.url
|
||||
text: root.text
|
||||
color: Theme.palette.directColor4
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideMiddle
|
||||
|
@ -56,43 +49,15 @@ Rectangle {
|
|||
|
||||
StatusToolTip {
|
||||
id: toolTip
|
||||
|
||||
contentItem: RowLayout {
|
||||
StatusBaseText {
|
||||
Layout.fillHeight: true
|
||||
Layout.maximumWidth: 300
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
text: toolTip.text
|
||||
color: Theme.palette.white
|
||||
elide: Text.ElideMiddle
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.Medium
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
StatusFlatRoundButton {
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 24
|
||||
Layout.bottomMargin: 8
|
||||
icon.name: "copy"
|
||||
icon.width: 18
|
||||
icon.height: 18
|
||||
type: StatusFlatRoundButton.Tertiary
|
||||
|
||||
onClicked: {
|
||||
globalUtils.copyToClipboard(toolTip.text)
|
||||
toolTip.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
text: root.url
|
||||
visible: mouseArea.containsMouse
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: toolTip.show(root.url, -1)
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: Global.openLink(root.url)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,10 +45,13 @@ Control {
|
|||
let links = JSON.parse(root.userSocialLinksJson)
|
||||
for (let i=0; i<links.length; i++) {
|
||||
let obj = links[i]
|
||||
const url = obj.url
|
||||
const type = textToType(obj.text)
|
||||
socialLinksModel.append({
|
||||
"text": obj.text,
|
||||
"url": obj.url,
|
||||
"linkType": textToType(obj.text)
|
||||
"text": Utils.stripSocialLinkPrefix(url, type),
|
||||
"url": url,
|
||||
"linkType": type,
|
||||
"icon": obj.icon
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +134,7 @@ Control {
|
|||
text: model.text
|
||||
url: model.url
|
||||
linkType: model.linkType
|
||||
icon: model.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -311,9 +311,7 @@ Pane {
|
|||
else
|
||||
result = d.contactDetails.optionalName // original display name
|
||||
}
|
||||
if (result)
|
||||
return "(%1)".arg(result)
|
||||
return ""
|
||||
return result ? "(%1)".arg(result) : ""
|
||||
}
|
||||
visible: text
|
||||
}
|
||||
|
|
|
@ -543,6 +543,16 @@ QtObject {
|
|||
readonly property int telegram: 6
|
||||
}
|
||||
|
||||
readonly property var socialLinkPrefixesByType: [ // NB order must match the "socialLinkType" enum above
|
||||
"",
|
||||
"https://twitter.com/",
|
||||
"",
|
||||
"https://github.com/",
|
||||
"https://www.youtube.com/",
|
||||
"https://discordapp.com/users/",
|
||||
"https://t.me/"
|
||||
]
|
||||
|
||||
enum DiscordImportErrorCode {
|
||||
Unknown = 1,
|
||||
Warning = 2,
|
||||
|
|
|
@ -756,6 +756,17 @@ QtObject {
|
|||
return reg.test(msg)
|
||||
}
|
||||
|
||||
function addSocialLinkPrefix(link, type) {
|
||||
const prefix = Constants.socialLinkPrefixesByType[type]
|
||||
if (link.startsWith(prefix))
|
||||
return link
|
||||
return prefix + link
|
||||
}
|
||||
|
||||
function stripSocialLinkPrefix(link, type) {
|
||||
return link.replace(Constants.socialLinkPrefixesByType[type], "")
|
||||
}
|
||||
|
||||
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
||||
function isPunct(c) {
|
||||
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
||||
|
|
Loading…
Reference in New Issue