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