Community request to join changes for pending and declined states (#3387)

* Community request to join changes

* Fix read state for request to join notification

* Bring back deleted notification when updated with response

* Update Request timeout to 7 days

* Update VERSION
This commit is contained in:
Mohamed Javid 2023-04-21 17:18:47 +08:00 committed by GitHub
parent 1fe4a898b6
commit 0eff61c57a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 666 additions and 6 deletions

View File

@ -1 +1 @@
0.146.3 0.146.4

View File

@ -1184,6 +1184,30 @@ func (m *Manager) markRequestToJoinAsCanceled(pk *ecdsa.PublicKey, community *Co
return m.persistence.SetRequestToJoinState(common.PubkeyToHex(pk), community.ID(), RequestToJoinStateCanceled) return m.persistence.SetRequestToJoinState(common.PubkeyToHex(pk), community.ID(), RequestToJoinStateCanceled)
} }
func (m *Manager) DeletePendingRequestToJoin(request *RequestToJoin) error {
community, err := m.GetByID(request.CommunityID)
if err != nil {
return err
}
err = m.persistence.DeletePendingRequestToJoin(request.ID)
if err != nil {
return err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return err
}
return nil
}
// UpdateClockInRequestToJoin method is used for testing
func (m *Manager) UpdateClockInRequestToJoin(id types.HexBytes, clock uint64) error {
return m.persistence.UpdateClockInRequestToJoin(id, clock)
}
func (m *Manager) SetMuted(id types.HexBytes, muted bool) error { func (m *Manager) SetMuted(id types.HexBytes, muted bool) error {
return m.persistence.SetMuted(id, muted) return m.persistence.SetMuted(id, muted)
} }
@ -1296,7 +1320,7 @@ func (m *Manager) DeclineRequestToJoin(request *requests.DeclineRequestToJoinCom
return m.persistence.SetRequestToJoinState(dbRequest.PublicKey, dbRequest.CommunityID, RequestToJoinStateDeclined) return m.persistence.SetRequestToJoinState(dbRequest.PublicKey, dbRequest.CommunityID, RequestToJoinStateDeclined)
} }
func (m *Manager) isUserRejectedFromCommunity(signer *ecdsa.PublicKey, community *Community) (bool, error) { func (m *Manager) isUserRejectedFromCommunity(signer *ecdsa.PublicKey, community *Community, requestClock uint64) (bool, error) {
declinedRequestsToJoin, err := m.persistence.DeclinedRequestsToJoinForCommunity(community.ID()) declinedRequestsToJoin, err := m.persistence.DeclinedRequestsToJoinForCommunity(community.ID())
if err != nil { if err != nil {
return false, err return false, err
@ -1304,7 +1328,14 @@ func (m *Manager) isUserRejectedFromCommunity(signer *ecdsa.PublicKey, community
for _, req := range declinedRequestsToJoin { for _, req := range declinedRequestsToJoin {
if req.PublicKey == common.PubkeyToHex(signer) { if req.PublicKey == common.PubkeyToHex(signer) {
return true, nil dbRequestTimeOutClock, err := AddTimeoutToRequestToJoinClock(req.Clock)
if err != nil {
return false, err
}
if requestClock < dbRequestTimeOutClock {
return true, nil
}
} }
} }
return false, nil return false, nil
@ -1319,7 +1350,7 @@ func (m *Manager) HandleCommunityCancelRequestToJoin(signer *ecdsa.PublicKey, re
return nil, ErrOrgNotFound return nil, ErrOrgNotFound
} }
isUserRejected, err := m.isUserRejectedFromCommunity(signer, community) isUserRejected, err := m.isUserRejectedFromCommunity(signer, community, request.Clock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1349,7 +1380,7 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, request
return nil, ErrOrgNotFound return nil, ErrOrgNotFound
} }
isUserRejected, err := m.isUserRejectedFromCommunity(signer, community) isUserRejected, err := m.isUserRejectedFromCommunity(signer, community, request.Clock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2121,6 +2152,10 @@ func (m *Manager) CanceledRequestsToJoinForUser(pk *ecdsa.PublicKey) ([]*Request
return m.persistence.CanceledRequestsToJoinForUser(common.PubkeyToHex(pk)) return m.persistence.CanceledRequestsToJoinForUser(common.PubkeyToHex(pk))
} }
func (m *Manager) PendingRequestsToJoin() ([]*RequestToJoin, error) {
return m.persistence.PendingRequestsToJoin()
}
func (m *Manager) PendingRequestsToJoinForUser(pk *ecdsa.PublicKey) ([]*RequestToJoin, error) { func (m *Manager) PendingRequestsToJoinForUser(pk *ecdsa.PublicKey) ([]*RequestToJoin, error) {
return m.persistence.PendingRequestsToJoinForUser(common.PubkeyToHex(pk)) return m.persistence.PendingRequestsToJoinForUser(common.PubkeyToHex(pk))
} }

View File

@ -470,6 +470,25 @@ func (p *Persistence) RequestsToJoinForCommunityWithState(id []byte, state Reque
return requests, nil return requests, nil
} }
func (p *Persistence) PendingRequestsToJoin() ([]*RequestToJoin, error) {
var requests []*RequestToJoin
rows, err := p.db.Query(`SELECT id,public_key,clock,ens_name,chat_id,community_id,state FROM communities_requests_to_join WHERE state = ?`, RequestToJoinStatePending)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
request := &RequestToJoin{}
err := rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.ChatID, &request.CommunityID, &request.State)
if err != nil {
return nil, err
}
requests = append(requests, request)
}
return requests, nil
}
func (p *Persistence) PendingRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) { func (p *Persistence) PendingRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStatePending) return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStatePending)
} }
@ -487,6 +506,17 @@ func (p *Persistence) SetRequestToJoinState(pk string, communityID []byte, state
return err return err
} }
func (p *Persistence) DeletePendingRequestToJoin(id []byte) error {
_, err := p.db.Exec(`DELETE FROM communities_requests_to_join WHERE id = ?`, id)
return err
}
// UpdateClockInRequestToJoin method is used for testing
func (p *Persistence) UpdateClockInRequestToJoin(id []byte, clock uint64) error {
_, err := p.db.Exec(`UPDATE communities_requests_to_join SET clock = ? WHERE id = ?`, clock, id)
return err
}
func (p *Persistence) SetMuted(communityID []byte, muted bool) error { func (p *Persistence) SetMuted(communityID []byte, muted bool) error {
_, err := p.db.Exec(`UPDATE communities_communities SET muted = ? WHERE id = ?`, muted, communityID) _, err := p.db.Exec(`UPDATE communities_communities SET muted = ? WHERE id = ?`, muted, communityID)
return err return err

View File

@ -1,6 +1,10 @@
package communities package communities
import ( import (
"fmt"
"strconv"
"time"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
) )
@ -24,6 +28,7 @@ type RequestToJoin struct {
State RequestToJoinState `json:"state"` State RequestToJoinState `json:"state"`
Our bool `json:"our"` Our bool `json:"our"`
RevealedAddresses map[string][]byte `json:"revealedAddresses,omitempty"` RevealedAddresses map[string][]byte `json:"revealedAddresses,omitempty"`
Deleted bool `json:"deleted"`
} }
func (r *RequestToJoin) CalculateID() { func (r *RequestToJoin) CalculateID() {
@ -57,3 +62,15 @@ func (r *RequestToJoin) InitFromSyncProtobuf(proto *protobuf.SyncCommunityReques
func (r *RequestToJoin) Empty() bool { func (r *RequestToJoin) Empty() bool {
return len(r.ID)+len(r.PublicKey)+int(r.Clock)+len(r.ENSName)+len(r.ChatID)+len(r.CommunityID)+int(r.State) == 0 return len(r.ID)+len(r.PublicKey)+int(r.Clock)+len(r.ENSName)+len(r.ChatID)+len(r.CommunityID)+int(r.State) == 0
} }
func AddTimeoutToRequestToJoinClock(clock uint64) (uint64, error) {
requestToJoinClock, err := strconv.ParseInt(fmt.Sprint(clock), 10, 64)
if err != nil {
return 0, err
}
// Adding 7 days to the request clock
requestTimeOutClock := uint64(time.Unix(requestToJoinClock, 0).AddDate(0, 0, 7).Unix())
return requestTimeOutClock, nil
}

View File

@ -1055,7 +1055,7 @@ func (s *MessengerCommunitiesSuite) TestRequestAccess() {
s.Require().NotNil(notification) s.Require().NotNil(notification)
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted) s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted)
s.Require().Equal(notification.Read, true) s.Require().Equal(notification.Read, false)
s.Require().Equal(notification.Accepted, false) s.Require().Equal(notification.Accepted, false)
s.Require().Equal(notification.Dismissed, false) s.Require().Equal(notification.Dismissed, false)
@ -1079,6 +1079,446 @@ func (s *MessengerCommunitiesSuite) TestRequestAccess() {
} }
func (s *MessengerCommunitiesSuite) TestDeletePendingRequestAccess() {
ctx := context.Background()
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_ON_REQUEST,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Bob creates a community
response, err := s.bob.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
community := response.Communities()[0]
chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
s.Require().NoError(s.bob.SaveChat(chat))
message := buildTestMessage(*chat)
message.CommunityID = community.IDString()
// Bob sends the community link to Alice
response, err = s.bob.SendChatMessage(ctx, message)
s.Require().NoError(err)
s.Require().NotNil(response)
// Retrieve community link & community for Alice
err = tt.RetryWithBackOff(func() error {
response, err = s.alice.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("message not received")
}
return nil
})
s.Require().NoError(err)
// Alice request to join community
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
response, err = s.alice.RequestToJoinCommunity(request)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
requestToJoin := response.RequestsToJoinCommunity[0]
s.Require().NotNil(requestToJoin)
s.Require().Equal(community.ID(), requestToJoin.CommunityID)
s.Require().NotEmpty(requestToJoin.ID)
s.Require().NotEmpty(requestToJoin.Clock)
s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State)
s.Require().Len(response.Communities(), 1)
s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin.Clock)
// updating request clock by 8 days back
requestTime := uint64(time.Now().AddDate(0, 0, -8).Unix())
err = s.alice.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
s.Require().NoError(err)
// pull to make sure it has been saved
requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 1)
requestToJoin = requestsToJoin[0]
s.Require().Equal(requestToJoin.Clock, requestTime)
// Make sure the requests are fetched also by community
requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 1)
// Retrieve request to join
bobRetrieveAll := func() (*MessengerResponse, error) {
return s.bob.RetrieveAll()
}
err = tt.RetryWithBackOff(func() error {
response, err = bobRetrieveAll()
if err != nil {
return err
}
if len(response.RequestsToJoinCommunity) == 0 {
return errors.New("request to join community not received")
}
// updating request clock by 8 days back
requestToJoin := response.RequestsToJoinCommunity[0]
err = s.bob.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
if err != nil {
return err
}
if len(response.ActivityCenterNotifications()) == 0 {
return errors.New("request to join community notification not added in activity center")
}
return nil
})
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
// Check activity center notification for Bob
fetchActivityCenterNotificationsForAdmin := func() (*ActivityCenterPaginationResponse, error) {
return s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
Cursor: "",
Limit: 10,
ActivityTypes: []ActivityCenterType{},
ReadType: ActivityCenterQueryParamsReadUnread,
})
}
notifications, err := fetchActivityCenterNotificationsForAdmin()
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
notification := notifications.Notifications[0]
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
// Delete pending request to join
response, err = s.alice.CheckAndDeletePendingRequestToJoinCommunity(true)
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
s.Require().Len(response.ActivityCenterNotifications(), 1)
requestToJoin = response.RequestsToJoinCommunity[0]
s.Require().True(requestToJoin.Deleted)
notification = response.ActivityCenterNotifications()[0]
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusIdle)
response, err = s.bob.CheckAndDeletePendingRequestToJoinCommunity(true)
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
s.Require().Len(response.ActivityCenterNotifications(), 1)
requestToJoin = response.RequestsToJoinCommunity[0]
s.Require().True(requestToJoin.Deleted)
notification = response.ActivityCenterNotifications()[0]
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
s.Require().True(notification.Deleted)
// Alice request to join community
request = &requests.RequestToJoinCommunity{CommunityID: community.ID()}
response, err = s.alice.RequestToJoinCommunity(request)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
// Retrieve request to join and Check activity center notification for Bob
err = tt.RetryWithBackOff(func() error {
response, err = bobRetrieveAll()
if err != nil {
return err
}
if len(response.RequestsToJoinCommunity) == 0 {
return errors.New("request to join community not received")
}
if len(response.ActivityCenterNotifications()) == 0 {
return errors.New("request to join community notification not added in activity center")
}
return nil
})
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
// Check activity center notification for Bob
notifications, err = fetchActivityCenterNotificationsForAdmin()
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
notification = notifications.Notifications[0]
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
}
func (s *MessengerCommunitiesSuite) TestDeletePendingRequestAccessWithDeclinedState() {
ctx := context.Background()
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_ON_REQUEST,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Bob creates a community
response, err := s.bob.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
community := response.Communities()[0]
chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
s.Require().NoError(s.bob.SaveChat(chat))
message := buildTestMessage(*chat)
message.CommunityID = community.IDString()
// Bob sends the community link to Alice
response, err = s.bob.SendChatMessage(ctx, message)
s.Require().NoError(err)
s.Require().NotNil(response)
// Retrieve community link & community for Alice
err = tt.RetryWithBackOff(func() error {
response, err = s.alice.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("message not received")
}
return nil
})
s.Require().NoError(err)
// Alice request to join community
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
response, err = s.alice.RequestToJoinCommunity(request)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
notification := response.ActivityCenterNotifications()[0]
s.Require().NotNil(notification)
s.Require().NotEmpty(notification.ID)
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
s.Require().Equal(notification.Deleted, false)
s.Require().Equal(notification.Read, true)
requestToJoin := response.RequestsToJoinCommunity[0]
s.Require().NotNil(requestToJoin)
s.Require().Equal(community.ID(), requestToJoin.CommunityID)
s.Require().NotEmpty(requestToJoin.ID)
s.Require().NotEmpty(requestToJoin.Clock)
s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State)
s.Require().Len(response.Communities(), 1)
s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin.Clock)
// Alice deletes activity center notification
err = s.alice.DeleteActivityCenterNotifications(ctx, []types.HexBytes{notification.ID}, false)
s.Require().NoError(err)
// Check activity center notification for Bob after deleting
notifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
Cursor: "",
Limit: 10,
ActivityTypes: []ActivityCenterType{},
ReadType: ActivityCenterQueryParamsReadUnread,
})
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 0)
// updating request clock by 8 days back
requestTime := uint64(time.Now().AddDate(0, 0, -8).Unix())
err = s.alice.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
s.Require().NoError(err)
// pull to make sure it has been saved
requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 1)
requestToJoin = requestsToJoin[0]
s.Require().Equal(requestToJoin.Clock, requestTime)
// Make sure the requests are fetched also by community
requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 1)
bobRetrieveAll := func() (*MessengerResponse, error) {
return s.bob.RetrieveAll()
}
// Retrieve request to join
err = tt.RetryWithBackOff(func() error {
response, err = bobRetrieveAll()
if err != nil {
return err
}
if len(response.RequestsToJoinCommunity) == 0 {
return errors.New("request to join community not received")
}
// updating request clock by 8 days back
requestToJoin := response.RequestsToJoinCommunity[0]
err = s.bob.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
if err != nil {
return err
}
if len(response.ActivityCenterNotifications()) == 0 {
return errors.New("request to join community notification not added in activity center")
}
return nil
})
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
// Check activity center notification for Bob
fetchActivityCenterNotificationsForAdmin := func() (*ActivityCenterPaginationResponse, error) {
return s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
Cursor: "",
Limit: 10,
ActivityTypes: []ActivityCenterType{},
ReadType: ActivityCenterQueryParamsReadUnread,
})
}
notifications, err = fetchActivityCenterNotificationsForAdmin()
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
notification = notifications.Notifications[0]
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
// Check if admin sees requests correctly
requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 1)
requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 0)
// Decline request
declinedRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: requestToJoin.ID}
response, err = s.bob.DeclineRequestToJoinCommunity(declinedRequestToJoin)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.ActivityCenterNotifications(), 1)
notification = response.ActivityCenterNotifications()[0]
s.Require().NotNil(notification)
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusDeclined)
s.Require().Equal(notification.Read, true)
s.Require().Equal(notification.Accepted, false)
s.Require().Equal(notification.Dismissed, true)
// Check if admin sees requests correctly
requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 0)
requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
s.Require().NoError(err)
s.Require().Len(requestsToJoin, 1)
// Bob deletes activity center notification
err = s.bob.DeleteActivityCenterNotifications(ctx, []types.HexBytes{notification.ID}, false)
s.Require().NoError(err)
// Check activity center notification for Bob after deleting
notifications, err = fetchActivityCenterNotificationsForAdmin()
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 0)
// Delete pending request to join
response, err = s.alice.CheckAndDeletePendingRequestToJoinCommunity(true)
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
requestToJoin = response.RequestsToJoinCommunity[0]
s.Require().True(requestToJoin.Deleted)
notification = response.ActivityCenterNotifications()[0]
s.Require().NotNil(notification)
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusIdle)
s.Require().Equal(notification.Read, false)
s.Require().Equal(notification.Deleted, false)
notificationState := response.ActivityCenterState()
s.Require().False(notificationState.HasSeen)
// Alice request to join community
request = &requests.RequestToJoinCommunity{CommunityID: community.ID()}
response, err = s.alice.RequestToJoinCommunity(request)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
// Retrieve request to join and Check activity center notification for Bob
err = tt.RetryWithBackOff(func() error {
response, err = bobRetrieveAll()
if err != nil {
return err
}
if len(response.RequestsToJoinCommunity) == 0 {
return errors.New("request to join community not received")
}
if len(response.ActivityCenterNotifications()) == 0 {
return errors.New("request to join community notification not added in activity center")
}
return nil
})
s.Require().NoError(err)
s.Require().Len(response.RequestsToJoinCommunity, 1)
// Check activity center notification for Bob
notifications, err = fetchActivityCenterNotificationsForAdmin()
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
notification = notifications.Notifications[0]
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
s.Require().False(notification.Deleted)
}
func (s *MessengerCommunitiesSuite) TestCancelRequestAccess() { func (s *MessengerCommunitiesSuite) TestCancelRequestAccess() {
ctx := context.Background() ctx := context.Background()

View File

@ -711,6 +711,7 @@ func (m *Messenger) Start() (*MessengerResponse, error) {
m.watchUnmutedChats() m.watchUnmutedChats()
m.watchExpiredMessages() m.watchExpiredMessages()
m.watchIdentityImageChanges() m.watchIdentityImageChanges()
m.watchPendingCommunityRequestToJoin()
m.broadcastLatestUserStatus() m.broadcastLatestUserStatus()
m.timeoutAutomaticStatusUpdates() m.timeoutAutomaticStatusUpdates()
m.startBackupLoop() m.startBackupLoop()
@ -1430,6 +1431,24 @@ func (m *Messenger) watchIdentityImageChanges() {
}() }()
} }
func (m *Messenger) watchPendingCommunityRequestToJoin() {
m.logger.Debug("watching community request to join")
go func() {
for {
select {
case <-time.After(time.Minute * 10):
_, err := m.CheckAndDeletePendingRequestToJoinCommunity(false)
if err != nil {
m.logger.Error("failed to check and delete pending request to join community", zap.Error(err))
}
case <-m.quit:
return
}
}
}()
}
func (m *Messenger) PublishIdentityImage() error { func (m *Messenger) PublishIdentityImage() error {
// Reset last published time for ChatIdentity so new contact can receive data // Reset last published time for ChatIdentity so new contact can receive data
err := m.resetLastPublishedTimeForChatIdentity() err := m.resetLastPublishedTimeForChatIdentity()

View File

@ -646,6 +646,7 @@ func (m *Messenger) RequestToJoinCommunity(request *requests.RequestToJoinCommun
CommunityID: community.IDString(), CommunityID: community.IDString(),
MembershipStatus: ActivityCenterMembershipStatusPending, MembershipStatus: ActivityCenterMembershipStatusPending,
Read: true, Read: true,
Deleted: false,
} }
err = m.addActivityCenterNotification(response, notification) err = m.addActivityCenterNotification(response, notification)
@ -780,6 +781,24 @@ func (m *Messenger) CancelRequestToJoinCommunity(request *requests.CancelRequest
response.AddCommunity(community) response.AddCommunity(community)
response.RequestsToJoinCommunity = append(response.RequestsToJoinCommunity, requestToJoin) response.RequestsToJoinCommunity = append(response.RequestsToJoinCommunity, requestToJoin)
// delete activity center notification
notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID)
if err != nil {
return nil, err
}
if notification != nil {
err = m.persistence.DeleteActivityCenterNotification(types.FromHex(requestToJoin.ID.String()))
if err != nil {
m.logger.Error("failed to delete notification from Activity Center", zap.Error(err))
return nil, err
}
// set notification as deleted, so that the client will remove the activity center notification from UI
notification.Deleted = true
response.AddActivityCenterNotification(notification)
}
return response, nil return response, nil
} }
@ -992,6 +1011,83 @@ func (m *Messenger) leaveCommunity(communityID types.HexBytes) (*MessengerRespon
return response, nil return response, nil
} }
func (m *Messenger) CheckAndDeletePendingRequestToJoinCommunity(sendResponse bool) (*MessengerResponse, error) {
sendSignal := false
pendingRequestsToJoin, err := m.communitiesManager.PendingRequestsToJoin()
if err != nil {
m.logger.Error("failed to fetch pending request to join", zap.Error(err))
return nil, err
}
if len(pendingRequestsToJoin) == 0 {
return nil, nil
}
response := &MessengerResponse{}
timeNow := uint64(time.Now().Unix())
for _, requestToJoin := range pendingRequestsToJoin {
requestTimeOutClock, err := communities.AddTimeoutToRequestToJoinClock(requestToJoin.Clock)
if err != nil {
return nil, err
}
if timeNow >= requestTimeOutClock {
err := m.communitiesManager.DeletePendingRequestToJoin(requestToJoin)
if err != nil {
m.logger.Error("failed to delete pending request to join", zap.String("req-id", requestToJoin.ID.String()), zap.Error(err))
return nil, err
}
requestToJoin.Deleted = true
response.AddRequestToJoinCommunity(requestToJoin)
notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID)
if err != nil {
m.logger.Error("failed to fetch pending request to join", zap.Error(err))
return nil, err
}
if notification != nil {
// Delete activity centre notification for community admin
if notification.Type == ActivityCenterNotificationTypeCommunityMembershipRequest {
err = m.persistence.DeleteActivityCenterNotification(types.FromHex(requestToJoin.ID.String()))
if err != nil {
m.logger.Error("failed to delete notification from activity center", zap.Error(err))
return nil, err
}
notification.Deleted = true
response.AddActivityCenterNotification(notification)
}
// Update activity centre notification for requester
if notification.Type == ActivityCenterNotificationTypeCommunityRequest {
notification.MembershipStatus = ActivityCenterMembershipStatusIdle
notification.Read = false
notification.Deleted = false
err = m.addActivityCenterNotification(response, notification)
if err != nil {
m.logger.Error("failed to update notification in activity center", zap.Error(err))
return nil, err
}
}
}
sendSignal = true
}
}
if sendSignal && !sendResponse {
signal.SendNewMessages(response)
}
if sendResponse {
return response, nil
}
return nil, nil
}
func (m *Messenger) CreateCommunityChat(communityID types.HexBytes, c *protobuf.CommunityChat) (*MessengerResponse, error) { func (m *Messenger) CreateCommunityChat(communityID types.HexBytes, c *protobuf.CommunityChat) (*MessengerResponse, error) {
var response MessengerResponse var response MessengerResponse

View File

@ -1262,6 +1262,17 @@ func (m *Messenger) HandleCommunityRequestToJoin(state *ReceivedMessageState, si
return errors.New("invalid community id") return errors.New("invalid community id")
} }
timeNow := uint64(time.Now().Unix())
requestTimeOutClock, err := communities.AddTimeoutToRequestToJoinClock(requestToJoinProto.Clock)
if err != nil {
return err
}
if timeNow >= requestTimeOutClock {
return errors.New("request is expired")
}
requestToJoin, err := m.communitiesManager.HandleCommunityRequestToJoin(signer, &requestToJoinProto) requestToJoin, err := m.communitiesManager.HandleCommunityRequestToJoin(signer, &requestToJoinProto)
if err != nil { if err != nil {
return err return err
@ -1317,6 +1328,7 @@ func (m *Messenger) HandleCommunityRequestToJoin(state *ReceivedMessageState, si
Author: contact.ID, Author: contact.ID,
CommunityID: community.IDString(), CommunityID: community.IDString(),
MembershipStatus: ActivityCenterMembershipStatusPending, MembershipStatus: ActivityCenterMembershipStatusPending,
Deleted: false,
} }
err = m.addActivityCenterNotification(state.Response, notification) err = m.addActivityCenterNotification(state.Response, notification)
@ -1417,6 +1429,8 @@ func (m *Messenger) HandleCommunityRequestToJoinResponse(state *ReceivedMessageS
if notification != nil { if notification != nil {
if requestToJoinResponseProto.Accepted { if requestToJoinResponseProto.Accepted {
notification.MembershipStatus = ActivityCenterMembershipStatusAccepted notification.MembershipStatus = ActivityCenterMembershipStatusAccepted
notification.Read = false
notification.Deleted = false
} else { } else {
notification.MembershipStatus = ActivityCenterMembershipStatusDeclined notification.MembershipStatus = ActivityCenterMembershipStatusDeclined
} }

View File

@ -340,6 +340,10 @@ func (r *MessengerResponse) AddCommunitySettings(c *communities.CommunitySetting
r.communitiesSettings[c.CommunityID] = c r.communitiesSettings[c.CommunityID] = c
} }
func (r *MessengerResponse) AddRequestToJoinCommunity(requestToJoin *communities.RequestToJoin) {
r.RequestsToJoinCommunity = append(r.RequestsToJoinCommunity, requestToJoin)
}
func (r *MessengerResponse) AddSetting(s *settings.SyncSettingField) { func (r *MessengerResponse) AddSetting(s *settings.SyncSettingField) {
r.Settings = append(r.Settings, s) r.Settings = append(r.Settings, s)
} }

View File

@ -554,6 +554,11 @@ func (api *PublicAPI) RequestToJoinCommunity(request *requests.RequestToJoinComm
return api.service.messenger.RequestToJoinCommunity(request) return api.service.messenger.RequestToJoinCommunity(request)
} }
// CheckAndClearPendingRequestToJoinCommunity to delete pending request to join a community which are older than 7 days
func (api *PublicAPI) CheckAndDeletePendingRequestToJoinCommunity() (*protocol.MessengerResponse, error) {
return api.service.messenger.CheckAndDeletePendingRequestToJoinCommunity(true)
}
// CreateCommunityCategory creates a category within a particular community // CreateCommunityCategory creates a category within a particular community
func (api *PublicAPI) CreateCommunityCategory(request *requests.CreateCommunityCategory) (*protocol.MessengerResponse, error) { func (api *PublicAPI) CreateCommunityCategory(request *requests.CreateCommunityCategory) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateCommunityCategory(request) return api.service.messenger.CreateCommunityCategory(request)