fix(@desktop/profile): profile social links
- added to sync mechanism - added to backup mechanism - UI updated according to the newest changes Closes: #10390
This commit is contained in:
parent
274fc98839
commit
ae492fe631
|
@ -2,6 +2,7 @@ import json, chronicles
|
||||||
|
|
||||||
import base
|
import base
|
||||||
|
|
||||||
|
import ../../../../app_service/common/social_links
|
||||||
import ../../../../app_service/service/message/dto/[message, pinned_message_update, reaction, removed_message]
|
import ../../../../app_service/service/message/dto/[message, pinned_message_update, reaction, removed_message]
|
||||||
import ../../../../app_service/service/chat/dto/[chat]
|
import ../../../../app_service/service/chat/dto/[chat]
|
||||||
import ../../../../app_service/service/bookmarks/dto/[bookmark]
|
import ../../../../app_service/service/bookmarks/dto/[bookmark]
|
||||||
|
@ -30,6 +31,7 @@ type MessageSignal* = ref object of Signal
|
||||||
removedChats*: seq[string]
|
removedChats*: seq[string]
|
||||||
currentStatus*: seq[StatusUpdateDto]
|
currentStatus*: seq[StatusUpdateDto]
|
||||||
settings*: seq[SettingsFieldDto]
|
settings*: seq[SettingsFieldDto]
|
||||||
|
socialLinksInfo*: SocialLinksInfo
|
||||||
clearedHistories*: seq[ClearedHistoryDto]
|
clearedHistories*: seq[ClearedHistoryDto]
|
||||||
verificationRequests*: seq[VerificationRequest]
|
verificationRequests*: seq[VerificationRequest]
|
||||||
savedAddresses*: seq[SavedAddressDto]
|
savedAddresses*: seq[SavedAddressDto]
|
||||||
|
@ -53,102 +55,110 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
||||||
signal.messages = @[]
|
signal.messages = @[]
|
||||||
signal.contacts = @[]
|
signal.contacts = @[]
|
||||||
|
|
||||||
if event["event"]{"contacts"} != nil:
|
if not event.contains("event"):
|
||||||
for jsonContact in event["event"]["contacts"]:
|
return signal
|
||||||
|
|
||||||
|
let e = event["event"]
|
||||||
|
|
||||||
|
if e.contains("contacts"):
|
||||||
|
for jsonContact in e["contacts"]:
|
||||||
signal.contacts.add(jsonContact.toContactsDto())
|
signal.contacts.add(jsonContact.toContactsDto())
|
||||||
|
|
||||||
if event["event"]{"messages"} != nil:
|
if e.contains("messages"):
|
||||||
for jsonMsg in event["event"]["messages"]:
|
for jsonMsg in e["messages"]:
|
||||||
var message = jsonMsg.toMessageDto()
|
var message = jsonMsg.toMessageDto()
|
||||||
signal.messages.add(message)
|
signal.messages.add(message)
|
||||||
info "received", signal="messages.new", messageID=message.id
|
info "received", signal="messages.new", messageID=message.id
|
||||||
|
|
||||||
if event["event"]{"chats"} != nil:
|
if e.contains("chats"):
|
||||||
for jsonChat in event["event"]["chats"]:
|
for jsonChat in e["chats"]:
|
||||||
var chat = jsonChat.toChatDto()
|
var chat = jsonChat.toChatDto()
|
||||||
signal.chats.add(chat)
|
signal.chats.add(chat)
|
||||||
|
|
||||||
if event["event"]{"clearedHistories"} != nil:
|
if e.contains("clearedHistories"):
|
||||||
for jsonClearedHistory in event["event"]{"clearedHistories"}:
|
for jsonClearedHistory in e["clearedHistories"]:
|
||||||
var clearedHistoryDto = jsonClearedHistory.toClearedHistoryDto()
|
var clearedHistoryDto = jsonClearedHistory.toClearedHistoryDto()
|
||||||
signal.clearedHistories.add(clearedHistoryDto)
|
signal.clearedHistories.add(clearedHistoryDto)
|
||||||
|
|
||||||
if event["event"]{"statusUpdates"} != nil:
|
if e.contains("statusUpdates"):
|
||||||
for jsonStatusUpdate in event["event"]["statusUpdates"]:
|
for jsonStatusUpdate in e["statusUpdates"]:
|
||||||
var statusUpdate = jsonStatusUpdate.toStatusUpdateDto()
|
var statusUpdate = jsonStatusUpdate.toStatusUpdateDto()
|
||||||
signal.statusUpdates.add(statusUpdate)
|
signal.statusUpdates.add(statusUpdate)
|
||||||
|
|
||||||
if event["event"]{"currentStatus"} != nil:
|
if e.contains("currentStatus"):
|
||||||
var currentStatus = event["event"]["currentStatus"].toStatusUpdateDto()
|
var currentStatus = e["currentStatus"].toStatusUpdateDto()
|
||||||
signal.currentStatus.add(currentStatus)
|
signal.currentStatus.add(currentStatus)
|
||||||
|
|
||||||
if event["event"]{"bookmarks"} != nil:
|
if e.contains("bookmarks"):
|
||||||
for jsonBookmark in event["event"]["bookmarks"]:
|
for jsonBookmark in e["bookmarks"]:
|
||||||
var bookmark = jsonBookmark.toBookmarkDto()
|
var bookmark = jsonBookmark.toBookmarkDto()
|
||||||
signal.bookmarks.add(bookmark)
|
signal.bookmarks.add(bookmark)
|
||||||
|
|
||||||
if event["event"]{"installations"} != nil:
|
if e.contains("installations"):
|
||||||
for jsonDevice in event["event"]["installations"]:
|
for jsonDevice in e["installations"]:
|
||||||
signal.installations.add(jsonDevice.toInstallationDto())
|
signal.installations.add(jsonDevice.toInstallationDto())
|
||||||
|
|
||||||
if event["event"]{"emojiReactions"} != nil:
|
if e.contains("emojiReactions"):
|
||||||
for jsonReaction in event["event"]["emojiReactions"]:
|
for jsonReaction in e["emojiReactions"]:
|
||||||
signal.emojiReactions.add(jsonReaction.toReactionDto())
|
signal.emojiReactions.add(jsonReaction.toReactionDto())
|
||||||
|
|
||||||
if event["event"]{"communities"} != nil:
|
if e.contains("communities"):
|
||||||
for jsonCommunity in event["event"]["communities"]:
|
for jsonCommunity in e["communities"]:
|
||||||
signal.communities.add(jsonCommunity.toCommunityDto())
|
signal.communities.add(jsonCommunity.toCommunityDto())
|
||||||
|
|
||||||
if event["event"]{"communitiesSettings"} != nil:
|
if e.contains("communitiesSettings"):
|
||||||
for jsonCommunitySettings in event["event"]["communitiesSettings"]:
|
for jsonCommunitySettings in e["communitiesSettings"]:
|
||||||
signal.communitiesSettings.add(jsonCommunitySettings.toCommunitySettingsDto())
|
signal.communitiesSettings.add(jsonCommunitySettings.toCommunitySettingsDto())
|
||||||
|
|
||||||
if event["event"]{"requestsToJoinCommunity"} != nil:
|
if e.contains("requestsToJoinCommunity"):
|
||||||
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
for jsonCommunity in e["requestsToJoinCommunity"]:
|
||||||
signal.membershipRequests.add(jsonCommunity.toCommunityMembershipRequestDto())
|
signal.membershipRequests.add(jsonCommunity.toCommunityMembershipRequestDto())
|
||||||
|
|
||||||
if event["event"]{"removedMessages"} != nil:
|
if e.contains("removedMessages"):
|
||||||
for jsonRemovedMessage in event["event"]["removedMessages"]:
|
for jsonRemovedMessage in e["removedMessages"]:
|
||||||
signal.deletedMessages.add(jsonRemovedMessage.toRemovedMessageDto())
|
signal.deletedMessages.add(jsonRemovedMessage.toRemovedMessageDto())
|
||||||
|
|
||||||
if event["event"]{"removedChats"} != nil:
|
if e.contains("removedChats"):
|
||||||
for removedChatID in event["event"]["removedChats"]:
|
for removedChatID in e["removedChats"]:
|
||||||
signal.removedChats.add(removedChatID.getStr())
|
signal.removedChats.add(removedChatID.getStr())
|
||||||
|
|
||||||
if event["event"]{"activityCenterNotifications"} != nil:
|
if e.contains("activityCenterNotifications"):
|
||||||
for jsonNotification in event["event"]["activityCenterNotifications"]:
|
for jsonNotification in e["activityCenterNotifications"]:
|
||||||
signal.activityCenterNotifications.add(jsonNotification.toActivityCenterNotificationDto())
|
signal.activityCenterNotifications.add(jsonNotification.toActivityCenterNotificationDto())
|
||||||
|
|
||||||
if event["event"]{"pinMessages"} != nil:
|
if e.contains("pinMessages"):
|
||||||
for jsonPinnedMessage in event["event"]["pinMessages"]:
|
for jsonPinnedMessage in e["pinMessages"]:
|
||||||
signal.pinnedMessages.add(jsonPinnedMessage.toPinnedMessageUpdateDto())
|
signal.pinnedMessages.add(jsonPinnedMessage.toPinnedMessageUpdateDto())
|
||||||
|
|
||||||
if event["event"]{"settings"} != nil:
|
if e.contains("settings"):
|
||||||
for jsonSettingsField in event["event"]["settings"]:
|
for jsonSettingsField in e["settings"]:
|
||||||
signal.settings.add(jsonSettingsField.toSettingsFieldDto())
|
signal.settings.add(jsonSettingsField.toSettingsFieldDto())
|
||||||
|
|
||||||
if event["event"]{"verificationRequests"} != nil:
|
if e.contains("socialLinksInfo"):
|
||||||
for jsonVerificationRequest in event["event"]["verificationRequests"]:
|
signal.socialLinksInfo = toSocialLinksInfo(e["socialLinksInfo"])
|
||||||
|
|
||||||
|
if e.contains("verificationRequests"):
|
||||||
|
for jsonVerificationRequest in e["verificationRequests"]:
|
||||||
signal.verificationRequests.add(jsonVerificationRequest.toVerificationRequest())
|
signal.verificationRequests.add(jsonVerificationRequest.toVerificationRequest())
|
||||||
|
|
||||||
if event["event"]{"savedAddresses"} != nil:
|
if e.contains("savedAddresses"):
|
||||||
for jsonSavedAddress in event["event"]["savedAddresses"]:
|
for jsonSavedAddress in e["savedAddresses"]:
|
||||||
signal.savedAddresses.add(jsonSavedAddress.toSavedAddressDto())
|
signal.savedAddresses.add(jsonSavedAddress.toSavedAddressDto())
|
||||||
|
|
||||||
if event["event"]{"keypairs"} != nil:
|
if e.contains("keypairs"):
|
||||||
for jsonKc in event["event"]["keypairs"]:
|
for jsonKc in e["keypairs"]:
|
||||||
signal.keypairs.add(jsonKc.toKeypairDto())
|
signal.keypairs.add(jsonKc.toKeypairDto())
|
||||||
|
|
||||||
if event["event"]{"keycards"} != nil:
|
if e.contains("keycards"):
|
||||||
for jsonKc in event["event"]["keycards"]:
|
for jsonKc in e["keycards"]:
|
||||||
signal.keycards.add(jsonKc.toKeycardDto())
|
signal.keycards.add(jsonKc.toKeycardDto())
|
||||||
|
|
||||||
if event["event"]{"keycardActions"} != nil:
|
if e.contains("keycardActions"):
|
||||||
for jsonKc in event["event"]["keycardActions"]:
|
for jsonKc in e["keycardActions"]:
|
||||||
signal.keycardActions.add(jsonKc.toKeycardActionDto())
|
signal.keycardActions.add(jsonKc.toKeycardActionDto())
|
||||||
|
|
||||||
if event["event"]{"accounts"} != nil:
|
if e.contains("accounts"):
|
||||||
for jsonAcc in event["event"]["accounts"]:
|
for jsonAcc in e["accounts"]:
|
||||||
signal.walletAccounts.add(jsonAcc.toWalletAccountDto())
|
signal.walletAccounts.add(jsonAcc.toWalletAccountDto())
|
||||||
|
|
||||||
result = signal
|
result = signal
|
||||||
|
|
|
@ -24,10 +24,16 @@ proc delete*(self: Controller) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc init*(self: Controller) =
|
proc init*(self: Controller) =
|
||||||
|
self.settingsService.fetchAndStoreSocialLinks()
|
||||||
|
|
||||||
self.events.on(SIGNAL_BIO_UPDATED) do(e: Args):
|
self.events.on(SIGNAL_BIO_UPDATED) do(e: Args):
|
||||||
let args = SettingsTextValueArgs(e)
|
let args = SettingsTextValueArgs(e)
|
||||||
self.delegate.onBioChanged(args.value)
|
self.delegate.onBioChanged(args.value)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_SOCIAL_LINKS_UPDATED) do(e: Args):
|
||||||
|
let args = SocialLinksArgs(e)
|
||||||
|
self.delegate.onSocialLinksUpdated(args.socialLinks, args.error)
|
||||||
|
|
||||||
proc storeIdentityImage*(self: Controller, address: string, image: string, aX: int, aY: int, bX: int, bY: int) =
|
proc storeIdentityImage*(self: Controller, address: string, image: string, aX: int, aY: int, bX: int, bY: int) =
|
||||||
discard self.profileService.storeIdentityImage(address, image, aX, aY, bX, bY)
|
discard self.profileService.storeIdentityImage(address, image, aX, aY, bX, bY)
|
||||||
|
|
||||||
|
@ -38,9 +44,9 @@ proc setDisplayName*(self: Controller, displayName: string) =
|
||||||
self.profileService.setDisplayName(displayName)
|
self.profileService.setDisplayName(displayName)
|
||||||
|
|
||||||
proc getSocialLinks*(self: Controller): SocialLinks =
|
proc getSocialLinks*(self: Controller): SocialLinks =
|
||||||
self.settingsService.getSocialLinks()
|
return self.settingsService.getSocialLinks()
|
||||||
|
|
||||||
proc setSocialLinks*(self: Controller, links: SocialLinks): bool =
|
proc setSocialLinks*(self: Controller, links: SocialLinks) =
|
||||||
self.settingsService.setSocialLinks(links)
|
self.settingsService.setSocialLinks(links)
|
||||||
|
|
||||||
proc getBio*(self: Controller): string =
|
proc getBio*(self: Controller): string =
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
|
import ../../../../../app_service/common/social_links
|
||||||
|
|
||||||
type
|
type
|
||||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||||
|
@ -34,7 +35,10 @@ method onBioChanged*(self: AccessInterface, bio: string) {.base.} =
|
||||||
method setDisplayName*(self: AccessInterface, displayName: string) {.base.} =
|
method setDisplayName*(self: AccessInterface, displayName: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method saveSocialLinks*(self: AccessInterface): bool {.base.} =
|
method saveSocialLinks*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onSocialLinksUpdated*(self: AccessInterface, socialLinks: SocialLinks, error: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
# View Delegate Interface
|
# View Delegate Interface
|
||||||
|
|
|
@ -49,12 +49,12 @@ method isLoaded*(self: Module): bool =
|
||||||
method getModuleAsVariant*(self: Module): QVariant =
|
method getModuleAsVariant*(self: Module): QVariant =
|
||||||
return self.viewVariant
|
return self.viewVariant
|
||||||
|
|
||||||
|
proc updateSocialLinks(self: Module, socialLinks: SocialLinks) =
|
||||||
|
var socialLinkItems = toSocialLinkItems(socialLinks)
|
||||||
|
self.view.socialLinksSaved(socialLinkItems)
|
||||||
|
|
||||||
method viewDidLoad*(self: Module) =
|
method viewDidLoad*(self: Module) =
|
||||||
var socialLinkItems = toSocialLinkItems(self.controller.getSocialLinks())
|
self.updateSocialLinks(self.controller.getSocialLinks())
|
||||||
|
|
||||||
self.view.socialLinksModel().setItems(socialLinkItems)
|
|
||||||
self.view.temporarySocialLinksModel().setItems(socialLinkItems)
|
|
||||||
|
|
||||||
self.moduleLoaded = true
|
self.moduleLoaded = true
|
||||||
self.delegate.profileModuleDidLoad()
|
self.delegate.profileModuleDidLoad()
|
||||||
|
|
||||||
|
@ -79,6 +79,12 @@ method setBio(self: Module, bio: string) =
|
||||||
method onBioChanged*(self: Module, bio: string) =
|
method onBioChanged*(self: Module, bio: string) =
|
||||||
self.view.emitBioChangedSignal()
|
self.view.emitBioChangedSignal()
|
||||||
|
|
||||||
method saveSocialLinks*(self: Module): bool =
|
method saveSocialLinks*(self: Module) =
|
||||||
let socialLinks = map(self.view.temporarySocialLinksModel.items(), x => SocialLink(text: x.text, url: x.url))
|
let socialLinks = map(self.view.temporarySocialLinksModel.items(), x => SocialLink(text: x.text, url: x.url, icon: x.icon))
|
||||||
return self.controller.setSocialLinks(socialLinks)
|
self.controller.setSocialLinks(socialLinks)
|
||||||
|
|
||||||
|
method onSocialLinksUpdated*(self: Module, socialLinks: SocialLinks, error: string) =
|
||||||
|
if error.len > 0:
|
||||||
|
# maybe we want in future popup or somehow display an error to a user
|
||||||
|
return
|
||||||
|
self.updateSocialLinks(socialLinks)
|
|
@ -90,6 +90,9 @@ QtObject:
|
||||||
read = areSocialLinksDirty
|
read = areSocialLinksDirty
|
||||||
notify = socialLinksDirtyChanged
|
notify = socialLinksDirtyChanged
|
||||||
|
|
||||||
|
proc containsSocialLink*(self: View, text: string, url: string): bool {.slot.} =
|
||||||
|
return self.temporarySocialLinksModel.containsSocialLink(text, url)
|
||||||
|
|
||||||
proc createLink(self: View, text: string, url: string, linkType: int, icon: string) {.slot.} =
|
proc createLink(self: View, text: string, url: string, linkType: int, icon: string) {.slot.} =
|
||||||
self.temporarySocialLinksModel.appendItem(initSocialLinkItem(text, url, (LinkType)linkType, icon))
|
self.temporarySocialLinksModel.appendItem(initSocialLinkItem(text, url, (LinkType)linkType, icon))
|
||||||
self.temporarySocialLinksJsonChanged()
|
self.temporarySocialLinksJsonChanged()
|
||||||
|
@ -114,12 +117,14 @@ QtObject:
|
||||||
self.socialLinksDirtyChanged()
|
self.socialLinksDirtyChanged()
|
||||||
self.temporarySocialLinksJsonChanged()
|
self.temporarySocialLinksJsonChanged()
|
||||||
|
|
||||||
proc saveSocialLinks(self: View, silent: bool = false): bool {.slot.} =
|
proc socialLinksSaved*(self: View, items: seq[SocialLinkItem]) =
|
||||||
result = self.delegate.saveSocialLinks()
|
self.socialLinksModel.setItems(items)
|
||||||
if (result):
|
self.temporarySocialLinksModel.setItems(items)
|
||||||
self.socialLinksModel.setItems(self.temporarySocialLinksModel.items)
|
|
||||||
self.socialLinksJsonChanged()
|
self.socialLinksJsonChanged()
|
||||||
self.temporarySocialLinksJsonChanged()
|
self.temporarySocialLinksJsonChanged()
|
||||||
|
|
||||||
|
proc saveSocialLinks(self: View, silent: bool = false) {.slot.} =
|
||||||
|
self.delegate.saveSocialLinks()
|
||||||
if not silent:
|
if not silent:
|
||||||
self.socialLinksDirtyChanged()
|
self.socialLinksDirtyChanged()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, tables, sequtils, sugar
|
import NimQml, tables, strutils, sequtils, sugar
|
||||||
|
|
||||||
import ../../../app_service/common/social_links
|
import ../../../app_service/common/social_links
|
||||||
|
|
||||||
|
@ -39,10 +39,21 @@ QtObject:
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result.setup
|
result.setup
|
||||||
|
|
||||||
|
proc countChanged(self: SocialLinksModel) {.signal.}
|
||||||
|
proc getCount(self: SocialLinksModel): int {.slot.} =
|
||||||
|
self.items.len
|
||||||
|
QtProperty[int] count:
|
||||||
|
read = getCount
|
||||||
|
notify = countChanged
|
||||||
|
|
||||||
|
proc containsSocialLink*(self: SocialLinksModel, text, url: string): bool =
|
||||||
|
return self.items.any(item => cmpIgnoreCase(item.text, text) == 0 and cmpIgnoreCase(item.url, url) == 0)
|
||||||
|
|
||||||
proc setItems*(self: SocialLinksModel, items: seq[SocialLinkItem]) =
|
proc setItems*(self: SocialLinksModel, items: seq[SocialLinkItem]) =
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
self.items = items
|
self.items = items
|
||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
proc appendItem*(self: SocialLinksModel, item: SocialLinkItem) =
|
proc appendItem*(self: SocialLinksModel, item: SocialLinkItem) =
|
||||||
let parentModelIndex = newQModelIndex()
|
let parentModelIndex = newQModelIndex()
|
||||||
|
@ -50,6 +61,7 @@ QtObject:
|
||||||
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
|
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
|
||||||
self.items.add(item)
|
self.items.add(item)
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
proc removeItem*(self: SocialLinksModel, uuid: string): bool =
|
proc removeItem*(self: SocialLinksModel, uuid: string): bool =
|
||||||
for i in 0 ..< self.items.len:
|
for i in 0 ..< self.items.len:
|
||||||
|
@ -59,6 +71,7 @@ QtObject:
|
||||||
self.beginRemoveRows(parentModelIndex, i, i)
|
self.beginRemoveRows(parentModelIndex, i, i)
|
||||||
self.items.delete(i)
|
self.items.delete(i)
|
||||||
self.endRemoveRows()
|
self.endRemoveRows()
|
||||||
|
self.countChanged()
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
|
@ -336,7 +336,6 @@ method checkFetchingStatusAndProceedWithAppLoading*[T](self: Module[T]) =
|
||||||
|
|
||||||
method onFetchingFromWakuMessageReceived*[T](self: Module[T], backedUpMsgClock: uint64, section: string,
|
method onFetchingFromWakuMessageReceived*[T](self: Module[T], backedUpMsgClock: uint64, section: string,
|
||||||
totalMessages: int, receivedMessageAtPosition: int) =
|
totalMessages: int, receivedMessageAtPosition: int) =
|
||||||
echo "onFetchingFromWakuMessageReceived: ", backedUpMsgClock, " section: ", section, " tm: ", totalMessages, " recAtPos: ", receivedMessageAtPosition
|
|
||||||
self.view.fetchingDataModel().checkLastKnownClockAndReinitModel(backedUpMsgClock, listOfEntitiesWeExpectToBeSynced)
|
self.view.fetchingDataModel().checkLastKnownClockAndReinitModel(backedUpMsgClock, listOfEntitiesWeExpectToBeSynced)
|
||||||
if self.view.fetchingDataModel().allMessagesLoaded():
|
if self.view.fetchingDataModel().allMessagesLoaded():
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import json, sequtils, sugar
|
import json, sequtils, sugar
|
||||||
|
|
||||||
|
include json_utils
|
||||||
|
|
||||||
const SOCIAL_LINK_TWITTER_ID* = "__twitter"
|
const SOCIAL_LINK_TWITTER_ID* = "__twitter"
|
||||||
const SOCIAL_LINK_PERSONAL_SITE_ID* = "__personal_site"
|
const SOCIAL_LINK_PERSONAL_SITE_ID* = "__personal_site"
|
||||||
const SOCIAL_LINK_GITHUB_ID* = "__github"
|
const SOCIAL_LINK_GITHUB_ID* = "__github"
|
||||||
|
@ -15,6 +17,10 @@ type
|
||||||
|
|
||||||
SocialLinks* = seq[SocialLink]
|
SocialLinks* = seq[SocialLink]
|
||||||
|
|
||||||
|
SocialLinksInfo* = object
|
||||||
|
links*: seq[SocialLink]
|
||||||
|
removed*: bool
|
||||||
|
|
||||||
proc socialLinkTextToIcon(text: string): string =
|
proc socialLinkTextToIcon(text: string): string =
|
||||||
if (text == SOCIAL_LINK_TWITTER_ID): return "twitter"
|
if (text == SOCIAL_LINK_TWITTER_ID): return "twitter"
|
||||||
if (text == SOCIAL_LINK_PERSONAL_SITE_ID): return "language"
|
if (text == SOCIAL_LINK_PERSONAL_SITE_ID): return "language"
|
||||||
|
@ -30,7 +36,12 @@ proc toSocialLinks*(jsonObj: JsonNode): SocialLinks =
|
||||||
url: node["url"].getStr(),
|
url: node["url"].getStr(),
|
||||||
icon: socialLinkTextToIcon(node["text"].getStr()))
|
icon: socialLinkTextToIcon(node["text"].getStr()))
|
||||||
)
|
)
|
||||||
return
|
|
||||||
|
proc toSocialLinksInfo*(jsonObj: JsonNode): SocialLinksInfo =
|
||||||
|
discard jsonObj.getProp("removed", result.removed)
|
||||||
|
var linksObj: JsonNode
|
||||||
|
if jsonObj.getProp("links", linksObj):
|
||||||
|
result.links = toSocialLinks(linksObj)
|
||||||
|
|
||||||
proc toJsonNode*(links: SocialLinks): JsonNode =
|
proc toJsonNode*(links: SocialLinks): JsonNode =
|
||||||
%*links
|
%*links
|
||||||
|
|
|
@ -4,6 +4,7 @@ import json
|
||||||
import ../../visual_identity/dto
|
import ../../visual_identity/dto
|
||||||
|
|
||||||
include ../../../common/json_utils
|
include ../../../common/json_utils
|
||||||
|
import ../../../common/social_links
|
||||||
|
|
||||||
type
|
type
|
||||||
Image* = object
|
Image* = object
|
||||||
|
@ -28,6 +29,7 @@ type AccountDto* = object
|
||||||
type WakuBackedUpProfileDto* = object
|
type WakuBackedUpProfileDto* = object
|
||||||
displayName*: string
|
displayName*: string
|
||||||
images*: seq[Image]
|
images*: seq[Image]
|
||||||
|
socialLinks*: SocialLinks
|
||||||
|
|
||||||
proc isValid*(self: AccountDto): bool =
|
proc isValid*(self: AccountDto): bool =
|
||||||
result = self.name.len > 0 and self.keyUid.len > 0
|
result = self.name.len > 0 and self.keyUid.len > 0
|
||||||
|
@ -69,7 +71,10 @@ proc toWakuBackedUpProfileDto*(jsonObj: JsonNode): WakuBackedUpProfileDto =
|
||||||
result = WakuBackedUpProfileDto()
|
result = WakuBackedUpProfileDto()
|
||||||
discard jsonObj.getProp("displayName", result.displayName)
|
discard jsonObj.getProp("displayName", result.displayName)
|
||||||
|
|
||||||
var imagesObj: JsonNode
|
var obj: JsonNode
|
||||||
if(jsonObj.getProp("images", imagesObj) and imagesObj.kind == JArray):
|
if(jsonObj.getProp("images", obj) and obj.kind == JArray):
|
||||||
for imgObj in imagesObj:
|
for imgObj in obj:
|
||||||
result.images.add(toImage(imgObj))
|
result.images.add(toImage(imgObj))
|
||||||
|
|
||||||
|
if(jsonObj.getProp("socialLinks", obj) and obj.kind == JArray):
|
||||||
|
result.socialLinks = toSocialLinks(obj)
|
|
@ -25,6 +25,7 @@ const SIGNAL_CURRENCY_UPDATED* = "currencyUpdated"
|
||||||
const SIGNAL_DISPLAY_NAME_UPDATED* = "displayNameUpdated"
|
const SIGNAL_DISPLAY_NAME_UPDATED* = "displayNameUpdated"
|
||||||
const SIGNAL_BIO_UPDATED* = "bioUpdated"
|
const SIGNAL_BIO_UPDATED* = "bioUpdated"
|
||||||
const SIGNAL_MNEMONIC_REMOVED* = "mnemonicRemoved"
|
const SIGNAL_MNEMONIC_REMOVED* = "mnemonicRemoved"
|
||||||
|
const SIGNAL_SOCIAL_LINKS_UPDATED* = "socialLinksUpdated"
|
||||||
const SIGNAL_CURRENT_USER_STATUS_UPDATED* = "currentUserStatusUpdated"
|
const SIGNAL_CURRENT_USER_STATUS_UPDATED* = "currentUserStatusUpdated"
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
|
@ -38,6 +39,13 @@ type
|
||||||
statusType*: StatusType
|
statusType*: StatusType
|
||||||
text*: string
|
text*: string
|
||||||
|
|
||||||
|
SocialLinksArgs* = ref object of Args
|
||||||
|
socialLinks*: SocialLinks
|
||||||
|
error*: string
|
||||||
|
|
||||||
|
SettingProfilePictureArgs* = ref object of Args
|
||||||
|
value*: int
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type Service* = ref object of QObject
|
type Service* = ref object of QObject
|
||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
|
@ -47,7 +55,7 @@ QtObject:
|
||||||
notifExemptionsCache: Table[string, NotificationsExemptions]
|
notifExemptionsCache: Table[string, NotificationsExemptions]
|
||||||
|
|
||||||
# Forward declaration
|
# Forward declaration
|
||||||
proc fetchSocialLinks*(self: Service): SocialLinks
|
proc storeSocialLinksAndNotify(self: Service, data: SocialLinksArgs)
|
||||||
proc initNotificationSettings*(self: Service)
|
proc initNotificationSettings*(self: Service)
|
||||||
proc getNotifSettingAllowNotifications*(self: Service): bool
|
proc getNotifSettingAllowNotifications*(self: Service): bool
|
||||||
proc getNotifSettingOneToOneChats*(self: Service): string
|
proc getNotifSettingOneToOneChats*(self: Service): string
|
||||||
|
@ -76,7 +84,6 @@ QtObject:
|
||||||
let response = status_settings.getSettings()
|
let response = status_settings.getSettings()
|
||||||
self.settings = response.result.toSettingsDto()
|
self.settings = response.result.toSettingsDto()
|
||||||
self.initNotificationSettings()
|
self.initNotificationSettings()
|
||||||
self.socialLinks = self.fetchSocialLinks()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
let errDesription = e.msg
|
let errDesription = e.msg
|
||||||
error "error: ", errDesription
|
error "error: ", errDesription
|
||||||
|
@ -103,6 +110,10 @@ QtObject:
|
||||||
self.settings.mnemonic = ""
|
self.settings.mnemonic = ""
|
||||||
self.events.emit(SIGNAL_MNEMONIC_REMOVED, Args())
|
self.events.emit(SIGNAL_MNEMONIC_REMOVED, Args())
|
||||||
|
|
||||||
|
if receivedData.socialLinksInfo.links.len > 0 or
|
||||||
|
receivedData.socialLinksInfo.removed:
|
||||||
|
self.storeSocialLinksAndNotify(SocialLinksArgs(socialLinks: receivedData.socialLinksInfo.links))
|
||||||
|
|
||||||
self.initialized = true
|
self.initialized = true
|
||||||
|
|
||||||
proc initNotificationSettings(self: Service) =
|
proc initNotificationSettings(self: Service) =
|
||||||
|
@ -920,32 +931,38 @@ QtObject:
|
||||||
proc getSocialLinks*(self: Service): SocialLinks =
|
proc getSocialLinks*(self: Service): SocialLinks =
|
||||||
return self.socialLinks
|
return self.socialLinks
|
||||||
|
|
||||||
proc fetchSocialLinks*(self: Service): SocialLinks =
|
proc storeSocialLinksAndNotify(self: Service, data: SocialLinksArgs) =
|
||||||
|
self.socialLinks = data.socialLinks
|
||||||
|
self.events.emit(SIGNAL_SOCIAL_LINKS_UPDATED, data)
|
||||||
|
|
||||||
|
proc fetchAndStoreSocialLinks*(self: Service) =
|
||||||
|
var data = SocialLinksArgs()
|
||||||
try:
|
try:
|
||||||
let response = status_settings.getSocialLinks()
|
let response = status_settings.getSocialLinks()
|
||||||
|
|
||||||
if(not response.error.isNil):
|
if(not response.error.isNil):
|
||||||
|
data.error = response.error.message
|
||||||
error "error getting social links", errDescription = response.error.message
|
error "error getting social links", errDescription = response.error.message
|
||||||
|
data.socialLinks = toSocialLinks(response.result)
|
||||||
result = toSocialLinks(response.result)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data.error = e.msg
|
||||||
error "error getting social links", errDesription = e.msg
|
error "error getting social links", errDesription = e.msg
|
||||||
|
self.storeSocialLinksAndNotify(data)
|
||||||
|
|
||||||
proc setSocialLinks*(self: Service, links: SocialLinks): bool =
|
proc setSocialLinks*(self: Service, links: SocialLinks) =
|
||||||
result = false
|
var data = SocialLinksArgs()
|
||||||
let isValid = all(links, proc (link: SocialLink): bool = common_utils.validateLink(link.url))
|
let isValid = all(links, proc (link: SocialLink): bool = common_utils.validateLink(link.url))
|
||||||
if (not isValid):
|
if not isValid:
|
||||||
error "error saving social links"
|
data.error = "invalid link provided"
|
||||||
return result
|
error "validation error", errDescription=data.error
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
let response = status_settings.setSocialLinks(%*links)
|
let response = status_settings.addOrReplaceSocialLinks(%*links)
|
||||||
|
if not response.error.isNil:
|
||||||
if(not response.error.isNil):
|
data.error = response.error.message
|
||||||
error "error setting social links", errDescription = response.error.message
|
error "error saving social links", errDescription=data.error
|
||||||
|
return
|
||||||
self.socialLinks = self.fetchSocialLinks()
|
data.socialLinks = links
|
||||||
|
|
||||||
result = true
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "error setting social links", errDesription = e.msg
|
data.error = e.msg
|
||||||
|
error "error saving social links", errDescription=data.error
|
||||||
|
self.storeSocialLinksAndNotify(data)
|
||||||
|
|
|
@ -96,8 +96,8 @@ proc setExemptions*(id: string, muteAllMessages: bool, personalMentions: string,
|
||||||
proc deleteExemptions*(id: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc deleteExemptions*(id: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
return core.callPrivateRPC("settings_deleteExemptions", %* [id])
|
return core.callPrivateRPC("settings_deleteExemptions", %* [id])
|
||||||
|
|
||||||
proc setSocialLinks*(value: JsonNode): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc addOrReplaceSocialLinks*(value: JsonNode): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
return core.callPrivateRPC("settings_setSocialLinks", %* [value])
|
return core.callPrivateRPC("settings_addOrReplaceSocialLinks", %* [value])
|
||||||
|
|
||||||
proc getSocialLinks*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc getSocialLinks*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
return core.callPrivateRPC("settings_getSocialLinks")
|
return core.callPrivateRPC("settings_getSocialLinks")
|
||||||
|
|
|
@ -440,11 +440,11 @@ class ProfileSettingsView(BaseElement):
|
||||||
verify_equals(8, len(table)) # Expecting 8 as social media link fields to verify
|
verify_equals(8, len(table)) # Expecting 8 as social media link fields to verify
|
||||||
links = {
|
links = {
|
||||||
'Twitter': [table[0][0]],
|
'Twitter': [table[0][0]],
|
||||||
'Personal Site': [table[1][0]],
|
'Personal site': [table[1][0]],
|
||||||
'Github': [table[2][0]],
|
'Github': [table[2][0]],
|
||||||
'YouTube': [table[3][0]],
|
'YouTube channel': [table[3][0]],
|
||||||
'Discord': [table[4][0]],
|
'Discord handle': [table[4][0]],
|
||||||
'Telegram': [table[5][0]],
|
'Telegram handle': [table[5][0]],
|
||||||
'Custom link': [table[6][0], table[7][0]],
|
'Custom link': [table[6][0], table[7][0]],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,11 +481,11 @@ class ProfileSettingsView(BaseElement):
|
||||||
links = self.social_links
|
links = self.social_links
|
||||||
|
|
||||||
compare_text(links['Twitter'], twitter)
|
compare_text(links['Twitter'], twitter)
|
||||||
compare_text(links['Personal Site'], personal_site)
|
compare_text(links['Personal site'], personal_site)
|
||||||
compare_text(links['Github'], github)
|
compare_text(links['Github'], github)
|
||||||
compare_text(links['YouTube'], youtube)
|
compare_text(links['YouTube channel'], youtube)
|
||||||
compare_text(links['Discord'], discord)
|
compare_text(links['Discord handle'], discord)
|
||||||
compare_text(links['Telegram'], telegram)
|
compare_text(links['Telegram handle'], telegram)
|
||||||
compare_text(links[custom_link_text], custom_link)
|
compare_text(links[custom_link_text], custom_link)
|
||||||
|
|
||||||
def verify_social_no_links(self):
|
def verify_social_no_links(self):
|
||||||
|
|
|
@ -26,6 +26,7 @@ Control {
|
||||||
Component {
|
Component {
|
||||||
id: addSocialLinkModalComponent
|
id: addSocialLinkModalComponent
|
||||||
AddSocialLinkModal {
|
AddSocialLinkModal {
|
||||||
|
containsSocialLink: root.profileStore.containsSocialLink
|
||||||
onAddLinkRequested: root.profileStore.createLink(linkText, linkUrl, linkType, linkIcon)
|
onAddLinkRequested: root.profileStore.createLink(linkText, linkUrl, linkType, linkIcon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +34,7 @@ Control {
|
||||||
Component {
|
Component {
|
||||||
id: modifySocialLinkModal
|
id: modifySocialLinkModal
|
||||||
ModifySocialLinkModal {
|
ModifySocialLinkModal {
|
||||||
|
containsSocialLink: root.profileStore.containsSocialLink
|
||||||
onUpdateLinkRequested: root.profileStore.updateLink(uuid, linkText, linkUrl)
|
onUpdateLinkRequested: root.profileStore.updateLink(uuid, linkText, linkUrl)
|
||||||
onRemoveLinkRequested: root.profileStore.removeLink(uuid)
|
onRemoveLinkRequested: root.profileStore.removeLink(uuid)
|
||||||
}
|
}
|
||||||
|
@ -50,31 +52,31 @@ Control {
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
StatusLinkText {
|
StatusBaseText {
|
||||||
objectName: "addMoreSocialLinks"
|
text: qsTr("%1/%2").arg(root.profileStore.temporarySocialLinksModel.count).arg(Constants.maxNumOfSocialLinks)
|
||||||
text: qsTr("+ Add more links")
|
color: Theme.palette.baseColor1
|
||||||
color: Theme.palette.primaryColor1
|
|
||||||
font.pixelSize: Theme.tertiaryTextFontSize
|
|
||||||
font.weight: Font.Normal
|
|
||||||
onClicked: Global.openPopup(addSocialLinkModalComponent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SortFilterProxyModel {
|
|
||||||
id: filteredSocialLinksModel
|
|
||||||
sourceModel: root.socialLinksModel
|
|
||||||
filters: ExpressionFilter {
|
|
||||||
expression: model.text !== "" && model.url !== ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty placeholder when no links; dashed rounded rectangle
|
// empty placeholder when no links; dashed rounded rectangle
|
||||||
ShapeRectangle {
|
ShapeRectangle {
|
||||||
|
readonly property bool maxReached: root.profileStore.temporarySocialLinksModel.count === Constants.maxNumOfSocialLinks
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredWidth: parent.width - 4 // the rectangular path is rendered outside
|
Layout.preferredWidth: parent.width - 4 // the rectangular path is rendered outside
|
||||||
Layout.preferredHeight: 44
|
Layout.preferredHeight: 44
|
||||||
visible: !filteredSocialLinksModel.count
|
text: maxReached? qsTr("Link limit of %1 reached").arg(Constants.maxNumOfSocialLinks) : ""
|
||||||
text: qsTr("Your links will appear here")
|
|
||||||
|
StatusLinkText {
|
||||||
|
objectName: "addMoreSocialLinks"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !parent.maxReached
|
||||||
|
text: qsTr("+ Add a link")
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
font.pixelSize: Theme.tertiaryTextFontSize
|
||||||
|
font.weight: Font.Normal
|
||||||
|
onClicked: Global.openPopup(addSocialLinkModalComponent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusListView {
|
StatusListView {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import utils 1.0
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Popups 0.1
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ import AppLayouts.Profile.controls 1.0
|
||||||
StatusStackModal {
|
StatusStackModal {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property var containsSocialLink: function (text, url) {return false}
|
||||||
signal addLinkRequested(string linkText, string linkUrl, int linkType, string linkIcon)
|
signal addLinkRequested(string linkText, string linkUrl, int linkType, string linkIcon)
|
||||||
|
|
||||||
implicitWidth: 480 // design
|
implicitWidth: 480 // design
|
||||||
|
@ -26,7 +28,7 @@ StatusStackModal {
|
||||||
rightButtons: [finishButton]
|
rightButtons: [finishButton]
|
||||||
finishButton: StatusButton {
|
finishButton: StatusButton {
|
||||||
text: qsTr("Add")
|
text: qsTr("Add")
|
||||||
enabled: !!linkTarget.text
|
enabled: linkTarget.valid && (!customTitle.visible || customTitle.valid)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.addLinkRequested(d.selectedLinkTypeText || customTitle.text, // text for custom link, otherwise the link typeId
|
root.addLinkRequested(d.selectedLinkTypeText || customTitle.text, // text for custom link, otherwise the link typeId
|
||||||
ProfileUtils.addSocialLinkPrefix(linkTarget.text, d.selectedLinkType),
|
ProfileUtils.addSocialLinkPrefix(linkTarget.text, d.selectedLinkType),
|
||||||
|
@ -84,6 +86,8 @@ StatusStackModal {
|
||||||
asset.name: model.icon
|
asset.name: model.icon
|
||||||
asset.color: ProfileUtils.linkTypeColor(model.type)
|
asset.color: ProfileUtils.linkTypeColor(model.type)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
customTitle.reset()
|
||||||
|
linkTarget.reset()
|
||||||
d.selectedLinkIndex = index
|
d.selectedLinkIndex = index
|
||||||
root.currentIndex++
|
root.currentIndex++
|
||||||
}
|
}
|
||||||
|
@ -110,6 +114,28 @@ StatusStackModal {
|
||||||
icon: "language"
|
icon: "language"
|
||||||
charLimit: Constants.maxSocialLinkTextLength
|
charLimit: Constants.maxSocialLinkTextLength
|
||||||
input.tabNavItem: linkTarget.input.edit
|
input.tabNavItem: linkTarget.input.edit
|
||||||
|
validators: [
|
||||||
|
StatusValidator {
|
||||||
|
name: "text-validation"
|
||||||
|
validate: (value) => {
|
||||||
|
return value.trim() !== ""
|
||||||
|
}
|
||||||
|
errorMessage: qsTr("Invalid title")
|
||||||
|
},
|
||||||
|
StatusValidator {
|
||||||
|
name: "check-social-link-existence"
|
||||||
|
validate: (value) => {
|
||||||
|
return !root.containsSocialLink(value,
|
||||||
|
ProfileUtils.addSocialLinkPrefix(linkTarget.text, d.selectedLinkType))
|
||||||
|
}
|
||||||
|
errorMessage: d.selectedLinkType === Constants.socialLinkType.custom?
|
||||||
|
qsTr("Name and link combination already added") :
|
||||||
|
qsTr("Link already added")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onValidChanged: {linkTarget.validate(true)}
|
||||||
|
onTextChanged: {linkTarget.validate(true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticSocialLinkInput {
|
StaticSocialLinkInput {
|
||||||
|
@ -121,6 +147,29 @@ StatusStackModal {
|
||||||
linkType: d.selectedLinkType
|
linkType: d.selectedLinkType
|
||||||
icon: d.selectedIcon
|
icon: d.selectedIcon
|
||||||
input.tabNavItem: customTitle.input.edit
|
input.tabNavItem: customTitle.input.edit
|
||||||
|
|
||||||
|
validators: [
|
||||||
|
StatusValidator {
|
||||||
|
name: "link-validation"
|
||||||
|
validate: (value) => {
|
||||||
|
return value.trim() !== "" && Utils.validLink(ProfileUtils.addSocialLinkPrefix(value, d.selectedLinkType))
|
||||||
|
}
|
||||||
|
errorMessage: qsTr("Invalid %1").arg(ProfileUtils.linkTypeToDescription(linkTarget.linkType).toLowerCase() || qsTr("link"))
|
||||||
|
},
|
||||||
|
StatusValidator {
|
||||||
|
name: "check-social-link-existence"
|
||||||
|
validate: (value) => {
|
||||||
|
return !root.containsSocialLink(d.selectedLinkTypeText || customTitle.text,
|
||||||
|
ProfileUtils.addSocialLinkPrefix(value, d.selectedLinkType))
|
||||||
|
}
|
||||||
|
errorMessage: d.selectedLinkType === Constants.socialLinkType.custom?
|
||||||
|
qsTr("Name and link combination already added") :
|
||||||
|
qsTr("Link already added")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onValidChanged: {customTitle.validate(true)}
|
||||||
|
onTextChanged: {customTitle.validate(true)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,6 +8,7 @@ import utils 1.0
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Popups.Dialog 0.1
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ import AppLayouts.Profile.controls 1.0
|
||||||
StatusDialog {
|
StatusDialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property var containsSocialLink: function (text, url) {return false}
|
||||||
property int linkType: -1
|
property int linkType: -1
|
||||||
property string icon
|
property string icon
|
||||||
|
|
||||||
|
@ -44,12 +46,7 @@ StatusDialog {
|
||||||
rightButtons: ObjectModel {
|
rightButtons: ObjectModel {
|
||||||
StatusButton {
|
StatusButton {
|
||||||
text: qsTr("Update")
|
text: qsTr("Update")
|
||||||
enabled: {
|
enabled: linkTarget.valid && (!customTitle.visible || customTitle.valid)
|
||||||
if (!customTitle.visible)
|
|
||||||
return linkTarget.text && linkTarget.text !== linkUrl
|
|
||||||
return linkTarget.text && (linkTarget.text !== linkUrl || (customTitle.text && customTitle.text !== root.linkText))
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.updateLinkRequested(root.uuid, customTitle.text, ProfileUtils.addSocialLinkPrefix(linkTarget.text, root.linkType))
|
root.updateLinkRequested(root.uuid, customTitle.text, ProfileUtils.addSocialLinkPrefix(linkTarget.text, root.linkType))
|
||||||
root.close()
|
root.close()
|
||||||
|
@ -82,6 +79,29 @@ StatusDialog {
|
||||||
text: root.linkText
|
text: root.linkText
|
||||||
charLimit: Constants.maxSocialLinkTextLength
|
charLimit: Constants.maxSocialLinkTextLength
|
||||||
input.tabNavItem: linkTarget.input.edit
|
input.tabNavItem: linkTarget.input.edit
|
||||||
|
|
||||||
|
validators: [
|
||||||
|
StatusValidator {
|
||||||
|
name: "text-validation"
|
||||||
|
validate: (value) => {
|
||||||
|
return value.trim() !== ""
|
||||||
|
}
|
||||||
|
errorMessage: qsTr("Invalid title")
|
||||||
|
},
|
||||||
|
StatusValidator {
|
||||||
|
name: "check-social-link-existence"
|
||||||
|
validate: (value) => {
|
||||||
|
return !root.containsSocialLink(value,
|
||||||
|
ProfileUtils.addSocialLinkPrefix(linkTarget.text, root.linkType))
|
||||||
|
}
|
||||||
|
errorMessage: root.linkType === Constants.socialLinkType.custom?
|
||||||
|
qsTr("Name and link combination already added") :
|
||||||
|
qsTr("Link already added")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onValidChanged: {linkTarget.validate(true)}
|
||||||
|
onTextChanged: {linkTarget.validate(true)}
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticSocialLinkInput {
|
StaticSocialLinkInput {
|
||||||
|
@ -94,6 +114,29 @@ StatusDialog {
|
||||||
icon: root.icon
|
icon: root.icon
|
||||||
text: root.linkUrl
|
text: root.linkUrl
|
||||||
input.tabNavItem: customTitle.input.edit
|
input.tabNavItem: customTitle.input.edit
|
||||||
|
|
||||||
|
validators: [
|
||||||
|
StatusValidator {
|
||||||
|
name: "link-validation"
|
||||||
|
validate: (value) => {
|
||||||
|
return value.trim() !== "" && Utils.validLink(ProfileUtils.addSocialLinkPrefix(value, root.linkType))
|
||||||
|
}
|
||||||
|
errorMessage: qsTr("Invalid %1").arg(ProfileUtils.linkTypeToDescription(linkTarget.linkType).toLowerCase() || qsTr("link"))
|
||||||
|
},
|
||||||
|
StatusValidator {
|
||||||
|
name: "check-social-link-existence"
|
||||||
|
validate: (value) => {
|
||||||
|
return !root.containsSocialLink(customTitle.text,
|
||||||
|
ProfileUtils.addSocialLinkPrefix(value, root.linkType))
|
||||||
|
}
|
||||||
|
errorMessage: root.linkType === Constants.socialLinkType.custom?
|
||||||
|
qsTr("Name and link combination already added") :
|
||||||
|
qsTr("Link already added")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onValidChanged: {customTitle.validate(true)}
|
||||||
|
onTextChanged: {customTitle.validate(true)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,10 @@ QtObject {
|
||||||
root.profileModule.setDisplayName(displayName)
|
root.profileModule.setDisplayName(displayName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function containsSocialLink(text, url) {
|
||||||
|
return root.profileModule.containsSocialLink(text, url)
|
||||||
|
}
|
||||||
|
|
||||||
function createLink(text, url, linkType, icon) {
|
function createLink(text, url, linkType, icon) {
|
||||||
root.profileModule.createLink(text, url, linkType, icon)
|
root.profileModule.createLink(text, url, linkType, icon)
|
||||||
}
|
}
|
||||||
|
|
|
@ -617,6 +617,7 @@ QtObject {
|
||||||
readonly property int telegram: 6
|
readonly property int telegram: 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property int maxNumOfSocialLinks: 20
|
||||||
readonly property int maxSocialLinkTextLength: 24
|
readonly property int maxSocialLinkTextLength: 24
|
||||||
|
|
||||||
readonly property QtObject localPairingEventType: QtObject {
|
readonly property QtObject localPairingEventType: QtObject {
|
||||||
|
|
|
@ -24,11 +24,11 @@ QtObject {
|
||||||
|
|
||||||
function linkTypeToText(linkType) {
|
function linkTypeToText(linkType) {
|
||||||
if (linkType === Constants.socialLinkType.twitter) return qsTr("Twitter")
|
if (linkType === Constants.socialLinkType.twitter) return qsTr("Twitter")
|
||||||
if (linkType === Constants.socialLinkType.personalSite) return qsTr("Personal Site")
|
if (linkType === Constants.socialLinkType.personalSite) return qsTr("Personal site")
|
||||||
if (linkType === Constants.socialLinkType.github) return qsTr("Github")
|
if (linkType === Constants.socialLinkType.github) return qsTr("Github")
|
||||||
if (linkType === Constants.socialLinkType.youtube) return qsTr("YouTube")
|
if (linkType === Constants.socialLinkType.youtube) return qsTr("YouTube channel")
|
||||||
if (linkType === Constants.socialLinkType.discord) return qsTr("Discord")
|
if (linkType === Constants.socialLinkType.discord) return qsTr("Discord handle")
|
||||||
if (linkType === Constants.socialLinkType.telegram) return qsTr("Telegram")
|
if (linkType === Constants.socialLinkType.telegram) return qsTr("Telegram handle")
|
||||||
return "" // "custom" link type allows for user defined text
|
return "" // "custom" link type allows for user defined text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkTypeToDescription(linkType) {
|
function linkTypeToDescription(linkType) {
|
||||||
if (linkType === Constants.socialLinkType.twitter) return qsTr("Twitter Handle")
|
if (linkType === Constants.socialLinkType.twitter) return qsTr("Twitter username")
|
||||||
if (linkType === Constants.socialLinkType.personalSite) return qsTr("Personal Site")
|
if (linkType === Constants.socialLinkType.personalSite) return qsTr("Personal site")
|
||||||
if (linkType === Constants.socialLinkType.github) return qsTr("Github")
|
if (linkType === Constants.socialLinkType.github) return qsTr("Github")
|
||||||
if (linkType === Constants.socialLinkType.youtube) return qsTr("YouTube Channel")
|
if (linkType === Constants.socialLinkType.youtube) return qsTr("YouTube channel")
|
||||||
if (linkType === Constants.socialLinkType.discord) return qsTr("Discord Handle")
|
if (linkType === Constants.socialLinkType.discord) return qsTr("Discord handle")
|
||||||
if (linkType === Constants.socialLinkType.telegram) return qsTr("Telegram Handle")
|
if (linkType === Constants.socialLinkType.telegram) return qsTr("Telegram handle")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,14 @@ QtObject {
|
||||||
return Style.current.accountColors[colorIndex]
|
return Style.current.accountColors[colorIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validLink(link) {
|
||||||
|
if (link.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var regex = /[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/
|
||||||
|
return regex.test(link)
|
||||||
|
}
|
||||||
|
|
||||||
function getLinkStyle(link, hoveredLink, textColor) {
|
function getLinkStyle(link, hoveredLink, textColor) {
|
||||||
return `<style type="text/css">` +
|
return `<style type="text/css">` +
|
||||||
`a {` +
|
`a {` +
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit bf29188b2d5e88dde54549927f41b9c2808bf225
|
Subproject commit 874a0656db7dfe1a7e0b7e7008c6bef6ac801a40
|
Loading…
Reference in New Issue