Feat: Add social links to the profile showcase (#4775)

* feat: add social links to the profile showcase

* fix: deprecate old social links, add synced profile showcase to response
This commit is contained in:
Mikhail Rogachev 2024-02-26 16:53:40 +03:00 committed by GitHub
parent 92bc64bb41
commit 7cc4c12642
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1088 additions and 451 deletions

View File

@ -109,6 +109,7 @@ type Contact struct {
// Bio - description of the contact (tell us about yourself) // Bio - description of the contact (tell us about yourself)
Bio string `json:"bio"` Bio string `json:"bio"`
// Deprecated: use social links from ProfileShowcasePreferences
SocialLinks identity.SocialLinks `json:"socialLinks"` SocialLinks identity.SocialLinks `json:"socialLinks"`
Images map[string]images.IdentityImage `json:"images"` Images map[string]images.IdentityImage `json:"images"`

View File

@ -4,6 +4,16 @@ import "errors"
var ErrorNoAccountProvidedWithTokenOrCollectible = errors.New("no account provided with tokens or collectible") var ErrorNoAccountProvidedWithTokenOrCollectible = errors.New("no account provided with tokens or collectible")
var ErrorExceedMaxProfileShowcaseCommunitiesLimit = errors.New("exeed maximum profile showcase communities limit")
var ErrorExceedMaxProfileShowcaseAccountsLimit = errors.New("exeed maximum profile showcase accounts limit")
var ErrorExceedMaxProfileShowcaseCollectiblesLimit = errors.New("exeed maximum profile showcase collectibles limit")
var ErrorExceedMaxProfileShowcaseVerifiedTokensLimit = errors.New("exeed maximum profile showcase verified tokens limit")
var ErrorExceedMaxProfileShowcaseUnverifiedTokensLimit = errors.New("exeed maximum profile showcase unverified tokens limit")
var ErrorExceedMaxProfileShowcaseSocialLinksLimit = errors.New("exeed maximum profile showcase communities limit")
const maxProfileShowcaseSocialLinksLimit = 20
const maxProfileShowcaseOtherEntriesLimit = 100
type ProfileShowcaseVisibility int type ProfileShowcaseVisibility int
const ( const (
@ -21,6 +31,8 @@ const (
ProfileShowcaseMembershipStatusNotAMember ProfileShowcaseMembershipStatusNotAMember
) )
// Profile showcase preferences
type ProfileShowcaseCommunityPreference struct { type ProfileShowcaseCommunityPreference struct {
CommunityID string `json:"communityId"` CommunityID string `json:"communityId"`
ShowcaseVisibility ProfileShowcaseVisibility `json:"showcaseVisibility"` ShowcaseVisibility ProfileShowcaseVisibility `json:"showcaseVisibility"`
@ -60,6 +72,13 @@ type ProfileShowcaseUnverifiedTokenPreference struct {
Order int `json:"order"` Order int `json:"order"`
} }
type ProfileShowcaseSocialLinkPreference struct {
URL string `json:"url"`
Text string `json:"text"`
ShowcaseVisibility ProfileShowcaseVisibility `json:"showcaseVisibility"`
Order int `json:"order"`
}
type ProfileShowcasePreferences struct { type ProfileShowcasePreferences struct {
Clock uint64 `json:"clock"` Clock uint64 `json:"clock"`
Communities []*ProfileShowcaseCommunityPreference `json:"communities"` Communities []*ProfileShowcaseCommunityPreference `json:"communities"`
@ -67,8 +86,11 @@ type ProfileShowcasePreferences struct {
Collectibles []*ProfileShowcaseCollectiblePreference `json:"collectibles"` Collectibles []*ProfileShowcaseCollectiblePreference `json:"collectibles"`
VerifiedTokens []*ProfileShowcaseVerifiedTokenPreference `json:"verifiedTokens"` VerifiedTokens []*ProfileShowcaseVerifiedTokenPreference `json:"verifiedTokens"`
UnverifiedTokens []*ProfileShowcaseUnverifiedTokenPreference `json:"unverifiedTokens"` UnverifiedTokens []*ProfileShowcaseUnverifiedTokenPreference `json:"unverifiedTokens"`
SocialLinks []*ProfileShowcaseSocialLinkPreference `json:"socialLinks"`
} }
// Profile showcase for a contact
type ProfileShowcaseCommunity struct { type ProfileShowcaseCommunity struct {
CommunityID string `json:"communityId"` CommunityID string `json:"communityId"`
Order int `json:"order"` Order int `json:"order"`
@ -105,6 +127,12 @@ type ProfileShowcaseUnverifiedToken struct {
Order int `json:"order"` Order int `json:"order"`
} }
type ProfileShowcaseSocialLink struct {
URL string `json:"url"`
Text string `json:"text"`
Order int `json:"order"`
}
type ProfileShowcase struct { type ProfileShowcase struct {
ContactID string `json:"contactId"` ContactID string `json:"contactId"`
Communities []*ProfileShowcaseCommunity `json:"communities"` Communities []*ProfileShowcaseCommunity `json:"communities"`
@ -112,9 +140,29 @@ type ProfileShowcase struct {
Collectibles []*ProfileShowcaseCollectible `json:"collectibles"` Collectibles []*ProfileShowcaseCollectible `json:"collectibles"`
VerifiedTokens []*ProfileShowcaseVerifiedToken `json:"verifiedTokens"` VerifiedTokens []*ProfileShowcaseVerifiedToken `json:"verifiedTokens"`
UnverifiedTokens []*ProfileShowcaseUnverifiedToken `json:"unverifiedTokens"` UnverifiedTokens []*ProfileShowcaseUnverifiedToken `json:"unverifiedTokens"`
SocialLinks []*ProfileShowcaseSocialLink `json:"socialLinks"`
} }
func Validate(preferences *ProfileShowcasePreferences) error { func Validate(preferences *ProfileShowcasePreferences) error {
if len(preferences.Communities) > maxProfileShowcaseOtherEntriesLimit {
return ErrorExceedMaxProfileShowcaseCommunitiesLimit
}
if len(preferences.Accounts) > maxProfileShowcaseOtherEntriesLimit {
return ErrorExceedMaxProfileShowcaseAccountsLimit
}
if len(preferences.Collectibles) > maxProfileShowcaseOtherEntriesLimit {
return ErrorExceedMaxProfileShowcaseCollectiblesLimit
}
if len(preferences.VerifiedTokens) > maxProfileShowcaseOtherEntriesLimit {
return ErrorExceedMaxProfileShowcaseVerifiedTokensLimit
}
if len(preferences.UnverifiedTokens) > maxProfileShowcaseOtherEntriesLimit {
return ErrorExceedMaxProfileShowcaseUnverifiedTokensLimit
}
if len(preferences.SocialLinks) > maxProfileShowcaseSocialLinksLimit {
return ErrorExceedMaxProfileShowcaseSocialLinksLimit
}
if (len(preferences.VerifiedTokens) > 0 || len(preferences.UnverifiedTokens) > 0 || len(preferences.Collectibles) > 0) && if (len(preferences.VerifiedTokens) > 0 || len(preferences.UnverifiedTokens) > 0 || len(preferences.Collectibles) > 0) &&
len(preferences.Accounts) == 0 { len(preferences.Accounts) == 0 {
return ErrorNoAccountProvidedWithTokenOrCollectible return ErrorNoAccountProvidedWithTokenOrCollectible

View File

@ -177,6 +177,12 @@ func (m *Messenger) handleBackedUpProfile(message *protobuf.BackedUpProfile, bac
return err return err
} }
profileShowcasePreferences, err := m.saveProfileShowcasePreferencesProto(message.ProfileShowcasePreferences, false)
if err != nil {
return err
}
response.SetProfileShowcasePreferences(profileShowcasePreferences)
var ensUsernameDetails []*ensservice.UsernameDetail var ensUsernameDetails []*ensservice.UsernameDetail
for _, d := range message.EnsUsernameDetails { for _, d := range message.EnsUsernameDetails {
dd, err := m.saveEnsUsernameDetailProto(d) dd, err := m.saveEnsUsernameDetailProto(d)

View File

@ -231,6 +231,7 @@ func (s *MessengerBackupSuite) TestBackupProfile() {
s.Require().Len(bob2ProfileShowcasePreferences.Collectibles, 0) s.Require().Len(bob2ProfileShowcasePreferences.Collectibles, 0)
s.Require().Len(bob2ProfileShowcasePreferences.VerifiedTokens, 0) s.Require().Len(bob2ProfileShowcasePreferences.VerifiedTokens, 0)
s.Require().Len(bob2ProfileShowcasePreferences.UnverifiedTokens, 0) s.Require().Len(bob2ProfileShowcasePreferences.UnverifiedTokens, 0)
s.Require().Len(bob2ProfileShowcasePreferences.SocialLinks, 0)
// Backup // Backup
clock, err := bob1.BackupData(context.Background()) clock, err := bob1.BackupData(context.Background())

View File

@ -3930,6 +3930,7 @@ func (m *Messenger) addNewKeypairAddedOnPairedDeviceACNotification(keyUID string
return nil return nil
} }
func (m *Messenger) HandleSyncProfileShowcasePreferences(state *ReceivedMessageState, p *protobuf.SyncProfileShowcasePreferences, msg *v1protocol.StatusMessage) error { func (m *Messenger) HandleSyncProfileShowcasePreferences(state *ReceivedMessageState, p *protobuf.SyncProfileShowcasePreferences, statusMessage *v1protocol.StatusMessage) error {
return m.saveProfileShowcasePreferencesProto(p, false) _, err := m.saveProfileShowcasePreferencesProto(p, false)
return err
} }

View File

@ -32,6 +32,12 @@ var errorNoAccountPresentedForCollectible = errors.New("account holding the coll
var errorDublicateAccountAddress = errors.New("duplicate account address") var errorDublicateAccountAddress = errors.New("duplicate account address")
var errorAccountVisibilityLowerThanCollectible = errors.New("account visibility lower than collectible") var errorAccountVisibilityLowerThanCollectible = errors.New("account visibility lower than collectible")
func sortProfileEntyByOrder(slice interface{}, getOrder func(int) int) {
sort.Slice(slice, func(i, j int) bool {
return getOrder(j) > getOrder(i)
})
}
func toCollectibleUniqueID(contractAddress string, tokenID string, chainID uint64) (thirdparty.CollectibleUniqueID, error) { func toCollectibleUniqueID(contractAddress string, tokenID string, chainID uint64) (thirdparty.CollectibleUniqueID, error) {
tokenIDInt := new(big.Int) tokenIDInt := new(big.Int)
tokenIDInt, isTokenIDOk := tokenIDInt.SetString(tokenID, 10) tokenIDInt, isTokenIDOk := tokenIDInt.SetString(tokenID, 10)
@ -198,6 +204,22 @@ func (m *Messenger) toProfileShowcaseUnverifiedTokensProto(preferences []*identi
return entries return entries
} }
func (m *Messenger) toProfileShowcaseSocialLinksProto(preferences []*identity.ProfileShowcaseSocialLinkPreference, visibility identity.ProfileShowcaseVisibility) []*protobuf.ProfileShowcaseSocialLink {
entries := []*protobuf.ProfileShowcaseSocialLink{}
for _, preference := range preferences {
if preference.ShowcaseVisibility != visibility {
continue
}
entries = append(entries, &protobuf.ProfileShowcaseSocialLink{
Text: preference.Text,
Url: preference.URL,
Order: uint32(preference.Order),
})
}
return entries
}
func (m *Messenger) fromProfileShowcaseCommunityProto(senderPubKey *ecdsa.PublicKey, messages []*protobuf.ProfileShowcaseCommunity) []*identity.ProfileShowcaseCommunity { func (m *Messenger) fromProfileShowcaseCommunityProto(senderPubKey *ecdsa.PublicKey, messages []*protobuf.ProfileShowcaseCommunity) []*identity.ProfileShowcaseCommunity {
entries := []*identity.ProfileShowcaseCommunity{} entries := []*identity.ProfileShowcaseCommunity{}
for _, message := range messages { for _, message := range messages {
@ -296,6 +318,18 @@ func (m *Messenger) fromProfileShowcaseUnverifiedTokenProto(messages []*protobuf
return entries return entries
} }
func (m *Messenger) fromProfileShowcaseSocialLinkProto(messages []*protobuf.ProfileShowcaseSocialLink) []*identity.ProfileShowcaseSocialLink {
entries := []*identity.ProfileShowcaseSocialLink{}
for _, entry := range messages {
entries = append(entries, &identity.ProfileShowcaseSocialLink{
Text: entry.Text,
URL: entry.Url,
Order: int(entry.Order),
})
}
return entries
}
func (m *Messenger) SetProfileShowcasePreferences(preferences *identity.ProfileShowcasePreferences, sync bool) error { func (m *Messenger) SetProfileShowcasePreferences(preferences *identity.ProfileShowcasePreferences, sync bool) error {
clock, _ := m.getLastClockWithRelatedChat() clock, _ := m.getLastClockWithRelatedChat()
preferences.Clock = clock preferences.Clock = clock
@ -450,6 +484,7 @@ func (m *Messenger) GetProfileShowcaseForSelfIdentity() (*protobuf.ProfileShowca
Collectibles: m.toProfileShowcaseCollectibleProto(preferences.Collectibles, identity.ProfileShowcaseVisibilityEveryone), Collectibles: m.toProfileShowcaseCollectibleProto(preferences.Collectibles, identity.ProfileShowcaseVisibilityEveryone),
VerifiedTokens: m.toProfileShowcaseVerifiedTokensProto(preferences.VerifiedTokens, identity.ProfileShowcaseVisibilityEveryone), VerifiedTokens: m.toProfileShowcaseVerifiedTokensProto(preferences.VerifiedTokens, identity.ProfileShowcaseVisibilityEveryone),
UnverifiedTokens: m.toProfileShowcaseUnverifiedTokensProto(preferences.UnverifiedTokens, identity.ProfileShowcaseVisibilityEveryone), UnverifiedTokens: m.toProfileShowcaseUnverifiedTokensProto(preferences.UnverifiedTokens, identity.ProfileShowcaseVisibilityEveryone),
SocialLinks: m.toProfileShowcaseSocialLinksProto(preferences.SocialLinks, identity.ProfileShowcaseVisibilityEveryone),
} }
forContacts := &protobuf.ProfileShowcaseEntries{ forContacts := &protobuf.ProfileShowcaseEntries{
@ -458,6 +493,7 @@ func (m *Messenger) GetProfileShowcaseForSelfIdentity() (*protobuf.ProfileShowca
Collectibles: m.toProfileShowcaseCollectibleProto(preferences.Collectibles, identity.ProfileShowcaseVisibilityContacts), Collectibles: m.toProfileShowcaseCollectibleProto(preferences.Collectibles, identity.ProfileShowcaseVisibilityContacts),
VerifiedTokens: m.toProfileShowcaseVerifiedTokensProto(preferences.VerifiedTokens, identity.ProfileShowcaseVisibilityContacts), VerifiedTokens: m.toProfileShowcaseVerifiedTokensProto(preferences.VerifiedTokens, identity.ProfileShowcaseVisibilityContacts),
UnverifiedTokens: m.toProfileShowcaseUnverifiedTokensProto(preferences.UnverifiedTokens, identity.ProfileShowcaseVisibilityContacts), UnverifiedTokens: m.toProfileShowcaseUnverifiedTokensProto(preferences.UnverifiedTokens, identity.ProfileShowcaseVisibilityContacts),
SocialLinks: m.toProfileShowcaseSocialLinksProto(preferences.SocialLinks, identity.ProfileShowcaseVisibilityContacts),
} }
forIDVerifiedContacts := &protobuf.ProfileShowcaseEntries{ forIDVerifiedContacts := &protobuf.ProfileShowcaseEntries{
@ -466,6 +502,7 @@ func (m *Messenger) GetProfileShowcaseForSelfIdentity() (*protobuf.ProfileShowca
Collectibles: m.toProfileShowcaseCollectibleProto(preferences.Collectibles, identity.ProfileShowcaseVisibilityIDVerifiedContacts), Collectibles: m.toProfileShowcaseCollectibleProto(preferences.Collectibles, identity.ProfileShowcaseVisibilityIDVerifiedContacts),
VerifiedTokens: m.toProfileShowcaseVerifiedTokensProto(preferences.VerifiedTokens, identity.ProfileShowcaseVisibilityIDVerifiedContacts), VerifiedTokens: m.toProfileShowcaseVerifiedTokensProto(preferences.VerifiedTokens, identity.ProfileShowcaseVisibilityIDVerifiedContacts),
UnverifiedTokens: m.toProfileShowcaseUnverifiedTokensProto(preferences.UnverifiedTokens, identity.ProfileShowcaseVisibilityIDVerifiedContacts), UnverifiedTokens: m.toProfileShowcaseUnverifiedTokensProto(preferences.UnverifiedTokens, identity.ProfileShowcaseVisibilityIDVerifiedContacts),
SocialLinks: m.toProfileShowcaseSocialLinksProto(preferences.SocialLinks, identity.ProfileShowcaseVisibilityIDVerifiedContacts),
} }
mutualContacts := []*Contact{} mutualContacts := []*Contact{}
@ -498,39 +535,6 @@ func (m *Messenger) GetProfileShowcaseForSelfIdentity() (*protobuf.ProfileShowca
}, nil }, nil
} }
func (m *Messenger) buildProfileShowcaseFromEntries(
contactID string,
communities []*identity.ProfileShowcaseCommunity,
accounts []*identity.ProfileShowcaseAccount,
collectibles []*identity.ProfileShowcaseCollectible,
verifiedTokens []*identity.ProfileShowcaseVerifiedToken,
unverifiedTokens []*identity.ProfileShowcaseUnverifiedToken) *identity.ProfileShowcase {
sort.Slice(communities, func(i, j int) bool {
return communities[j].Order > communities[i].Order
})
sort.Slice(accounts, func(i, j int) bool {
return accounts[j].Order > accounts[i].Order
})
sort.Slice(collectibles, func(i, j int) bool {
return collectibles[j].Order > collectibles[i].Order
})
sort.Slice(verifiedTokens, func(i, j int) bool {
return verifiedTokens[j].Order > verifiedTokens[i].Order
})
sort.Slice(unverifiedTokens, func(i, j int) bool {
return unverifiedTokens[j].Order > unverifiedTokens[i].Order
})
return &identity.ProfileShowcase{
ContactID: contactID,
Communities: communities,
Accounts: accounts,
Collectibles: collectibles,
VerifiedTokens: verifiedTokens,
UnverifiedTokens: unverifiedTokens,
}
}
func (m *Messenger) BuildProfileShowcaseFromIdentity(state *ReceivedMessageState, message *protobuf.ProfileShowcase) error { func (m *Messenger) BuildProfileShowcaseFromIdentity(state *ReceivedMessageState, message *protobuf.ProfileShowcase) error {
senderPubKey := state.CurrentMessageState.PublicKey senderPubKey := state.CurrentMessageState.PublicKey
contactID := state.CurrentMessageState.Contact.ID contactID := state.CurrentMessageState.Contact.ID
@ -540,12 +544,14 @@ func (m *Messenger) BuildProfileShowcaseFromIdentity(state *ReceivedMessageState
collectibles := []*identity.ProfileShowcaseCollectible{} collectibles := []*identity.ProfileShowcaseCollectible{}
verifiedTokens := []*identity.ProfileShowcaseVerifiedToken{} verifiedTokens := []*identity.ProfileShowcaseVerifiedToken{}
unverifiedTokens := []*identity.ProfileShowcaseUnverifiedToken{} unverifiedTokens := []*identity.ProfileShowcaseUnverifiedToken{}
socialLinks := []*identity.ProfileShowcaseSocialLink{}
communities = append(communities, m.fromProfileShowcaseCommunityProto(senderPubKey, message.ForEveryone.Communities)...) communities = append(communities, m.fromProfileShowcaseCommunityProto(senderPubKey, message.ForEveryone.Communities)...)
accounts = append(accounts, m.fromProfileShowcaseAccountProto(message.ForEveryone.Accounts)...) accounts = append(accounts, m.fromProfileShowcaseAccountProto(message.ForEveryone.Accounts)...)
collectibles = append(collectibles, m.fromProfileShowcaseCollectibleProto(message.ForEveryone.Collectibles)...) collectibles = append(collectibles, m.fromProfileShowcaseCollectibleProto(message.ForEveryone.Collectibles)...)
verifiedTokens = append(verifiedTokens, m.fromProfileShowcaseVerifiedTokenProto(message.ForEveryone.VerifiedTokens)...) verifiedTokens = append(verifiedTokens, m.fromProfileShowcaseVerifiedTokenProto(message.ForEveryone.VerifiedTokens)...)
unverifiedTokens = append(unverifiedTokens, m.fromProfileShowcaseUnverifiedTokenProto(message.ForEveryone.UnverifiedTokens)...) unverifiedTokens = append(unverifiedTokens, m.fromProfileShowcaseUnverifiedTokenProto(message.ForEveryone.UnverifiedTokens)...)
socialLinks = append(socialLinks, m.fromProfileShowcaseSocialLinkProto(message.ForEveryone.SocialLinks)...)
forContacts, err := m.DecryptProfileShowcaseEntriesWithPubKey(senderPubKey, message.ForContacts) forContacts, err := m.DecryptProfileShowcaseEntriesWithPubKey(senderPubKey, message.ForContacts)
if err != nil { if err != nil {
@ -558,6 +564,7 @@ func (m *Messenger) BuildProfileShowcaseFromIdentity(state *ReceivedMessageState
collectibles = append(collectibles, m.fromProfileShowcaseCollectibleProto(forContacts.Collectibles)...) collectibles = append(collectibles, m.fromProfileShowcaseCollectibleProto(forContacts.Collectibles)...)
verifiedTokens = append(verifiedTokens, m.fromProfileShowcaseVerifiedTokenProto(forContacts.VerifiedTokens)...) verifiedTokens = append(verifiedTokens, m.fromProfileShowcaseVerifiedTokenProto(forContacts.VerifiedTokens)...)
unverifiedTokens = append(unverifiedTokens, m.fromProfileShowcaseUnverifiedTokenProto(forContacts.UnverifiedTokens)...) unverifiedTokens = append(unverifiedTokens, m.fromProfileShowcaseUnverifiedTokenProto(forContacts.UnverifiedTokens)...)
socialLinks = append(socialLinks, m.fromProfileShowcaseSocialLinkProto(forContacts.SocialLinks)...)
} }
forIDVerifiedContacts, err := m.DecryptProfileShowcaseEntriesWithPubKey(senderPubKey, message.ForIdVerifiedContacts) forIDVerifiedContacts, err := m.DecryptProfileShowcaseEntriesWithPubKey(senderPubKey, message.ForIdVerifiedContacts)
@ -571,10 +578,25 @@ func (m *Messenger) BuildProfileShowcaseFromIdentity(state *ReceivedMessageState
collectibles = append(collectibles, m.fromProfileShowcaseCollectibleProto(forIDVerifiedContacts.Collectibles)...) collectibles = append(collectibles, m.fromProfileShowcaseCollectibleProto(forIDVerifiedContacts.Collectibles)...)
verifiedTokens = append(verifiedTokens, m.fromProfileShowcaseVerifiedTokenProto(forIDVerifiedContacts.VerifiedTokens)...) verifiedTokens = append(verifiedTokens, m.fromProfileShowcaseVerifiedTokenProto(forIDVerifiedContacts.VerifiedTokens)...)
unverifiedTokens = append(unverifiedTokens, m.fromProfileShowcaseUnverifiedTokenProto(forIDVerifiedContacts.UnverifiedTokens)...) unverifiedTokens = append(unverifiedTokens, m.fromProfileShowcaseUnverifiedTokenProto(forIDVerifiedContacts.UnverifiedTokens)...)
socialLinks = append(socialLinks, m.fromProfileShowcaseSocialLinkProto(forIDVerifiedContacts.SocialLinks)...)
} }
newShowcase := m.buildProfileShowcaseFromEntries( sortProfileEntyByOrder(communities, func(i int) int { return communities[i].Order })
contactID, communities, accounts, collectibles, verifiedTokens, unverifiedTokens) sortProfileEntyByOrder(accounts, func(i int) int { return accounts[i].Order })
sortProfileEntyByOrder(collectibles, func(i int) int { return collectibles[i].Order })
sortProfileEntyByOrder(verifiedTokens, func(i int) int { return verifiedTokens[i].Order })
sortProfileEntyByOrder(unverifiedTokens, func(i int) int { return unverifiedTokens[i].Order })
sortProfileEntyByOrder(socialLinks, func(i int) int { return socialLinks[i].Order })
newShowcase := &identity.ProfileShowcase{
ContactID: contactID,
Communities: communities,
Accounts: accounts,
Collectibles: collectibles,
VerifiedTokens: verifiedTokens,
UnverifiedTokens: unverifiedTokens,
SocialLinks: socialLinks,
}
oldShowcase, err := m.persistence.GetProfileShowcaseForContact(contactID) oldShowcase, err := m.persistence.GetProfileShowcaseForContact(contactID)
if err != nil { if err != nil {
@ -646,9 +668,9 @@ func (m *Messenger) DeleteProfileShowcaseCommunity(community *communities.Commun
return nil return nil
} }
func (m *Messenger) saveProfileShowcasePreferencesProto(p *protobuf.SyncProfileShowcasePreferences, shouldSync bool) error { func (m *Messenger) saveProfileShowcasePreferencesProto(p *protobuf.SyncProfileShowcasePreferences, shouldSync bool) (*identity.ProfileShowcasePreferences, error) {
preferences := FromProfileShowcasePreferencesProto(p) preferences := FromProfileShowcasePreferencesProto(p)
return m.setProfileShowcasePreferences(preferences, shouldSync) return preferences, m.setProfileShowcasePreferences(preferences, shouldSync)
} }
func (m *Messenger) syncProfileShowcasePreferences(ctx context.Context, rawMessageHandler RawMessageHandler) error { func (m *Messenger) syncProfileShowcasePreferences(ctx context.Context, rawMessageHandler RawMessageHandler) error {

View File

@ -25,7 +25,7 @@ func ToProfileShowcaseCommunityPreferenceProto(p *identity.ProfileShowcaseCommun
return &protobuf.ProfileShowcaseCommunityPreference{ return &protobuf.ProfileShowcaseCommunityPreference{
CommunityId: p.CommunityID, CommunityId: p.CommunityID,
ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility), ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: int32(p.Order), Order: uint32(p.Order),
} }
} }
@ -63,7 +63,7 @@ func ToProfileShowcaseAccountPreferenceProto(p *identity.ProfileShowcaseAccountP
ColorId: p.ColorID, ColorId: p.ColorID,
Emoji: p.Emoji, Emoji: p.Emoji,
ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility), ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: int32(p.Order), Order: uint32(p.Order),
} }
} }
@ -103,7 +103,7 @@ func ToProfileShowcaseCollectiblePreferenceProto(p *identity.ProfileShowcaseColl
CommunityId: p.CommunityID, CommunityId: p.CommunityID,
AccountAddress: p.AccountAddress, AccountAddress: p.AccountAddress,
ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility), ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: int32(p.Order), Order: uint32(p.Order),
} }
} }
@ -135,7 +135,7 @@ func ToProfileShowcaseVerifiedTokenPreferenceProto(p *identity.ProfileShowcaseVe
return &protobuf.ProfileShowcaseVerifiedTokenPreference{ return &protobuf.ProfileShowcaseVerifiedTokenPreference{
Symbol: p.Symbol, Symbol: p.Symbol,
ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility), ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: int32(p.Order), Order: uint32(p.Order),
} }
} }
@ -172,7 +172,7 @@ func ToProfileShowcaseUnverifiedTokenPreferenceProto(p *identity.ProfileShowcase
ChainId: p.ChainID, ChainId: p.ChainID,
CommunityId: p.CommunityID, CommunityId: p.CommunityID,
ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility), ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: int32(p.Order), Order: uint32(p.Order),
} }
} }
@ -184,6 +184,40 @@ func ToProfileShowcaseUnverifiedTokensPreferenceProto(preferences []*identity.Pr
return out return out
} }
func FromProfileShowcaseSocialLinkPreferenceProto(p *protobuf.ProfileShowcaseSocialLinkPreference) *identity.ProfileShowcaseSocialLinkPreference {
return &identity.ProfileShowcaseSocialLinkPreference{
Text: p.GetText(),
URL: p.GetUrl(),
ShowcaseVisibility: identity.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: int(p.Order),
}
}
func FromProfileShowcaseSocialLinksPreferencesProto(preferences []*protobuf.ProfileShowcaseSocialLinkPreference) []*identity.ProfileShowcaseSocialLinkPreference {
out := make([]*identity.ProfileShowcaseSocialLinkPreference, 0, len(preferences))
for _, p := range preferences {
out = append(out, FromProfileShowcaseSocialLinkPreferenceProto(p))
}
return out
}
func ToProfileShowcaseSocialLinkPreferenceProto(p *identity.ProfileShowcaseSocialLinkPreference) *protobuf.ProfileShowcaseSocialLinkPreference {
return &protobuf.ProfileShowcaseSocialLinkPreference{
Text: p.Text,
Url: p.URL,
ShowcaseVisibility: protobuf.ProfileShowcaseVisibility(p.ShowcaseVisibility),
Order: uint32(p.Order),
}
}
func ToProfileShowcaseSocialLinksPreferenceProto(preferences []*identity.ProfileShowcaseSocialLinkPreference) []*protobuf.ProfileShowcaseSocialLinkPreference {
out := make([]*protobuf.ProfileShowcaseSocialLinkPreference, 0, len(preferences))
for _, p := range preferences {
out = append(out, ToProfileShowcaseSocialLinkPreferenceProto(p))
}
return out
}
func FromProfileShowcasePreferencesProto(p *protobuf.SyncProfileShowcasePreferences) *identity.ProfileShowcasePreferences { func FromProfileShowcasePreferencesProto(p *protobuf.SyncProfileShowcasePreferences) *identity.ProfileShowcasePreferences {
return &identity.ProfileShowcasePreferences{ return &identity.ProfileShowcasePreferences{
Clock: p.GetClock(), Clock: p.GetClock(),
@ -192,6 +226,7 @@ func FromProfileShowcasePreferencesProto(p *protobuf.SyncProfileShowcasePreferen
Collectibles: FromProfileShowcaseCollectiblesPreferencesProto(p.Collectibles), Collectibles: FromProfileShowcaseCollectiblesPreferencesProto(p.Collectibles),
VerifiedTokens: FromProfileShowcaseVerifiedTokensPreferencesProto(p.VerifiedTokens), VerifiedTokens: FromProfileShowcaseVerifiedTokensPreferencesProto(p.VerifiedTokens),
UnverifiedTokens: FromProfileShowcaseUnverifiedTokensPreferencesProto(p.UnverifiedTokens), UnverifiedTokens: FromProfileShowcaseUnverifiedTokensPreferencesProto(p.UnverifiedTokens),
SocialLinks: FromProfileShowcaseSocialLinksPreferencesProto(p.SocialLinks),
} }
} }
@ -203,5 +238,6 @@ func ToProfileShowcasePreferencesProto(p *identity.ProfileShowcasePreferences) *
Collectibles: ToProfileShowcaseCollectiblesPreferenceProto(p.Collectibles), Collectibles: ToProfileShowcaseCollectiblesPreferenceProto(p.Collectibles),
VerifiedTokens: ToProfileShowcaseVerifiedTokensPreferenceProto(p.VerifiedTokens), VerifiedTokens: ToProfileShowcaseVerifiedTokensPreferenceProto(p.VerifiedTokens),
UnverifiedTokens: ToProfileShowcaseUnverifiedTokensPreferenceProto(p.UnverifiedTokens), UnverifiedTokens: ToProfileShowcaseUnverifiedTokensPreferenceProto(p.UnverifiedTokens),
SocialLinks: ToProfileShowcaseSocialLinksPreferenceProto(p.SocialLinks),
} }
} }

View File

@ -249,6 +249,11 @@ func (s *TestMessengerProfileShowcase) TestSaveAndGetProfileShowcasePreferences(
for i := 0; i < len(response.UnverifiedTokens); i++ { for i := 0; i < len(response.UnverifiedTokens); i++ {
s.Require().Equal(*response.UnverifiedTokens[i], *request.UnverifiedTokens[i]) s.Require().Equal(*response.UnverifiedTokens[i], *request.UnverifiedTokens[i])
} }
s.Require().Equal(len(response.SocialLinks), len(request.SocialLinks))
for i := 0; i < len(response.SocialLinks); i++ {
s.Require().Equal(*response.SocialLinks[i], *request.SocialLinks[i])
}
} }
func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesWithWrongVisibility() { func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesWithWrongVisibility() {
@ -357,6 +362,23 @@ func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntri
Order: 1, Order: 1,
}, },
}, },
SocialLinks: []*protobuf.ProfileShowcaseSocialLink{
&protobuf.ProfileShowcaseSocialLink{
Text: identity.TwitterID,
Url: "https://twitter.com/ethstatus",
Order: 1,
},
&protobuf.ProfileShowcaseSocialLink{
Text: identity.TwitterID,
Url: "https://twitter.com/StatusIMBlog",
Order: 2,
},
&protobuf.ProfileShowcaseSocialLink{
Text: identity.GithubID,
Url: "https://github.com/status-im",
Order: 3,
},
},
} }
data, err := s.m.EncryptProfileShowcaseEntriesWithContactPubKeys(entries, s.m.Contacts()) data, err := s.m.EncryptProfileShowcaseEntriesWithContactPubKeys(entries, s.m.Contacts())
s.Require().NoError(err) s.Require().NoError(err)
@ -401,6 +423,13 @@ func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntri
s.Require().Equal(entries.UnverifiedTokens[i].ChainId, entriesBack.UnverifiedTokens[i].ChainId) s.Require().Equal(entries.UnverifiedTokens[i].ChainId, entriesBack.UnverifiedTokens[i].ChainId)
s.Require().Equal(entries.UnverifiedTokens[i].Order, entriesBack.UnverifiedTokens[i].Order) s.Require().Equal(entries.UnverifiedTokens[i].Order, entriesBack.UnverifiedTokens[i].Order)
} }
s.Require().Equal(len(entries.SocialLinks), len(entriesBack.SocialLinks))
for i := 0; i < len(entriesBack.SocialLinks); i++ {
s.Require().Equal(entries.SocialLinks[i].Text, entriesBack.SocialLinks[i].Text)
s.Require().Equal(entries.SocialLinks[i].Url, entriesBack.SocialLinks[i].Url)
s.Require().Equal(entries.SocialLinks[i].Order, entriesBack.SocialLinks[i].Order)
}
} }
func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() { func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
@ -499,11 +528,19 @@ func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID)
s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order) s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order)
s.Require().Len(profileShowcase.SocialLinks, 2)
s.Require().Equal(profileShowcase.SocialLinks[0].Text, request.SocialLinks[0].Text)
s.Require().Equal(profileShowcase.SocialLinks[0].URL, request.SocialLinks[0].URL)
s.Require().Equal(profileShowcase.SocialLinks[0].Order, request.SocialLinks[0].Order)
s.Require().Equal(profileShowcase.SocialLinks[1].Text, request.SocialLinks[2].Text)
s.Require().Equal(profileShowcase.SocialLinks[1].URL, request.SocialLinks[2].URL)
s.Require().Equal(profileShowcase.SocialLinks[1].Order, request.SocialLinks[2].Order)
// Get summarised profile data for verified contact // Get summarised profile data for verified contact
resp, err = WaitOnMessengerResponse( resp, err = WaitOnMessengerResponse(
verifiedContact, verifiedContact,
func(r *MessengerResponse) bool { func(r *MessengerResponse) bool {
return len(r.updatedProfileShowcases) > 0 return len(r.updatedProfileShowcases) > 0 && r.updatedProfileShowcases[contactID] != nil
}, },
"no messages", "no messages",
) )
@ -546,6 +583,17 @@ func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
s.Require().Equal(profileShowcase.UnverifiedTokens[1].ContractAddress, request.UnverifiedTokens[1].ContractAddress) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ContractAddress, request.UnverifiedTokens[1].ContractAddress)
s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID)
s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order) s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order)
s.Require().Len(profileShowcase.SocialLinks, 3)
s.Require().Equal(profileShowcase.SocialLinks[0].Text, request.SocialLinks[0].Text)
s.Require().Equal(profileShowcase.SocialLinks[0].URL, request.SocialLinks[0].URL)
s.Require().Equal(profileShowcase.SocialLinks[0].Order, request.SocialLinks[0].Order)
s.Require().Equal(profileShowcase.SocialLinks[1].Text, request.SocialLinks[1].Text)
s.Require().Equal(profileShowcase.SocialLinks[1].URL, request.SocialLinks[1].URL)
s.Require().Equal(profileShowcase.SocialLinks[1].Order, request.SocialLinks[1].Order)
s.Require().Equal(profileShowcase.SocialLinks[2].Text, request.SocialLinks[2].Text)
s.Require().Equal(profileShowcase.SocialLinks[2].URL, request.SocialLinks[2].URL)
s.Require().Equal(profileShowcase.SocialLinks[2].Order, request.SocialLinks[2].Order)
} }
func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipUnencryptedCommunities() { func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipUnencryptedCommunities() {

View File

@ -333,7 +333,7 @@ func (m *Messenger) HandleSyncRawMessages(rawMessages []*protobuf.RawMessage) er
if err != nil { if err != nil {
return err return err
} }
err = m.saveProfileShowcasePreferencesProto(&message, false) _, err = m.saveProfileShowcasePreferencesProto(&message, false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -466,6 +466,26 @@ func DummyProfileShowcasePreferences(withCollectibles bool) *identity.ProfileSho
Order: 1, Order: 1,
}, },
}, },
SocialLinks: []*identity.ProfileShowcaseSocialLinkPreference{
&identity.ProfileShowcaseSocialLinkPreference{
Text: identity.TwitterID,
URL: "https://twitter.com/ethstatus",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
Order: 1,
},
&identity.ProfileShowcaseSocialLinkPreference{
Text: identity.TwitterID,
URL: "https://twitter.com/StatusIMBlog",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityIDVerifiedContacts,
Order: 2,
},
&identity.ProfileShowcaseSocialLinkPreference{
Text: identity.GithubID,
URL: "https://github.com/status-im",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
Order: 3,
},
},
} }
if withCollectibles { if withCollectibles {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
-- Create tables for storing social links in the prodile showcase
CREATE TABLE profile_showcase_social_links_preferences (
url VARCHAR NOT NULL CHECK (length(trim(url)) > 0),
text VARCHAR NOT NULL CHECK (length(trim(text)) > 0),
visibility INT NOT NULL DEFAULT 0,
sort_order INT DEFAULT 0,
PRIMARY KEY (text, url)
);
CREATE TABLE profile_showcase_social_links_contacts (
url VARCHAR NOT NULL CHECK (length(trim(url)) > 0),
text VARCHAR NOT NULL CHECK (length(trim(text)) > 0),
sort_order INT DEFAULT 0,
contact_id TEXT NOT NULL,
PRIMARY KEY (contact_id, text, url)
);
CREATE INDEX profile_showcase_social_links_contact_id ON profile_showcase_social_links_contacts (contact_id);
-- Copy existing social links to a new table
INSERT INTO profile_showcase_social_links_preferences (text, url, sort_order)
SELECT text, url, position
FROM profile_social_links;

View File

@ -8,6 +8,7 @@ import (
"github.com/status-im/status-go/protocol/identity" "github.com/status-im/status-go/protocol/identity"
) )
// Profile showcase preferences
const upsertProfileShowcasePreferencesQuery = "UPDATE profile_showcase_preferences SET clock=? WHERE NOT EXISTS (SELECT 1 FROM profile_showcase_preferences WHERE clock >= ?)" const upsertProfileShowcasePreferencesQuery = "UPDATE profile_showcase_preferences SET clock=? WHERE NOT EXISTS (SELECT 1 FROM profile_showcase_preferences WHERE clock >= ?)"
const selectProfileShowcasePreferencesQuery = "SELECT clock FROM profile_showcase_preferences" const selectProfileShowcasePreferencesQuery = "SELECT clock FROM profile_showcase_preferences"
@ -29,6 +30,11 @@ const selectProfileShowcaseVerifiedTokenPreferenceQuery = "SELECT symbol, visibi
const upsertProfileShowcaseUnverifiedTokenPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_unverified_tokens_preferences(contract_address, chain_id, community_id, visibility, sort_order) VALUES (?, ?, ?, ?, ?)" // #nosec G101 const upsertProfileShowcaseUnverifiedTokenPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_unverified_tokens_preferences(contract_address, chain_id, community_id, visibility, sort_order) VALUES (?, ?, ?, ?, ?)" // #nosec G101
const selectProfileShowcaseUnverifiedTokenPreferenceQuery = "SELECT contract_address, chain_id, community_id, visibility, sort_order FROM profile_showcase_unverified_tokens_preferences" // #nosec G101 const selectProfileShowcaseUnverifiedTokenPreferenceQuery = "SELECT contract_address, chain_id, community_id, visibility, sort_order FROM profile_showcase_unverified_tokens_preferences" // #nosec G101
const upsertProfileShowcaseSocialLinkPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_social_links_preferences(url, text, visibility, sort_order) VALUES (?, ?, ?, ?)" // #nosec G101
const selectProfileShowcaseSocialLinkPreferenceQuery = "SELECT url, text, visibility, sort_order FROM profile_showcase_social_links_preferences" // #nosec G101
// Profile showcase for a contact
const upsertContactProfileShowcaseCommunityQuery = "INSERT OR REPLACE INTO profile_showcase_communities_contacts(contact_id, community_id, sort_order) VALUES (?, ?, ?)" // #nosec G101 const upsertContactProfileShowcaseCommunityQuery = "INSERT OR REPLACE INTO profile_showcase_communities_contacts(contact_id, community_id, sort_order) VALUES (?, ?, ?)" // #nosec G101
const selectContactProfileShowcaseCommunityQuery = "SELECT community_id, sort_order FROM profile_showcase_communities_contacts WHERE contact_id = ?" // #nosec G101 const selectContactProfileShowcaseCommunityQuery = "SELECT community_id, sort_order FROM profile_showcase_communities_contacts WHERE contact_id = ?" // #nosec G101
const removeContactProfileShowcaseCommunityQuery = "DELETE FROM profile_showcase_communities_contacts WHERE contact_id = ?" // #nosec G101 const removeContactProfileShowcaseCommunityQuery = "DELETE FROM profile_showcase_communities_contacts WHERE contact_id = ?" // #nosec G101
@ -49,6 +55,10 @@ const upsertContactProfileShowcaseUnverifiedTokenQuery = "INSERT OR REPLACE INTO
const selectContactProfileShowcaseUnverifiedTokenQuery = "SELECT contract_address, chain_id, community_id, sort_order FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?" // #nosec G101 const selectContactProfileShowcaseUnverifiedTokenQuery = "SELECT contract_address, chain_id, community_id, sort_order FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?" // #nosec G101
const removeContactProfileShowcaseUnverifiedTokenQuery = "DELETE FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?" // #nosec G101 const removeContactProfileShowcaseUnverifiedTokenQuery = "DELETE FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?" // #nosec G101
const upsertContactProfileShowcaseSocialLinkQuery = "INSERT OR REPLACE INTO profile_showcase_social_links_contacts(contact_id, url, text, sort_order) VALUES (?, ?, ?, ?)" // #nosec G101
const selectContactProfileShowcaseSocialLinkQuery = "SELECT url, text, sort_order FROM profile_showcase_social_links_contacts WHERE contact_id = ?" // #nosec G101
const removeContactProfileShowcaseSocialLinkQuery = "DELETE FROM profile_showcase_social_links_contacts WHERE contact_id = ?" // #nosec G101
const selectProfileShowcaseAccountsWhichMatchTheAddress = ` const selectProfileShowcaseAccountsWhichMatchTheAddress = `
SELECT psa.* SELECT psa.*
FROM FROM
@ -61,7 +71,7 @@ WHERE
psa.address = ? psa.address = ?
` `
// Queries for showcase preferences // Queries for the profile showcase preferences
func (db sqlitePersistence) saveProfileShowcasePreferencesClock(tx *sql.Tx, clock uint64) error { func (db sqlitePersistence) saveProfileShowcasePreferencesClock(tx *sql.Tx, clock uint64) error {
_, err := tx.Exec(upsertProfileShowcasePreferencesQuery, clock, clock) _, err := tx.Exec(upsertProfileShowcasePreferencesQuery, clock, clock)
@ -247,18 +257,6 @@ func (db sqlitePersistence) saveProfileShowcaseVerifiedTokenPreference(tx *sql.T
return err return err
} }
func (db sqlitePersistence) saveProfileShowcaseUnverifiedTokenPreference(tx *sql.Tx, token *identity.ProfileShowcaseUnverifiedTokenPreference) error {
_, err := tx.Exec(upsertProfileShowcaseUnverifiedTokenPreferenceQuery,
token.ContractAddress,
token.ChainID,
token.CommunityID,
token.ShowcaseVisibility,
token.Order,
)
return err
}
func (db sqlitePersistence) getProfileShowcaseVerifiedTokensPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseVerifiedTokenPreference, error) { func (db sqlitePersistence) getProfileShowcaseVerifiedTokensPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseVerifiedTokenPreference, error) {
rows, err := tx.Query(selectProfileShowcaseVerifiedTokenPreferenceQuery) rows, err := tx.Query(selectProfileShowcaseVerifiedTokenPreferenceQuery)
if err != nil { if err != nil {
@ -285,6 +283,18 @@ func (db sqlitePersistence) getProfileShowcaseVerifiedTokensPreferences(tx *sql.
return tokens, nil return tokens, nil
} }
func (db sqlitePersistence) saveProfileShowcaseUnverifiedTokenPreference(tx *sql.Tx, token *identity.ProfileShowcaseUnverifiedTokenPreference) error {
_, err := tx.Exec(upsertProfileShowcaseUnverifiedTokenPreferenceQuery,
token.ContractAddress,
token.ChainID,
token.CommunityID,
token.ShowcaseVisibility,
token.Order,
)
return err
}
func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseUnverifiedTokenPreference, error) { func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseUnverifiedTokenPreference, error) {
rows, err := tx.Query(selectProfileShowcaseUnverifiedTokenPreferenceQuery) rows, err := tx.Query(selectProfileShowcaseUnverifiedTokenPreferenceQuery)
if err != nil { if err != nil {
@ -313,7 +323,45 @@ func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensPreferences(tx *sq
return tokens, nil return tokens, nil
} }
// Queries for contacts showcase func (db sqlitePersistence) saveProfileShowcaseSocialLinkPreference(tx *sql.Tx, link *identity.ProfileShowcaseSocialLinkPreference) error {
_, err := tx.Exec(upsertProfileShowcaseSocialLinkPreferenceQuery,
link.URL,
link.Text,
link.ShowcaseVisibility,
link.Order,
)
return err
}
func (db sqlitePersistence) getProfileShowcaseSocialLinkPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseSocialLinkPreference, error) {
rows, err := tx.Query(selectProfileShowcaseSocialLinkPreferenceQuery)
if err != nil {
return nil, err
}
links := []*identity.ProfileShowcaseSocialLinkPreference{}
for rows.Next() {
link := &identity.ProfileShowcaseSocialLinkPreference{}
err := rows.Scan(
&link.URL,
&link.Text,
&link.ShowcaseVisibility,
&link.Order,
)
if err != nil {
return nil, err
}
links = append(links, link)
}
return links, nil
}
// Queries for the profile showcase for a contact
func (db sqlitePersistence) saveProfileShowcaseCommunityContact(tx *sql.Tx, contactID string, community *identity.ProfileShowcaseCommunity) error { func (db sqlitePersistence) saveProfileShowcaseCommunityContact(tx *sql.Tx, contactID string, community *identity.ProfileShowcaseCommunity) error {
_, err := tx.Exec(upsertContactProfileShowcaseCommunityQuery, _, err := tx.Exec(upsertContactProfileShowcaseCommunityQuery,
contactID, contactID,
@ -468,18 +516,6 @@ func (db sqlitePersistence) saveProfileShowcaseVerifiedTokenContact(tx *sql.Tx,
return err return err
} }
func (db sqlitePersistence) saveProfileShowcaseUnverifiedTokenContact(tx *sql.Tx, contactID string, token *identity.ProfileShowcaseUnverifiedToken) error {
_, err := tx.Exec(upsertContactProfileShowcaseUnverifiedTokenQuery,
contactID,
token.ContractAddress,
token.ChainID,
token.CommunityID,
token.Order,
)
return err
}
func (db sqlitePersistence) getProfileShowcaseVerifiedTokensContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseVerifiedToken, error) { func (db sqlitePersistence) getProfileShowcaseVerifiedTokensContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseVerifiedToken, error) {
rows, err := tx.Query(selectContactProfileShowcaseVerifiedTokenQuery, contactID) rows, err := tx.Query(selectContactProfileShowcaseVerifiedTokenQuery, contactID)
if err != nil { if err != nil {
@ -503,6 +539,23 @@ func (db sqlitePersistence) getProfileShowcaseVerifiedTokensContact(tx *sql.Tx,
return tokens, nil return tokens, nil
} }
func (db sqlitePersistence) clearProfileShowcaseVerifiedTokensContact(tx *sql.Tx, contactID string) error {
_, err := tx.Exec(removeContactProfileShowcaseVerifiedTokenQuery, contactID)
return err
}
func (db sqlitePersistence) saveProfileShowcaseUnverifiedTokenContact(tx *sql.Tx, contactID string, token *identity.ProfileShowcaseUnverifiedToken) error {
_, err := tx.Exec(upsertContactProfileShowcaseUnverifiedTokenQuery,
contactID,
token.ContractAddress,
token.ChainID,
token.CommunityID,
token.Order,
)
return err
}
func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseUnverifiedToken, error) { func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseUnverifiedToken, error) {
rows, err := tx.Query(selectContactProfileShowcaseUnverifiedTokenQuery, contactID) rows, err := tx.Query(selectContactProfileShowcaseUnverifiedTokenQuery, contactID)
if err != nil { if err != nil {
@ -528,13 +581,52 @@ func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensContact(tx *sql.Tx
return tokens, nil return tokens, nil
} }
func (db sqlitePersistence) clearProfileShowcaseVerifiedTokensContact(tx *sql.Tx, contactID string) error { func (db sqlitePersistence) clearProfileShowcaseUnverifiedTokensContact(tx *sql.Tx, contactID string) error {
_, err := tx.Exec(removeContactProfileShowcaseVerifiedTokenQuery, contactID) _, err := tx.Exec(removeContactProfileShowcaseUnverifiedTokenQuery, contactID)
return err return err
} }
func (db sqlitePersistence) clearProfileShowcaseUnverifiedTokensContact(tx *sql.Tx, contactID string) error { func (db sqlitePersistence) saveProfileShowcaseSocialLinkContact(tx *sql.Tx, contactID string, link *identity.ProfileShowcaseSocialLink) error {
_, err := tx.Exec(removeContactProfileShowcaseUnverifiedTokenQuery, contactID) _, err := tx.Exec(upsertContactProfileShowcaseSocialLinkQuery,
contactID,
link.URL,
link.Text,
link.Order,
)
return err
}
func (db sqlitePersistence) getProfileShowcaseSocialLinksContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseSocialLink, error) {
rows, err := tx.Query(selectContactProfileShowcaseSocialLinkQuery, contactID)
if err != nil {
return nil, err
}
links := []*identity.ProfileShowcaseSocialLink{}
for rows.Next() {
link := &identity.ProfileShowcaseSocialLink{}
err := rows.Scan(
&link.URL,
&link.Text,
&link.Order)
if err != nil {
return nil, err
}
links = append(links, link)
}
err = rows.Err()
if err != nil {
return nil, err
}
return links, nil
}
func (db sqlitePersistence) clearProfileShowcaseSocialLinksContact(tx *sql.Tx, contactID string) error {
_, err := tx.Exec(removeContactProfileShowcaseSocialLinkQuery, contactID)
return err return err
} }
@ -593,6 +685,13 @@ func (db sqlitePersistence) SaveProfileShowcasePreferences(preferences *identity
} }
} }
for _, link := range preferences.SocialLinks {
err = db.saveProfileShowcaseSocialLinkPreference(tx, link)
if err != nil {
return err
}
}
return nil return nil
} }
@ -656,6 +755,11 @@ func (db sqlitePersistence) GetProfileShowcasePreferences() (*identity.ProfileSh
return nil, err return nil, err
} }
socialLinks, err := db.getProfileShowcaseSocialLinkPreferences(tx)
if err != nil {
return nil, err
}
return &identity.ProfileShowcasePreferences{ return &identity.ProfileShowcasePreferences{
Clock: clock, Clock: clock,
Communities: communities, Communities: communities,
@ -663,6 +767,7 @@ func (db sqlitePersistence) GetProfileShowcasePreferences() (*identity.ProfileSh
Collectibles: collectibles, Collectibles: collectibles,
VerifiedTokens: verifiedTokens, VerifiedTokens: verifiedTokens,
UnverifiedTokens: unverifiedTokens, UnverifiedTokens: unverifiedTokens,
SocialLinks: socialLinks,
}, nil }, nil
} }
@ -715,6 +820,13 @@ func (db sqlitePersistence) SaveProfileShowcaseForContact(showcase *identity.Pro
} }
} }
for _, link := range showcase.SocialLinks {
err = db.saveProfileShowcaseSocialLinkContact(tx, showcase.ContactID, link)
if err != nil {
return err
}
}
return nil return nil
} }
@ -757,6 +869,11 @@ func (db sqlitePersistence) GetProfileShowcaseForContact(contactID string) (*ide
return nil, err return nil, err
} }
socialLinks, err := db.getProfileShowcaseSocialLinksContact(tx, contactID)
if err != nil {
return nil, err
}
return &identity.ProfileShowcase{ return &identity.ProfileShowcase{
ContactID: contactID, ContactID: contactID,
Communities: communities, Communities: communities,
@ -764,6 +881,7 @@ func (db sqlitePersistence) GetProfileShowcaseForContact(contactID string) (*ide
Collectibles: collectibles, Collectibles: collectibles,
VerifiedTokens: verifiedTokens, VerifiedTokens: verifiedTokens,
UnverifiedTokens: unverifiedTokens, UnverifiedTokens: unverifiedTokens,
SocialLinks: socialLinks,
}, nil }, nil
} }
@ -806,5 +924,10 @@ func (db sqlitePersistence) ClearProfileShowcaseForContact(contactID string) err
return err return err
} }
err = db.clearProfileShowcaseSocialLinksContact(tx, contactID)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -91,6 +91,26 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcasePreferences() {
Order: 1, Order: 1,
}, },
}, },
SocialLinks: []*identity.ProfileShowcaseSocialLinkPreference{
&identity.ProfileShowcaseSocialLinkPreference{
Text: identity.TwitterID,
URL: "https://twitter.com/ethstatus",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
Order: 1,
},
&identity.ProfileShowcaseSocialLinkPreference{
Text: identity.TwitterID,
URL: "https://twitter.com/StatusIMBlog",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityIDVerifiedContacts,
Order: 2,
},
&identity.ProfileShowcaseSocialLinkPreference{
Text: identity.GithubID,
URL: "https://github.com/status-im",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
Order: 3,
},
},
} }
err = persistence.SaveProfileShowcasePreferences(preferences) err = persistence.SaveProfileShowcasePreferences(preferences)
@ -123,6 +143,11 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcasePreferences() {
for i := 0; i < len(preferences.UnverifiedTokens); i++ { for i := 0; i < len(preferences.UnverifiedTokens); i++ {
s.Require().Equal(*preferences.UnverifiedTokens[i], *preferencesBack.UnverifiedTokens[i]) s.Require().Equal(*preferences.UnverifiedTokens[i], *preferencesBack.UnverifiedTokens[i])
} }
s.Require().Equal(len(preferencesBack.SocialLinks), len(preferences.SocialLinks))
for i := 0; i < len(preferences.SocialLinks); i++ {
s.Require().Equal(*preferences.SocialLinks[i], *preferencesBack.SocialLinks[i])
}
} }
func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() { func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
@ -197,6 +222,18 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
Order: 1, Order: 1,
}, },
}, },
SocialLinks: []*identity.ProfileShowcaseSocialLink{
&identity.ProfileShowcaseSocialLink{
URL: "https://status.app/",
Text: "Status",
Order: 1,
},
&identity.ProfileShowcaseSocialLink{
URL: "https://github.com/status-im",
Text: "Github",
Order: 2,
},
},
} }
err = persistence.SaveProfileShowcaseForContact(showcase1) err = persistence.SaveProfileShowcaseForContact(showcase1)
s.Require().NoError(err) s.Require().NoError(err)
@ -249,6 +286,10 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
for i := 0; i < len(showcase1.UnverifiedTokens); i++ { for i := 0; i < len(showcase1.UnverifiedTokens); i++ {
s.Require().Equal(*showcase1.UnverifiedTokens[i], *showcase1Back.UnverifiedTokens[i]) s.Require().Equal(*showcase1.UnverifiedTokens[i], *showcase1Back.UnverifiedTokens[i])
} }
s.Require().Equal(len(showcase1.SocialLinks), len(showcase1Back.SocialLinks))
for i := 0; i < len(showcase1.SocialLinks); i++ {
s.Require().Equal(*showcase1.SocialLinks[i], *showcase1Back.SocialLinks[i])
}
showcase2Back, err := persistence.GetProfileShowcaseForContact("contact_2") showcase2Back, err := persistence.GetProfileShowcaseForContact("contact_2")
s.Require().NoError(err) s.Require().NoError(err)
@ -261,6 +302,7 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
s.Require().Equal(0, len(showcase2Back.Accounts)) s.Require().Equal(0, len(showcase2Back.Accounts))
s.Require().Equal(0, len(showcase2Back.VerifiedTokens)) s.Require().Equal(0, len(showcase2Back.VerifiedTokens))
s.Require().Equal(0, len(showcase2Back.UnverifiedTokens)) s.Require().Equal(0, len(showcase2Back.UnverifiedTokens))
s.Require().Equal(0, len(showcase2Back.SocialLinks))
} }
func (s *TestProfileShowcasePersistence) TestFetchingProfileShowcaseAccountsByAddress() { func (s *TestProfileShowcasePersistence) TestFetchingProfileShowcaseAccountsByAddress() {

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@ syntax = "proto3";
option go_package = "./;protobuf"; option go_package = "./;protobuf";
package protobuf; package protobuf;
// Profile showcase for a contact
message ProfileShowcaseCommunity { message ProfileShowcaseCommunity {
string community_id = 1; string community_id = 1;
uint32 order = 2; uint32 order = 2;
@ -39,12 +41,19 @@ message ProfileShowcaseUnverifiedToken {
string community_id = 4; string community_id = 4;
} }
message ProfileShowcaseSocialLink {
string url = 1;
uint32 order = 2;
string text = 3;
}
message ProfileShowcaseEntries { message ProfileShowcaseEntries {
repeated ProfileShowcaseCommunity communities = 1; repeated ProfileShowcaseCommunity communities = 1;
repeated ProfileShowcaseAccount accounts = 2; repeated ProfileShowcaseAccount accounts = 2;
repeated ProfileShowcaseCollectible collectibles = 3; repeated ProfileShowcaseCollectible collectibles = 3;
repeated ProfileShowcaseVerifiedToken verifiedTokens = 4; repeated ProfileShowcaseVerifiedToken verified_tokens = 4;
repeated ProfileShowcaseUnverifiedToken unverifiedTokens = 5; repeated ProfileShowcaseUnverifiedToken unverified_tokens = 5;
repeated ProfileShowcaseSocialLink social_links = 6;
} }
message ProfileShowcaseEntriesEncrypted { message ProfileShowcaseEntriesEncrypted {
@ -58,7 +67,7 @@ message ProfileShowcase {
ProfileShowcaseEntriesEncrypted for_id_verified_contacts = 3; ProfileShowcaseEntriesEncrypted for_id_verified_contacts = 3;
} }
// Preferences // Profile showcase preferences
enum ProfileShowcaseVisibility { enum ProfileShowcaseVisibility {
PROFILE_SHOWCASE_VISIBILITY_NO_ONE = 0; PROFILE_SHOWCASE_VISIBILITY_NO_ONE = 0;
@ -70,7 +79,7 @@ enum ProfileShowcaseVisibility {
message ProfileShowcaseCommunityPreference { message ProfileShowcaseCommunityPreference {
string community_id = 1; string community_id = 1;
ProfileShowcaseVisibility showcase_visibility = 2; ProfileShowcaseVisibility showcase_visibility = 2;
int32 order = 3; uint32 order = 3;
} }
message ProfileShowcaseAccountPreference { message ProfileShowcaseAccountPreference {
@ -79,7 +88,7 @@ message ProfileShowcaseAccountPreference {
string color_id = 3; string color_id = 3;
string emoji = 4; string emoji = 4;
ProfileShowcaseVisibility showcase_visibility = 5; ProfileShowcaseVisibility showcase_visibility = 5;
int32 order = 6; uint32 order = 6;
} }
message ProfileShowcaseCollectiblePreference { message ProfileShowcaseCollectiblePreference {
@ -89,13 +98,13 @@ message ProfileShowcaseCollectiblePreference {
string community_id = 4; string community_id = 4;
string account_address = 5; string account_address = 5;
ProfileShowcaseVisibility showcase_visibility = 6; ProfileShowcaseVisibility showcase_visibility = 6;
int32 order = 7; uint32 order = 7;
} }
message ProfileShowcaseVerifiedTokenPreference { message ProfileShowcaseVerifiedTokenPreference {
string symbol = 1; string symbol = 1;
ProfileShowcaseVisibility showcase_visibility = 2; ProfileShowcaseVisibility showcase_visibility = 2;
int32 order = 3; uint32 order = 3;
} }
message ProfileShowcaseUnverifiedTokenPreference { message ProfileShowcaseUnverifiedTokenPreference {
@ -103,7 +112,14 @@ message ProfileShowcaseUnverifiedTokenPreference {
uint64 chain_id = 2; uint64 chain_id = 2;
string community_id = 3; string community_id = 3;
ProfileShowcaseVisibility showcase_visibility = 4; ProfileShowcaseVisibility showcase_visibility = 4;
int32 order = 5; uint32 order = 5;
}
message ProfileShowcaseSocialLinkPreference {
string url = 1;
string text = 2;
uint32 order = 3;
ProfileShowcaseVisibility showcase_visibility = 4;
} }
message SyncProfileShowcasePreferences { message SyncProfileShowcasePreferences {
@ -113,4 +129,5 @@ message SyncProfileShowcasePreferences {
repeated ProfileShowcaseCollectiblePreference collectibles = 4; repeated ProfileShowcaseCollectiblePreference collectibles = 4;
repeated ProfileShowcaseVerifiedTokenPreference verified_tokens = 5; repeated ProfileShowcaseVerifiedTokenPreference verified_tokens = 5;
repeated ProfileShowcaseUnverifiedTokenPreference unverified_tokens = 6; repeated ProfileShowcaseUnverifiedTokenPreference unverified_tokens = 6;
repeated ProfileShowcaseSocialLinkPreference social_links = 7;
} }

View File

@ -7,10 +7,12 @@ import (
) )
type BackedUpProfile struct { type BackedUpProfile struct {
DisplayName string `json:"displayName,omitempty"` DisplayName string `json:"displayName,omitempty"`
Images []images.IdentityImage `json:"images,omitempty"` Images []images.IdentityImage `json:"images,omitempty"`
SocialLinks []*identity.SocialLink `json:"socialLinks,omitempty"` // Deprecated: use social links from ProfileShowcasePreferences
EnsUsernameDetails []*ens.UsernameDetail `json:"ensUsernameDetails,omitempty"` SocialLinks []*identity.SocialLink `json:"socialLinks,omitempty"`
EnsUsernameDetails []*ens.UsernameDetail `json:"ensUsernameDetails,omitempty"`
ProfileShowcasePreferences identity.ProfileShowcasePreferences `json:"profile_showcase_preferences,omitempty"`
} }
func (sfwr *WakuBackedUpDataResponse) SetDisplayName(displayName string) { func (sfwr *WakuBackedUpDataResponse) SetDisplayName(displayName string) {
@ -21,6 +23,7 @@ func (sfwr *WakuBackedUpDataResponse) SetImages(images []images.IdentityImage) {
sfwr.Profile.Images = images sfwr.Profile.Images = images
} }
// Deprecated: use social links from ProfileShowcasePreferences
func (sfwr *WakuBackedUpDataResponse) SetSocialLinks(socialLinks []*identity.SocialLink) { func (sfwr *WakuBackedUpDataResponse) SetSocialLinks(socialLinks []*identity.SocialLink) {
sfwr.Profile.SocialLinks = socialLinks sfwr.Profile.SocialLinks = socialLinks
} }
@ -28,3 +31,7 @@ func (sfwr *WakuBackedUpDataResponse) SetSocialLinks(socialLinks []*identity.Soc
func (sfwr *WakuBackedUpDataResponse) SetEnsUsernameDetails(ensUsernameDetails []*ens.UsernameDetail) { func (sfwr *WakuBackedUpDataResponse) SetEnsUsernameDetails(ensUsernameDetails []*ens.UsernameDetail) {
sfwr.Profile.EnsUsernameDetails = ensUsernameDetails sfwr.Profile.EnsUsernameDetails = ensUsernameDetails
} }
func (sfwr *WakuBackedUpDataResponse) SetProfileShowcasePreferences(profileShowcasePreferences *identity.ProfileShowcasePreferences) {
sfwr.Profile.ProfileShowcasePreferences = *profileShowcasePreferences
}

View File

@ -454,6 +454,11 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
err = serverBackend.StatusNode().EnsService().API().Add(ctx, ensChainID, ensUsername) err = serverBackend.StatusNode().EnsService().API().Add(ctx, ensChainID, ensUsername)
require.NoError(s.T(), err) require.NoError(s.T(), err)
// generate profile showcase preferences
profileShowcasePreferences := protocol.DummyProfileShowcasePreferences(false)
err = serverMessenger.SetProfileShowcasePreferences(profileShowcasePreferences, false)
require.NoError(s.T(), err)
// generate local deleted message // generate local deleted message
_, err = serverMessenger.CreatePublicChat(&requests.CreatePublicChat{ID: publicChatID}) _, err = serverMessenger.CreatePublicChat(&requests.CreatePublicChat{ID: publicChatID})
require.NoError(s.T(), err) require.NoError(s.T(), err)
@ -496,10 +501,16 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
require.NoError(s.T(), err) require.NoError(s.T(), err)
require.Equal(s.T(), 1, len(bookmarks)) require.Equal(s.T(), 1, len(bookmarks))
require.Equal(s.T(), "status.im", bookmarks[0].Name) require.Equal(s.T(), "status.im", bookmarks[0].Name)
clientSocialLinks, err := clientMessenger.GetSocialLinks() clientSocialLinks, err := clientMessenger.GetSocialLinks()
require.NoError(s.T(), err) require.NoError(s.T(), err)
require.Equal(s.T(), 1, len(clientSocialLinks)) require.Equal(s.T(), 1, len(clientSocialLinks))
require.True(s.T(), socialLinksToAdd.Equal(clientSocialLinks)) require.True(s.T(), socialLinksToAdd.Equal(clientSocialLinks))
clientProfileShowcasePreferences, err := clientMessenger.GetProfileShowcasePreferences()
require.NoError(s.T(), err)
require.True(s.T(), reflect.DeepEqual(profileShowcasePreferences, clientProfileShowcasePreferences))
uds, err := clientBackend.StatusNode().EnsService().API().GetEnsUsernames(ctx) uds, err := clientBackend.StatusNode().EnsService().API().GetEnsUsernames(ctx)
require.NoError(s.T(), err) require.NoError(s.T(), err)
require.Equal(s.T(), 1, len(uds)) require.Equal(s.T(), 1, len(uds))

View File

@ -171,10 +171,12 @@ func (api *SettingsAPI) SetBio(bio string) error {
return (*api.messenger).SetBio(bio) return (*api.messenger).SetBio(bio)
} }
// Deprecated: use social links from ProfileShowcasePreferences
func (api *SettingsAPI) GetSocialLinks() (identity.SocialLinks, error) { func (api *SettingsAPI) GetSocialLinks() (identity.SocialLinks, error) {
return api.db.GetSocialLinks() return api.db.GetSocialLinks()
} }
// Deprecated: use social links from ProfileShowcasePreferences
func (api *SettingsAPI) AddOrReplaceSocialLinks(links identity.SocialLinks) error { func (api *SettingsAPI) AddOrReplaceSocialLinks(links identity.SocialLinks) error {
for _, link := range links { for _, link := range links {
if len(link.Text) == 0 { if len(link.Text) == 0 {