Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Profile showcase data sharing #4209

Merged
merged 6 commits into from
Nov 9, 2023

Conversation

MishkaRogachev
Copy link
Contributor

@MishkaRogachev MishkaRogachev commented Oct 25, 2023

Provide broadcasting of profile showcase changes for 3 categories: everyone, mutual contacts, and ID verified contacts.

Important changes:

  • Add .proto messages for profile showcase updates
  • Dispatch profile showcase updates on profile preference changes
  • Send unencrypted profile showcase updates for everyone
  • Send encrypted profile showcase updates for mutual contacts and ID verified contacts
  • Receive and handle profile showcase updates
  • Decrypt showcase updates for mutual contacts and ID verified contacts
  • Store profile showcase updates for contacts in the database
  • Promote changes to UI

Closes #4143

@status-im-auto
Copy link
Member

status-im-auto commented Oct 25, 2023

Jenkins Builds

Click to see older builds (115)
Commit #️⃣ Finished (UTC) Duration Platform Result
d209db0 #1 2023-10-25 16:04:26 ~1 min linux 📄log
d209db0 #1 2023-10-25 16:04:29 ~1 min ios 📄log
d209db0 #1 2023-10-25 16:04:35 ~1 min android 📄log
✖️ d209db0 #1 2023-10-25 16:05:24 ~2 min tests 📄log
a1489f9 #2 2023-10-25 16:37:47 ~43 sec ios 📄log
a1489f9 #2 2023-10-25 16:38:13 ~1 min linux 📄log
a1489f9 #2 2023-10-25 16:38:16 ~1 min android 📄log
✖️ a1489f9 #2 2023-10-25 16:39:34 ~2 min tests 📄log
bd9c4fb #3 2023-10-26 10:48:28 ~18 sec ios 📄log
✖️ bd9c4fb #3 2023-10-26 10:49:23 ~1 min tests 📄log
bd9c4fb #3 2023-10-26 10:49:33 ~1 min linux 📄log
bd9c4fb #3 2023-10-26 10:49:36 ~1 min android 📄log
88708f0 #4 2023-10-26 12:25:37 ~17 sec linux 📄log
88708f0 #4 2023-10-26 12:25:40 ~19 sec ios 📄log
✖️ 88708f0 #4 2023-10-26 12:26:21 ~1 min tests 📄log
88708f0 #4 2023-10-26 12:26:46 ~1 min android 📄log
df4c721 #5 2023-10-27 08:54:32 ~15 sec linux 📄log
df4c721 #5 2023-10-27 08:54:35 ~18 sec ios 📄log
✖️ df4c721 #5 2023-10-27 08:55:17 ~1 min tests 📄log
df4c721 #5 2023-10-27 08:55:30 ~1 min android 📄log
✔️ a6da7c6 #6 2023-10-27 09:06:14 ~2 min linux 📦zip
✖️ a6da7c6 #6 2023-10-27 09:06:39 ~2 min tests 📄log
✔️ a6da7c6 #6 2023-10-27 09:08:05 ~4 min ios 📦zip
✔️ a6da7c6 #6 2023-10-27 09:08:24 ~4 min android 📦aar
✔️ 957acd7 #7 2023-10-27 09:18:15 ~1 min linux 📦zip
✖️ 957acd7 #7 2023-10-27 09:19:00 ~2 min tests 📄log
✔️ 957acd7 #7 2023-10-27 09:19:57 ~3 min ios 📦zip
✔️ 957acd7 #7 2023-10-27 09:21:17 ~4 min android 📦aar
✔️ e27e6f3 #8 2023-10-27 09:24:40 ~2 min android 📦aar
✔️ e27e6f3 #8 2023-10-27 09:24:45 ~2 min linux 📦zip
✔️ e27e6f3 #8 2023-10-27 09:25:41 ~3 min ios 📦zip
✖️ e27e6f3 #8 2023-10-27 09:27:45 ~5 min tests 📄log
✔️ e27e6f3 #9 2023-10-27 10:40:53 ~32 min tests 📄log
✔️ f1dc7be #9 2023-10-30 11:00:29 ~1 min linux 📦zip
✔️ f1dc7be #9 2023-10-30 11:01:07 ~2 min android 📦aar
✔️ f1dc7be #9 2023-10-30 11:05:34 ~6 min ios 📦zip
✔️ f1dc7be #10 2023-10-30 11:31:10 ~32 min tests 📄log
✖️ f1f58c9 #11 2023-10-30 15:41:17 ~6 min tests 📄log
✔️ f1f58c9 #10 2023-10-30 15:42:40 ~8 min linux 📦zip
✔️ f1f58c9 #10 2023-10-30 15:44:28 ~10 min android 📦aar
✔️ f1f58c9 #10 2023-10-30 15:58:53 ~24 min ios 📦zip
✖️ 3f232ee #12 2023-10-31 13:42:32 ~1 min tests 📄log
✔️ 3f232ee #11 2023-10-31 13:43:01 ~1 min linux 📦zip
✔️ 3f232ee #11 2023-10-31 13:43:41 ~2 min android 📦aar
✖️ e5febab #13 2023-10-31 18:41:41 ~49 sec tests 📄log
✔️ e5febab #12 2023-10-31 18:42:33 ~1 min linux 📦zip
✔️ e5febab #12 2023-10-31 18:44:59 ~4 min android 📦aar
✖️ 48565fc #14 2023-11-01 08:44:32 ~42 sec tests 📄log
✔️ 48565fc #13 2023-11-01 08:45:31 ~1 min linux 📦zip
✔️ 48565fc #13 2023-11-01 08:46:11 ~2 min android 📦aar
✔️ 48565fc #14 2023-11-01 09:26:36 ~3 min ios 📦zip
✔️ 4eebe9a #14 2023-11-01 13:00:53 ~1 min android 📦aar
✖️ 4eebe9a #15 2023-11-01 13:01:22 ~2 min tests 📄log
✔️ 4eebe9a #14 2023-11-01 13:02:40 ~3 min linux 📦zip
✔️ 4eebe9a #15 2023-11-01 13:02:46 ~3 min ios 📦zip
✖️ 4eebe9a #16 2023-11-01 13:21:14 ~1 min tests 📄log
✔️ 5b2ab5b #15 2023-11-01 13:26:03 ~1 min linux 📦zip
✔️ 5b2ab5b #15 2023-11-01 13:26:41 ~2 min android 📦aar
✖️ 5b2ab5b #17 2023-11-01 13:28:11 ~3 min tests 📄log
✔️ 5b2ab5b #16 2023-11-01 13:31:33 ~6 min ios 📦zip
✖️ 5b2ab5b #18 2023-11-01 13:33:48 ~2 min tests 📄log
✖️ 5b2ab5b #19 2023-11-01 13:40:05 ~3 min tests 📄log
✖️ 5b2ab5b #20 2023-11-01 13:50:30 ~2 min tests 📄log
✖️ 5b2ab5b #21 2023-11-01 16:07:56 ~3 min tests 📄log
✖️ 5b2ab5b #22 2023-11-01 17:04:26 ~3 min tests 📄log
✔️ f7adfc3 #16 2023-11-03 09:19:53 ~1 min linux 📦zip
✔️ f7adfc3 #16 2023-11-03 09:20:25 ~2 min android 📦aar
✔️ f7adfc3 #17 2023-11-03 09:22:53 ~4 min ios 📦zip
✖️ f7adfc3 #23 2023-11-03 09:28:03 ~9 min tests 📄log
✖️ f7adfc3 #24 2023-11-03 13:21:21 ~35 min tests 📄log
✔️ 5c9c07f #18 2023-11-03 14:05:21 ~4 min ios 📦zip
✔️ 5c9c07f #17 2023-11-03 14:06:41 ~5 min linux 📦zip
✔️ 5c9c07f #17 2023-11-03 14:07:14 ~6 min android 📦aar
✖️ 5c9c07f #25 2023-11-03 14:20:03 ~19 min tests 📄log
✔️ d529279 #18 2023-11-03 15:24:12 ~1 min linux 📦zip
✔️ d529279 #18 2023-11-03 15:24:44 ~2 min android 📦aar
✔️ d529279 #19 2023-11-03 15:25:29 ~3 min ios 📦zip
✖️ d529279 #26 2023-11-03 15:32:26 ~9 min tests 📄log
✔️ cd8573f #19 2023-11-03 23:44:45 ~2 min android 📦aar
✔️ cd8573f #20 2023-11-03 23:45:39 ~3 min ios 📦zip
✔️ cd8573f #19 2023-11-03 23:45:43 ~3 min linux 📦zip
✔️ cd8573f #27 2023-11-04 00:14:20 ~31 min tests 📄log
✔️ cb0cfcf #20 2023-11-06 14:58:43 ~1 min linux 📦zip
✔️ cb0cfcf #20 2023-11-06 14:59:18 ~2 min android 📦aar
✔️ cb0cfcf #21 2023-11-06 15:00:31 ~3 min ios 📦zip
✖️ cb0cfcf #28 2023-11-06 15:25:27 ~28 min tests 📄log
✔️ 6c985f4 #21 2023-11-06 15:02:01 ~1 min linux 📦zip
✔️ 6c985f4 #22 2023-11-06 15:03:35 ~2 min ios 📦zip
✔️ 6c985f4 #21 2023-11-06 15:04:46 ~4 min android 📦aar
✔️ 6c985f4 #29 2023-11-06 15:57:17 ~31 min tests 📄log
✔️ 8b359bd #22 2023-11-07 16:42:44 ~2 min android 📦aar
✔️ 8b359bd #23 2023-11-07 16:43:32 ~2 min ios 📦zip
✔️ 8b359bd #22 2023-11-07 16:43:47 ~3 min linux 📦zip
✖️ 8b359bd #30 2023-11-07 16:49:51 ~9 min tests 📄log
✔️ d7f3d9d #23 2023-11-07 19:17:18 ~1 min linux 📦zip
✔️ d7f3d9d #23 2023-11-07 19:17:59 ~2 min android 📦aar
✔️ d7f3d9d #24 2023-11-07 19:18:28 ~2 min ios 📦zip
✖️ d7f3d9d #31 2023-11-07 19:38:54 ~23 min tests 📄log
✖️ 0004ccb #32 2023-11-08 21:00:08 ~1 min tests 📄log
✔️ 0004ccb #24 2023-11-08 21:00:27 ~1 min linux 📦zip
✔️ 0004ccb #25 2023-11-08 21:01:37 ~2 min ios 📦zip
✔️ 0004ccb #24 2023-11-08 21:03:03 ~4 min android 📦aar
✖️ 0004ccb #33 2023-11-09 10:23:18 ~1 min tests 📄log
✔️ c9df522 #25 2023-11-09 10:41:02 ~1 min linux 📦zip
✔️ c9df522 #26 2023-11-09 10:42:39 ~3 min ios 📦zip
✔️ c9df522 #25 2023-11-09 10:43:43 ~4 min android 📦aar
✔️ c9df522 #34 2023-11-09 11:12:59 ~33 min tests 📄log
✔️ 1f6681a #27 2023-11-09 11:47:55 ~2 min ios 📦zip
✔️ 1f6681a #26 2023-11-09 11:48:09 ~3 min android 📦aar
✔️ 1f6681a #26 2023-11-09 11:48:11 ~3 min linux 📦zip
✔️ 1f6681a #35 2023-11-09 12:18:03 ~32 min tests 📄log
✔️ 8837ff3 #27 2023-11-09 17:01:02 ~1 min linux 📦zip
✔️ 8837ff3 #27 2023-11-09 17:02:11 ~2 min android 📦aar
✔️ 8837ff3 #28 2023-11-09 17:02:17 ~2 min ios 📦zip
✔️ 8837ff3 #36 2023-11-09 17:31:21 ~31 min tests 📄log
Commit #️⃣ Finished (UTC) Duration Platform Result
ca09eb0 #28 2023-11-09 17:45:27 ~17 sec linux 📄log
ca09eb0 #29 2023-11-09 17:45:31 ~19 sec ios 📄log
ca09eb0 #28 2023-11-09 17:45:32 ~22 sec android 📄log
✖️ ca09eb0 #37 2023-11-09 17:46:09 ~59 sec tests 📄log
✔️ b40e3af #29 2023-11-09 18:28:16 ~1 min android 📦aar
✔️ b40e3af #29 2023-11-09 18:29:09 ~2 min linux 📦zip
✔️ b40e3af #30 2023-11-09 18:29:13 ~2 min ios 📦zip
✔️ b40e3af #38 2023-11-09 18:58:31 ~32 min tests 📄log

@MishkaRogachev MishkaRogachev force-pushed the feat/profile-showcase-messaging branch 3 times, most recently from df4c721 to a6da7c6 Compare October 27, 2023 09:03
@MishkaRogachev MishkaRogachev marked this pull request as ready for review October 27, 2023 09:04
@MishkaRogachev
Copy link
Contributor Author

FYI: this PR contains temporary commit with protobuf binaries, see #4191

@MishkaRogachev
Copy link
Contributor Author

@saledjenic is it better to add synchronisation and backup for showcase in another PR?

m.allContacts.Range(func(_ string, contact *Contact) (shouldContinue bool) {
if contact.mutual() {
mutualContacts = append(mutualContacts, contact)
if contact.IsVerified() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MishkaRogachev isn't a verified contact at the same time the mutual contact as well? I guess we should be mutually exclusive here.

			if contact.IsVerified() {
							iDVerifiedContacts = append(iDVerifiedContacts, contact)
			} else if contact.mutual() {
							mutualContacts = append(mutualContacts, contact)
			}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the identity verification correctly, verifification avaliable only for mutual contacts, so here i explicitly ask for being mutual contacts for identity verified contacts:

Visibility For Everyone For Contacts For ID Verified Contacts
Everyone true false false
Mutual contacts true true false
Id verified mutual contacts true true true

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but I see that as verified contacts are a subset of mutual contacts, right?

But what I'm pointing you here is that in the current code, the same verified contact, let's say X, will be added to both arrays, mutualContacts and iDVerifiedContacts. It means that contact X will receive a single message meant to be used for mutual and another message meant to be used for verified contacts, that further means contact X will have a profile showcase for a user that is carried by the last received message, regardless is it mutual to verified contact.

Is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

		if contact.IsVerified() {
			iDVerifiedContacts = append(iDVerifiedContacts, contact)
		} else if contact.mutual() {
			mutualContacts = append(mutualContacts, contact)
		}

With this implementation, a ID verified contact will be able to decrypt forIDVerifiedContacts part but will not be able to decrypt forContacts part because it won't contain his key. The test will fail here. So i will need to encrypt forContacts message both with iDVerifiedContacts and mutualContacts keys.

Each 'protobuf.ProfileShowcaseEntries' message contains only those items that are visible to the corresponding category. They are dispatched together in one protobuf.ProfileShowcase message, so end user will always use latest message, regardless is it mutual, verified or neither of them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saledjenic I guess it's also about the contained data.
A verified contact should process all of the 3 categories to get full showcase. forIDVerifiedContacts only contains part of the showcase that's available to verified users.

}, nil
}

func (m *Messenger) BuildProfileShowcaseFromIdentity(senderPubKey *ecdsa.PublicKey, message *protobuf.ProfileShowcase) (*identity.ProfileShowcase, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MishkaRogachev do we protect somehow that messages meant to be delivered to verified contacts can not be revealed by mutual contacts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, each group encrypted with with explicitly specified contact's keys, same as in identity image flow

package protobuf;

message ProfileShowcaseEntry {
string entry_id = 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MishkaRogachev why don't we start from 1 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's typo, fixed :)

@saledjenic
Copy link
Contributor

@saledjenic is it better to add synchronisation and backup for showcase in another PR?

Whichever you prefer more.

Copy link
Collaborator

@igor-sirotin igor-sirotin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice job! 💪

result := []*protobuf.ProfileShowcaseEntry{}

for _, entry := range entries {
if entry.ShowcaseVisibility == visibility {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe fast return here?

if entry.ShowcaseVisibility != visibility {
    continue
}
// ...

m.allContacts.Range(func(_ string, contact *Contact) (shouldContinue bool) {
if contact.mutual() {
mutualContacts = append(mutualContacts, contact)
if contact.IsVerified() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saledjenic I guess it's also about the contained data.
A verified contact should process all of the 3 categories to get full showcase. forIDVerifiedContacts only contains part of the showcase that's available to verified users.

_, err = WaitOnMessengerResponse(
theirMessenger,
func(r *MessengerResponse) bool {
return len(r.Contacts) > 0 && len(r.Messages()) > 0 && len(r.ActivityCenterNotifications()) > 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ActivityCenterNotifications shouldn't be part of wait condition, because it's created on the fly. We should check it afterwards, yes, but no during wait.

}

func (s *MessengerProfileShowcaseSuite) TestSetAndGetProfileShowcasePreferences() {
func (s *TestMessengerProfileShowcase) prepareShocasePreferencs() ProfileShowcasePreferences {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 typos combo! 😄

Suggested change
func (s *TestMessengerProfileShowcase) prepareShocasePreferencs() ProfileShowcasePreferences {
func (s *TestMessengerProfileShowcase) prepareShowcasePreferences() ProfileShowcasePreferences {

@@ -0,0 +1,9 @@
DROP TABLE profile_showcase_contacts;

CREATE TABLE IF NOT EXISTS profile_showcase_contacts (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess IF NOT EXISTS is not needed if we drop the table just one line earlier 😄

@igor-sirotin
Copy link
Collaborator

@MishkaRogachev this is not related to storing the profile of ourselves, right?
If it is, then, please, make sure that messenger.selfContact is properly updated 🙏

@MishkaRogachev
Copy link
Contributor Author

@saledjenic @igor-sirotin, thanks fort review!

If it is, then, please, make sure that messenger.selfContact is properly updated 🙏

No, as far as we don't use showcase data for user urls :)

@@ -77,7 +77,7 @@ image:
// Decrypt the main encryption AES key with AES encryption using the DH key
dAESKey, err := common.Decrypt(empk, sharedKey)
if err != nil {
if err.Error() == "cipher: message authentication failed" {
if err.Error() == "cipher: message authentication failed" { // nolint: goconst
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably const this instead of disabling lint :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed :)

@@ -727,6 +727,12 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
}
contact.SocialLinks = append(contact.SocialLinks, link)
}

profileShowcase, err := db.GetProfileShowcaseForContact(contact.ID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably should be a join for performance reasons? (same with social links) at least we could do
(1 for contacts, one for social links, one for profile showcase), instead of now that we do2*nContacts + 1 query, I think that's going to be slow maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is, both for social links and profile entries, there are several rows for one contact id, so LEFT JOIN will trigger a whole query to have several rows. But challenge to optimise this func is interesting. Can I try to do it in separated PR ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, that's no problem

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MishkaRogachev MishkaRogachev force-pushed the feat/profile-showcase-messaging branch 4 times, most recently from 4eebe9a to 5b2ab5b Compare November 1, 2023 13:24
@MishkaRogachev MishkaRogachev force-pushed the feat/profile-showcase-messaging branch 2 times, most recently from f7adfc3 to 5c9c07f Compare November 3, 2023 14:00
@MishkaRogachev
Copy link
Contributor Author

MishkaRogachev commented Nov 3, 2023

@saledjenic @igor-sirotin @cammellos

i've re-requested review because of add ing the meta field. I'm going to use it to share some extra metadata for ui proposal

I've decided to separate profile entries by category to be able provide custom fields

uint32 order = 5;
}

message ProfileShowcaseCollectible {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dlipicar can you please check if there is a need to add any additional fields to assets and collectibles?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what the "uid" is. To identify any collectible you need three separate components:

  • chainID
  • contract address
  • tokenID

@MishkaRogachev MishkaRogachev force-pushed the feat/profile-showcase-messaging branch 3 times, most recently from 6c985f4 to 8b359bd Compare November 7, 2023 16:40
Copy link
Contributor

@cammellos cammellos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks ok, main concern is performance of having to query multiple tables in separate queries, did you check though queries are covered by indexes? you are querying by contact-id most of the time, but primary key is composite, so depending on how the index is created (community first, contact last I guess), it could be slow if the community is popular, or viceversa if the user has many communities for example.

Also worth considering having a single table for your own? though no strong opinion on my side

protocol/identity_images.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@igor-sirotin igor-sirotin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job! LGTM 👍

return m.persistence.GetProfileShowcasePreferences()
}

func (m *Messenger) EncryptProfileShowcaseEntriesWithContactPubKeys(entries *protobuf.ProfileShowcaseEntries, contacts []*Contact) (*protobuf.ProfileShowcaseEntriesEncrypted, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of contacts might be quite big actually, maybe pass it by pointer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a slice, so it's already a pointer

protocol/persistence_profile_showcase.go Outdated Show resolved Hide resolved
@MishkaRogachev MishkaRogachev force-pushed the feat/profile-showcase-messaging branch 2 times, most recently from c9df522 to 1f6681a Compare November 9, 2023 11:44
@MishkaRogachev MishkaRogachev merged commit 03c32f6 into develop Nov 9, 2023
6 of 7 checks passed
@MishkaRogachev MishkaRogachev deleted the feat/profile-showcase-messaging branch November 9, 2023 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Profile showcase data sharing
6 participants