New way of sharing communities (#4341)

This commit is contained in:
Ibrahem Khalil 2023-12-15 13:55:32 +02:00 committed by GitHub
parent 7af313cd53
commit 90c31afe7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 169 additions and 178 deletions

View File

@ -1 +1 @@
0.171.33
0.171.34

View File

@ -5102,3 +5102,53 @@ func (m *Manager) GetCuratedCommunities() (*CuratedCommunities, error) {
func (m *Manager) SetCuratedCommunities(communities *CuratedCommunities) error {
return m.persistence.SetCuratedCommunities(communities)
}
func ToLinkPreveiwThumbnail(image images.IdentityImage) (*common.LinkPreviewThumbnail, error) {
thumbnail := &common.LinkPreviewThumbnail{}
if image.IsEmpty() {
return nil, nil
}
width, height, err := images.GetImageDimensions(image.Payload)
if err != nil {
return nil, fmt.Errorf("failed to get image dimensions: %w", err)
}
dataURI, err := image.GetDataURI()
if err != nil {
return nil, fmt.Errorf("failed to get data uri: %w", err)
}
thumbnail.Width = width
thumbnail.Height = height
thumbnail.DataURI = dataURI
return thumbnail, nil
}
func (c *Community) ToStatusLinkPreview() (*common.StatusCommunityLinkPreview, error) {
communityLinkPreview := &common.StatusCommunityLinkPreview{}
if image, ok := c.Images()[images.SmallDimName]; ok {
thumbnail, err := ToLinkPreveiwThumbnail(images.IdentityImage{Payload: image.Payload})
if err != nil {
c.config.Logger.Warn("unfurling status link: failed to set community thumbnail", zap.Error(err))
}
communityLinkPreview.Icon = *thumbnail
}
if image, ok := c.Images()[images.BannerIdentityName]; ok {
thumbnail, err := ToLinkPreveiwThumbnail(images.IdentityImage{Payload: image.Payload})
if err != nil {
c.config.Logger.Warn("unfurling status link: failed to set community thumbnail", zap.Error(err))
}
communityLinkPreview.Banner = *thumbnail
}
communityLinkPreview.CommunityID = c.IDString()
communityLinkPreview.DisplayName = c.Name()
communityLinkPreview.Description = c.DescriptionText()
communityLinkPreview.MembersCount = uint32(c.MembersCount())
communityLinkPreview.Color = c.Color()
return communityLinkPreview, nil
}

View File

@ -116,8 +116,8 @@ func setUpCommunityAndRoles(base CommunityEventsTestsInterface, role protobuf.Co
refreshMessengerResponses(base)
// add events sender and member to the community
advertiseCommunityTo(suite, community, base.GetControlNode(), base.GetEventSender())
advertiseCommunityTo(suite, community, base.GetControlNode(), base.GetMember())
advertiseCommunityToUserOldWay(suite, community, base.GetControlNode(), base.GetEventSender())
advertiseCommunityToUserOldWay(suite, community, base.GetControlNode(), base.GetMember())
request := &requests.RequestToJoinCommunity{
CommunityID: community.ID(),
@ -417,8 +417,8 @@ func setUpOnRequestCommunityAndRoles(base CommunityEventsTestsInterface, role pr
community := createTestCommunity(base, protobuf.CommunityPermissions_MANUAL_ACCEPT)
refreshMessengerResponses(base)
advertiseCommunityTo(s, community, base.GetControlNode(), base.GetEventSender())
advertiseCommunityTo(s, community, base.GetControlNode(), base.GetMember())
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), base.GetEventSender())
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), base.GetMember())
requestEventSender := &requests.RequestToJoinCommunity{
CommunityID: community.ID(),
@ -451,7 +451,7 @@ func setUpOnRequestCommunityAndRoles(base CommunityEventsTestsInterface, role pr
waitOnMessengerResponse(s, checkPermissionGranted, base.GetMember())
for _, eventSender := range additionalEventSenders {
advertiseCommunityTo(s, community, base.GetControlNode(), eventSender)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), eventSender)
joinOnRequestCommunity(s, community, base.GetControlNode(), eventSender, requestEventSender)
grantPermission(s, community, base.GetControlNode(), eventSender, role)
@ -1019,6 +1019,34 @@ func testCreateEditDeleteBecomeMemberPermission(base CommunityEventsTestsInterfa
deleteTokenPermission(base, community, deleteTokenPermissionRequest)
}
// To be removed in https://github.com/status-im/status-go/issues/4437
func advertiseCommunityToUserOldWay(s *suite.Suite, community *communities.Community, owner *Messenger, user *Messenger) {
chat := CreateOneToOneChat(common.PubkeyToHex(&user.identity.PublicKey), &user.identity.PublicKey, user.transport)
inputMessage := common.NewMessage()
inputMessage.ChatId = chat.ID
inputMessage.Text = "some text"
inputMessage.CommunityID = community.IDString()
err := owner.SaveChat(chat)
s.Require().NoError(err)
_, err = owner.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
// Ensure community is received
response, err := WaitOnMessengerResponse(
user,
func(r *MessengerResponse) bool {
return len(r.Communities()) > 0
},
"event sender did not receive community request to join",
)
s.Require().NoError(err)
communityInResponse := response.Communities()[0]
s.Require().Equal(community.ID(), communityInResponse.ID())
}
func testAcceptMemberRequestToJoin(base CommunityEventsTestsInterface, community *communities.Community, user *Messenger) {
// set up additional user that will send request to join
_, err := user.Start()
@ -1028,7 +1056,7 @@ func testAcceptMemberRequestToJoin(base CommunityEventsTestsInterface, community
s.Require().NoError(err)
defer TearDownMessenger(s, user)
advertiseCommunityTo(s, community, base.GetControlNode(), user)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), user)
// user sends request to join
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID(), ENSName: "testName"}
@ -1158,7 +1186,7 @@ func testAcceptMemberRequestToJoinResponseSharedWithOtherEventSenders(base Commu
s.Require().NoError(err)
defer TearDownMessenger(s, user)
advertiseCommunityTo(s, community, base.GetControlNode(), user)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), user)
// user sends request to join
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
@ -1236,7 +1264,7 @@ func testRejectMemberRequestToJoinResponseSharedWithOtherEventSenders(base Commu
s.Require().NoError(err)
defer TearDownMessenger(s, user)
advertiseCommunityTo(s, community, base.GetControlNode(), user)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), user)
// user sends request to join
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
@ -1313,7 +1341,7 @@ func testRejectMemberRequestToJoin(base CommunityEventsTestsInterface, community
s.Require().NoError(err)
defer TearDownMessenger(s, user)
advertiseCommunityTo(s, community, base.GetControlNode(), user)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), user)
// user sends request to join
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
@ -1411,7 +1439,7 @@ func testControlNodeHandlesMultipleEventSenderRequestToJoinDecisions(base Commun
s.Require().NoError(err)
defer TearDownMessenger(s, user)
advertiseCommunityTo(s, community, base.GetControlNode(), user)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), user)
// user sends request to join
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
@ -2029,8 +2057,8 @@ func testJoinedPrivilegedMemberReceiveRequestsToJoin(base CommunityEventsTestsIn
s := base.GetSuite()
advertiseCommunityTo(s, community, base.GetControlNode(), bob)
advertiseCommunityTo(s, community, base.GetControlNode(), newPrivilegedUser)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), bob)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), newPrivilegedUser)
requestNewPrivilegedUser := &requests.RequestToJoinCommunity{
CommunityID: community.ID(),
@ -2095,9 +2123,9 @@ func testMemberReceiveRequestsToJoinAfterGettingNewRole(base CommunityEventsTest
// control node creates a community and chat
community := createTestCommunity(base, protobuf.CommunityPermissions_MANUAL_ACCEPT)
advertiseCommunityTo(s, community, base.GetControlNode(), base.GetEventSender())
advertiseCommunityTo(s, community, base.GetControlNode(), base.GetMember())
advertiseCommunityTo(s, community, base.GetControlNode(), bob)
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), base.GetEventSender())
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), base.GetMember())
advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), bob)
requestAlice := &requests.RequestToJoinCommunity{
CommunityID: community.ID(),

View File

@ -29,7 +29,6 @@ import (
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/communitytokens"
walletToken "github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/t/helpers"
@ -320,29 +319,19 @@ func createCommunityConfigurable(s *suite.Suite, owner *Messenger, permission pr
}
func advertiseCommunityTo(s *suite.Suite, community *communities.Community, owner *Messenger, user *Messenger) {
chat := CreateOneToOneChat(common.PubkeyToHex(&user.identity.PublicKey), &user.identity.PublicKey, user.transport)
inputMessage := common.NewMessage()
inputMessage.ChatId = chat.ID
inputMessage.Text = "some text"
inputMessage.CommunityID = community.IDString()
err := owner.SaveChat(chat)
s.Require().NoError(err)
_, err = owner.SendChatMessage(context.Background(), inputMessage)
// Create wrapped (Signed) community data.
wrappedCommunity, err := community.ToProtocolMessageBytes()
s.Require().NoError(err)
// Ensure community is received
err = tt.RetryWithBackOff(func() error {
response, err := user.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("community not received")
}
return nil
})
// Unwrap signer (Admin) data at user side.
signer, description, err := communities.UnwrapCommunityDescriptionMessage(wrappedCommunity)
s.Require().NoError(err)
// Handle community data state at receiver side
messageState := user.buildMessageState()
messageState.CurrentMessageState = &CurrentMessageState{}
messageState.CurrentMessageState.PublicKey = &user.identity.PublicKey
err = user.handleCommunityDescription(messageState, signer, description, wrappedCommunity, nil)
s.Require().NoError(err)
}

View File

@ -22,6 +22,7 @@ import (
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/images"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
@ -496,8 +497,8 @@ func (s *MessengerCommunitiesSuite) TestCommunityContactCodeAdvertisement() {
// create community and make bob and alice join to it
community, _ := s.createCommunity()
s.advertiseCommunityTo(community, s.owner, s.bob)
s.advertiseCommunityTo(community, s.owner, s.alice)
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.bob)
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
s.joinCommunity(community, s.owner, s.bob)
s.joinCommunity(community, s.owner, s.alice)
@ -2106,8 +2107,8 @@ func (s *MessengerCommunitiesSuite) TestDeclineAccess() {
func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
community, _ := s.createCommunity()
s.advertiseCommunityTo(community, s.owner, s.alice)
s.advertiseCommunityTo(community, s.owner, s.bob)
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.bob)
s.joinCommunity(community, s.owner, s.alice)
s.joinCommunity(community, s.owner, s.bob)
@ -2192,57 +2193,54 @@ func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
func (s *MessengerCommunitiesSuite) TestShareCommunity() {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT,
Name: "status",
Color: "#ffffff",
Description: "status community description",
Color: "#FFFFFF",
Image: "../_assets/tests/status.png",
ImageAx: 0,
ImageAy: 0,
ImageBx: 256,
ImageBy: 256,
Banner: images.CroppedImage{
ImagePath: "../_assets/tests/IMG_1205.HEIC.jpg",
X: 0,
Y: 0,
Width: 160,
Height: 90,
},
}
inviteMessage := "invite to community testing message"
response, err := s.owner.CreateCommunity(description, true)
// Create an community
response, err := s.bob.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Chats(), 1)
community := response.Communities()[0]
response, err = s.bob.ShareCommunity(
&requests.ShareCommunity{
CommunityID: community.ID(),
Users: []types.HexBytes{common.PubkeyToHexBytes(&s.alice.identity.PublicKey)},
InviteMessage: inviteMessage,
},
)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Messages(), 1)
// Add bob to contacts so it does not go on activity center
bobPk := common.PubkeyToHex(&s.bob.identity.PublicKey)
request := &requests.AddContact{ID: bobPk}
_, err = s.alice.AddContact(context.Background(), request)
s.Require().NoError(err)
// Pull message and make sure org is received
err = tt.RetryWithBackOff(func() error {
response, err = s.alice.RetrieveAll()
if err != nil {
return err
}
if len(response.messages) == 0 {
return errors.New("community link not received")
}
return nil
inputMessageText := "Come on alice, You'll like it here!"
// Alice shares community with Bob
response, err = s.owner.ShareCommunity(&requests.ShareCommunity{
CommunityID: community.ID(),
Users: []types.HexBytes{common.PubkeyToHexBytes(&s.alice.identity.PublicKey)},
InviteMessage: inputMessageText,
})
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Messages(), 1)
sentMessageText := response.Messages()[0].Text
message := response.Messages()[0]
s.Require().Equal(community.IDString(), message.CommunityID)
s.Require().Equal(inviteMessage, message.Text)
_, err = WaitOnMessengerResponse(s.alice, func(r *MessengerResponse) bool {
return len(r.Messages()) > 0
}, "Messages not received")
communityURL := response.Messages()[0].UnfurledStatusLinks.GetUnfurledStatusLinks()[0].Url
s.Require().NoError(err)
s.Require().Len(response.Messages(), 1)
s.Require().Equal(fmt.Sprintf("%s\n%s", inputMessageText, communityURL), sentMessageText)
s.Require().NotNil(response.Messages()[0].UnfurledStatusLinks.GetUnfurledStatusLinks()[0].GetCommunity().CommunityId)
}
func (s *MessengerCommunitiesSuite) TestShareCommunityWithPreviousMember() {
@ -2253,8 +2251,6 @@ func (s *MessengerCommunitiesSuite) TestShareCommunityWithPreviousMember() {
Description: "status community description",
}
inviteMessage := "invite to community testing message"
// Create an community chat
response, err := s.bob.CreateCommunity(description, true)
s.Require().NoError(err)
@ -2289,16 +2285,7 @@ func (s *MessengerCommunitiesSuite) TestShareCommunityWithPreviousMember() {
err = s.bob.communitiesManager.SaveCommunity(community)
s.Require().NoError(err)
response, err = s.bob.ShareCommunity(
&requests.ShareCommunity{
CommunityID: community.ID(),
Users: []types.HexBytes{common.PubkeyToHexBytes(&s.alice.identity.PublicKey)},
InviteMessage: inviteMessage,
},
)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Messages(), 1)
advertiseCommunityToUserOldWay(&s.Suite, community, s.bob, s.alice)
// Add bob to contacts so it does not go on activity center
bobPk := common.PubkeyToHex(&s.bob.identity.PublicKey)
@ -2306,25 +2293,6 @@ func (s *MessengerCommunitiesSuite) TestShareCommunityWithPreviousMember() {
_, err = s.alice.AddContact(context.Background(), request)
s.Require().NoError(err)
// Pull message and make sure org is received
err = tt.RetryWithBackOff(func() error {
response, err = s.alice.RetrieveAll()
if err != nil {
return err
}
if len(response.messages) == 0 {
return errors.New("community link not received")
}
return nil
})
s.Require().NoError(err)
s.Require().Len(response.Messages(), 1)
message := response.Messages()[0]
s.Require().Equal(community.IDString(), message.CommunityID)
s.Require().Equal(inviteMessage, message.Text)
// Alice should have the Joined status for the community
communityInResponse := response.Communities()[0]
s.Require().Equal(community.ID(), communityInResponse.ID())

View File

@ -79,22 +79,6 @@ func (u *StatusUnfurler) buildContactData(publicKey string) (*common.StatusConta
return c, nil
}
func (u *StatusUnfurler) fillCommunityImages(community *communities.Community, icon *common.LinkPreviewThumbnail, banner *common.LinkPreviewThumbnail) error {
if image, ok := community.Images()[images.SmallDimName]; ok {
if err := updateThumbnail(&images.IdentityImage{Payload: image.Payload}, icon); err != nil {
u.logger.Warn("unfurling status link: failed to set community thumbnail", zap.Error(err))
}
}
if image, ok := community.Images()[images.BannerIdentityName]; ok {
if err := updateThumbnail(&images.IdentityImage{Payload: image.Payload}, banner); err != nil {
u.logger.Warn("unfurling status link: failed to set community banner", zap.Error(err))
}
}
return nil
}
func (u *StatusUnfurler) buildCommunityData(communityID string, shard *shard.Shard) (*communities.Community, *common.StatusCommunityLinkPreview, error) {
// This automatically checks the database
community, err := u.m.FetchCommunity(&FetchCommunityRequest{
@ -112,20 +96,12 @@ func (u *StatusUnfurler) buildCommunityData(communityID string, shard *shard.Sha
return community, nil, fmt.Errorf("community info fetched, but it is empty")
}
c := &common.StatusCommunityLinkPreview{
CommunityID: community.IDString(),
DisplayName: community.Name(),
Description: community.DescriptionText(),
MembersCount: uint32(community.MembersCount()),
Color: community.Color(),
}
err = u.fillCommunityImages(community, &c.Icon, &c.Banner)
statusCommunityLinkPreviews, err := community.ToStatusLinkPreview()
if err != nil {
return community, c, err
return nil, nil, fmt.Errorf("failed to get status community link preview for communityID '%s': %w", communityID, err)
}
return community, c, nil
return community, statusCommunityLinkPreviews, nil
}
func (u *StatusUnfurler) buildChannelData(channelUUID string, communityID string, communityShard *shard.Shard) (*common.StatusCommunityChannelLinkPreview, error) {

View File

@ -2311,22 +2311,35 @@ func (m *Messenger) ShareCommunity(request *requests.ShareCommunity) (*Messenger
if err := request.Validate(); err != nil {
return nil, err
}
response := &MessengerResponse{}
community, err := m.communitiesManager.GetByID(request.CommunityID)
community, err := m.GetCommunityByID(request.CommunityID)
if err != nil {
return nil, err
}
response := &MessengerResponse{}
communityURL, err := m.ShareCommunityURLWithData(request.CommunityID)
if err != nil {
return nil, err
}
var statusLinkPreview common.StatusLinkPreview
statusCommunityLinkPreview, err := community.ToStatusLinkPreview()
if err != nil {
return nil, err
}
statusLinkPreview.URL = communityURL
statusLinkPreview.Community = statusCommunityLinkPreview
var messages []*common.Message
for _, pk := range request.Users {
message := common.NewMessage()
message.StatusLinkPreviews = []common.StatusLinkPreview{statusLinkPreview}
message.ChatId = pk.String()
message.CommunityID = request.CommunityID.String()
message.Shard = community.Shard().Protobuffer()
message.Text = fmt.Sprintf("Community %s has been shared with you", community.Name())
message.ContentType = protobuf.ChatMessage_TEXT_PLAIN
message.Text = communityURL
if request.InviteMessage != "" {
message.Text = request.InviteMessage
message.Text = fmt.Sprintf("%s\n%s", request.InviteMessage, communityURL)
}
messages = append(messages, message)
r, err := m.CreateOneToOneChat(&requests.CreateOneToOneChat{ID: pk})

View File

@ -215,17 +215,9 @@ func (s *MessengerSyncActivityCenterSuite) addContactAndShareCommunity(userB *Me
return false
}, "contact request not accepted on device 2")
s.Require().NoError(err)
_, err = s.m.ShareCommunity(&requests.ShareCommunity{
CommunityID: communityID,
Users: []types.HexBytes{common.PubkeyToHexBytes(&userB.identity.PublicKey)},
InviteMessage: "invite to community testing message",
})
s.Require().NoError(err)
_, err = WaitOnMessengerResponse(userB, func(r *MessengerResponse) bool {
return len(r.Communities()) > 0
}, "community not received")
community, err := s.m.GetCommunityByID(communityID)
s.Require().NoError(err)
advertiseCommunityToUserOldWay(&s.Suite, community, s.m, userB)
}
func (s *MessengerSyncActivityCenterSuite) requestToJoinCommunity(userB *Messenger, communityID types.HexBytes) {

View File

@ -6,7 +6,6 @@ import (
_ "github.com/mutecomm/go-sqlcipher/v4" // require go-sqlcipher that overrides default implementation
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
@ -287,8 +286,6 @@ func (s *MessengerSuite) TestMarkMessageWithNotificationAsUnreadInCommunityChatS
Description: "This is just a test description for the community",
}
inviteMessage := "You are invited to community testing message"
response, err := other.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
@ -303,29 +300,7 @@ func (s *MessengerSuite) TestMarkMessageWithNotificationAsUnreadInCommunityChatS
err = other.communitiesManager.SaveCommunity(community)
s.Require().NoError(err)
response, err = other.ShareCommunity(
&requests.ShareCommunity{
CommunityID: community.ID(),
Users: []types.HexBytes{common.PubkeyToHexBytes(&s.m.identity.PublicKey)},
InviteMessage: inviteMessage,
},
)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Messages(), 1)
response, err = s.retrieveAllWithRetry("community link not received")
s.Require().NoError(err)
s.Require().Len(response.Messages(), 1)
message := response.Messages()[0]
s.Require().Equal(community.IDString(), message.CommunityID)
s.Require().Equal(inviteMessage, message.Text)
communityInResponse := response.Communities()[0]
s.Require().Equal(community.ID(), communityInResponse.ID())
s.Require().True(communityInResponse.Joined())
advertiseCommunityToUserOldWay(&s.Suite, community, other, s.m)
inputMessage1 := buildTestMessage(*communityChat)
inputMessage1.ChatId = communityChat.ID