Community categories (#2228)

* create and edit community categories
* edit categories order
* adding category to chat
* Adding categories to json
This commit is contained in:
RichΛrd 2021-05-23 09:34:17 -04:00 committed by GitHub
parent beacc3233c
commit 92032c7158
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1377 additions and 74 deletions

View File

@ -1 +1 @@
0.79.1
0.79.2

View File

@ -176,7 +176,7 @@ func (d *pathDecoder) parseSeparator() error {
return d.saveSegment()
}
return fmt.Errorf("expected %s, got %s", string(tokenSeparator), string(b))
return fmt.Errorf("expected %s, got %s", string(rune(tokenSeparator)), string(b))
}
func (d *pathDecoder) parseSegment() error {

1
go.mod
View File

@ -78,6 +78,7 @@ require (
golang.org/x/tools v0.0.0-20200211045251-2de505fc5306 // indirect
google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11 // indirect
google.golang.org/grpc v1.25.1 // indirect
google.golang.org/protobuf v1.26.0-rc.1
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0

10
go.sum
View File

@ -202,6 +202,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -850,23 +852,20 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20180302121509-abf0ba0be5d5/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190709231704-1e4459ed25ff h1:uuol9OUzSvZntY1v963NAbVd7A+PHLMz1FlCe3Lorcs=
@ -874,7 +873,6 @@ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190709231704-1e4459ed25ff/go.mod h1:uAJ
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
@ -884,13 +882,11 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.6 h1:97YCGUei5WVbkKfogoJQsLwUJ17cWvpLrgNvlcbxikE=
gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=

View File

@ -85,6 +85,10 @@ type Chat struct {
// CommunityID is the id of the community it belongs to
CommunityID string `json:"communityId,omitempty"`
// CategoryID is the id of the community category this chat belongs to.
CategoryID string `json:"categoryId,omitempty"`
// Joined is a timestamp that indicates when the chat was joined
Joined int64 `json:"joined,omitempty"`
@ -305,6 +309,7 @@ func CreateCommunityChat(orgID, chatID string, orgChat *protobuf.CommunityChat,
return &Chat{
CommunityID: orgID,
CategoryID: orgChat.CategoryId,
Name: orgChat.Identity.DisplayName,
Active: true,
Color: color,

View File

@ -62,6 +62,14 @@ type CommunityChat struct {
Members map[string]*protobuf.CommunityMember `json:"members"`
Permissions *protobuf.CommunityPermissions `json:"permissions"`
CanPost bool `json:"canPost"`
Position int `json:"position"`
CategoryID string `json:"categoryID"`
}
type CommunityCategory struct {
ID string `json:"id"`
Name string `json:"name"`
Position int `json:"position"` // Position is used to sort the categories
}
func (o *Community) MarshalJSON() ([]byte, error) {
@ -77,6 +85,7 @@ func (o *Community) MarshalJSON() ([]byte, error) {
Name string `json:"name"`
Description string `json:"description"`
Chats map[string]CommunityChat `json:"chats"`
Categories map[string]CommunityCategory `json:"categories"`
Images map[string]images.IdentityImage `json:"images"`
Permissions *protobuf.CommunityPermissions `json:"permissions"`
Members map[string]*protobuf.CommunityMember `json:"members"`
@ -91,6 +100,7 @@ func (o *Community) MarshalJSON() ([]byte, error) {
Admin: o.IsAdmin(),
Verified: o.config.Verified,
Chats: make(map[string]CommunityChat),
Categories: make(map[string]CommunityCategory),
Joined: o.config.Joined,
CanRequestAccess: o.CanRequestAccess(o.config.MemberIdentity),
CanJoin: o.canJoin(),
@ -99,6 +109,14 @@ func (o *Community) MarshalJSON() ([]byte, error) {
IsMember: o.isMember(),
}
if o.config.CommunityDescription != nil {
for id, c := range o.config.CommunityDescription.Categories {
category := CommunityCategory{
ID: id,
Name: c.Name,
Position: int(c.Position),
}
communityItem.Categories[id] = category
}
for id, c := range o.config.CommunityDescription.Chats {
canPost, err := o.CanPost(o.config.MemberIdentity, id, nil)
if err != nil {
@ -110,6 +128,8 @@ func (o *Community) MarshalJSON() ([]byte, error) {
Permissions: c.Permissions,
Members: c.Members,
CanPost: canPost,
CategoryID: c.CategoryId,
Position: int(c.Position),
}
communityItem.Chats[id] = chat
}
@ -169,8 +189,10 @@ func (o *Community) initialize() {
}
type CommunityChatChanges struct {
MembersAdded map[string]*protobuf.CommunityMember
MembersRemoved map[string]*protobuf.CommunityMember
MembersAdded map[string]*protobuf.CommunityMember
MembersRemoved map[string]*protobuf.CommunityMember
CategoryModified string
PositionModified int
}
type CommunityChanges struct {
@ -182,6 +204,10 @@ type CommunityChanges struct {
ChatsAdded map[string]*protobuf.CommunityChat `json:"chatsAdded"`
ChatsModified map[string]*CommunityChatChanges `json:"chatsModified"`
CategoriesRemoved []string `json:"categoriesRemoved"`
CategoriesAdded map[string]*protobuf.CommunityCategory `json:"categoriesAdded"`
CategoriesModified map[string]*protobuf.CommunityCategory `json:"categoriesModified"`
// ShouldMemberJoin indicates whether the user should join this community
// automatically
ShouldMemberJoin bool `json:"memberAdded"`
@ -233,6 +259,14 @@ func (o *Community) CreateChat(chatID string, chat *protobuf.CommunityChat) (*Co
return nil, ErrChatAlreadyExists
}
// Sets the chat position to be the last within its category
chat.Position = 0
for _, c := range o.config.CommunityDescription.Chats {
if c.CategoryId == chat.CategoryId {
chat.Position++
}
}
o.config.CommunityDescription.Chats[chatID] = chat
o.increaseClock()
@ -253,6 +287,15 @@ func (o *Community) DeleteChat(chatID string) (*protobuf.CommunityDescription, e
if o.config.CommunityDescription.Chats == nil {
o.config.CommunityDescription.Chats = make(map[string]*protobuf.CommunityChat)
}
changes := o.emptyCommunityChanges()
if chat, exists := o.config.CommunityDescription.Chats[chatID]; exists {
tmpCatID := chat.CategoryId
chat.CategoryId = ""
o.SortCategoryChats(changes, tmpCatID)
}
delete(o.config.CommunityDescription.Chats, chatID)
o.increaseClock()
@ -570,6 +613,7 @@ func (o *Community) UpdateCommunityDescription(signer *ecdsa.PublicKey, descript
if o.config.CommunityDescription.Chats == nil {
o.config.CommunityDescription.Chats = make(map[string]*protobuf.CommunityChat)
}
if _, ok := o.config.CommunityDescription.Chats[chatID]; !ok {
if response.ChatsAdded == nil {
response.ChatsAdded = make(map[string]*protobuf.CommunityChat)
@ -606,6 +650,65 @@ func (o *Community) UpdateCommunityDescription(signer *ecdsa.PublicKey, descript
}
}
}
// Check for categories that were removed
for categoryID := range o.config.CommunityDescription.Categories {
if description.Categories == nil {
description.Categories = make(map[string]*protobuf.CommunityCategory)
}
if description.Chats == nil {
description.Chats = make(map[string]*protobuf.CommunityChat)
}
if _, ok := description.Categories[categoryID]; !ok {
response.CategoriesRemoved = append(response.CategoriesRemoved, categoryID)
}
if o.config.CommunityDescription.Chats == nil {
o.config.CommunityDescription.Chats = make(map[string]*protobuf.CommunityChat)
}
}
// Check for categories that were added
for categoryID, category := range description.Categories {
if o.config.CommunityDescription.Categories == nil {
o.config.CommunityDescription.Categories = make(map[string]*protobuf.CommunityCategory)
}
if _, ok := o.config.CommunityDescription.Categories[categoryID]; !ok {
if response.CategoriesAdded == nil {
response.CategoriesAdded = make(map[string]*protobuf.CommunityCategory)
}
response.CategoriesAdded[categoryID] = category
} else {
if o.config.CommunityDescription.Categories[categoryID].Name != category.Name || o.config.CommunityDescription.Categories[categoryID].Position != category.Position {
response.CategoriesModified[categoryID] = category
}
}
}
// Check for chat categories that were modified
for chatID, chat := range description.Chats {
if o.config.CommunityDescription.Chats == nil {
o.config.CommunityDescription.Chats = make(map[string]*protobuf.CommunityChat)
}
if _, ok := o.config.CommunityDescription.Chats[chatID]; !ok {
continue // It's a new chat
}
if o.config.CommunityDescription.Chats[chatID].CategoryId != chat.CategoryId {
if response.ChatsModified[chatID] == nil {
response.ChatsModified[chatID] = &CommunityChatChanges{
MembersAdded: make(map[string]*protobuf.CommunityMember),
MembersRemoved: make(map[string]*protobuf.CommunityMember),
}
}
response.ChatsModified[chatID].CategoryModified = chat.CategoryId
}
}
}
o.config.CommunityDescription = description
@ -765,6 +868,17 @@ func (o *Community) Chats() map[string]*protobuf.CommunityChat {
return response
}
func (o *Community) Categories() map[string]*protobuf.CommunityCategory {
o.mutex.Lock()
defer o.mutex.Unlock()
response := make(map[string]*protobuf.CommunityCategory)
for k, v := range o.config.CommunityDescription.Categories {
response[k] = v
}
return response
}
func (o *Community) VerifyGrantSignature(data []byte) (*protobuf.Grant, error) {
if len(data) <= signatureLength {
return nil, ErrInvalidGrant
@ -1015,5 +1129,28 @@ func emptyCommunityChanges() *CommunityChanges {
ChatsRemoved: make(map[string]*protobuf.CommunityChat),
ChatsAdded: make(map[string]*protobuf.CommunityChat),
ChatsModified: make(map[string]*CommunityChatChanges),
CategoriesRemoved: []string{},
CategoriesAdded: make(map[string]*protobuf.CommunityCategory),
CategoriesModified: make(map[string]*protobuf.CommunityCategory),
}
}
type sortSlice []sorterHelperIdx
type sorterHelperIdx struct {
pos int32
catID string
chatID string
}
func (d sortSlice) Len() int {
return len(d)
}
func (d sortSlice) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
func (d sortSlice) Less(i, j int) bool {
return d[i].pos < d[j].pos
}

View File

@ -0,0 +1,359 @@
package communities
import (
"sort"
"github.com/status-im/status-go/protocol/protobuf"
)
func (o *Community) CreateCategory(categoryID string, categoryName string, chatIDs []string) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
if o.config.CommunityDescription.Categories == nil {
o.config.CommunityDescription.Categories = make(map[string]*protobuf.CommunityCategory)
}
if _, ok := o.config.CommunityDescription.Categories[categoryID]; ok {
return nil, ErrCategoryAlreadyExists
}
for _, cid := range chatIDs {
c, exists := o.config.CommunityDescription.Chats[cid]
if !exists {
return nil, ErrChatNotFound
}
if exists && c.CategoryId != categoryID && c.CategoryId != "" {
return nil, ErrChatAlreadyAssigned
}
}
changes := o.emptyCommunityChanges()
o.config.CommunityDescription.Categories[categoryID] = &protobuf.CommunityCategory{
CategoryId: categoryID,
Name: categoryName,
Position: int32(len(o.config.CommunityDescription.Categories)),
}
for i, cid := range chatIDs {
o.config.CommunityDescription.Chats[cid].CategoryId = categoryID
o.config.CommunityDescription.Chats[cid].Position = int32(i)
}
o.SortCategoryChats(changes, "")
o.increaseClock()
changes.CategoriesAdded[categoryID] = o.config.CommunityDescription.Categories[categoryID]
for i, cid := range chatIDs {
changes.ChatsModified[cid] = &CommunityChatChanges{
MembersAdded: make(map[string]*protobuf.CommunityMember),
MembersRemoved: make(map[string]*protobuf.CommunityMember),
CategoryModified: categoryID,
PositionModified: i,
}
}
return changes, nil
}
func (o *Community) EditCategory(categoryID string, categoryName string, chatIDs []string) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
if o.config.CommunityDescription.Categories == nil {
o.config.CommunityDescription.Categories = make(map[string]*protobuf.CommunityCategory)
}
if _, ok := o.config.CommunityDescription.Categories[categoryID]; !ok {
return nil, ErrCategoryNotFound
}
for _, cid := range chatIDs {
c, exists := o.config.CommunityDescription.Chats[cid]
if !exists {
return nil, ErrChatNotFound
}
if exists && c.CategoryId != categoryID && c.CategoryId != "" {
return nil, ErrChatAlreadyAssigned
}
}
changes := o.emptyCommunityChanges()
emptyCatLen := o.getCategoryChatCount("")
// remove any chat that might have been assigned before and now it's not part of the category
var chatsToRemove []string
for k, chat := range o.config.CommunityDescription.Chats {
if chat.CategoryId == categoryID {
found := false
for _, c := range chatIDs {
if k == c {
found = true
}
}
if !found {
chat.CategoryId = ""
chatsToRemove = append(chatsToRemove, k)
}
}
}
o.config.CommunityDescription.Categories[categoryID].Name = categoryName
for i, cid := range chatIDs {
o.config.CommunityDescription.Chats[cid].CategoryId = categoryID
o.config.CommunityDescription.Chats[cid].Position = int32(i)
}
for i, cid := range chatsToRemove {
o.config.CommunityDescription.Chats[cid].Position = int32(emptyCatLen + i)
changes.ChatsModified[cid] = &CommunityChatChanges{
MembersAdded: make(map[string]*protobuf.CommunityMember),
MembersRemoved: make(map[string]*protobuf.CommunityMember),
CategoryModified: "",
PositionModified: int(o.config.CommunityDescription.Chats[cid].Position),
}
}
o.SortCategoryChats(changes, "")
o.increaseClock()
changes.CategoriesModified[categoryID] = o.config.CommunityDescription.Categories[categoryID]
for i, cid := range chatIDs {
changes.ChatsModified[cid] = &CommunityChatChanges{
MembersAdded: make(map[string]*protobuf.CommunityMember),
MembersRemoved: make(map[string]*protobuf.CommunityMember),
CategoryModified: categoryID,
PositionModified: i,
}
}
return changes, nil
}
func (o *Community) ReorderCategories(categoryID string, newPosition int) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
if newPosition > 0 && newPosition >= len(o.config.CommunityDescription.Categories) {
newPosition = len(o.config.CommunityDescription.Categories) - 1
} else if newPosition < 0 {
newPosition = 0
}
o.config.CommunityDescription.Categories[categoryID].Position = int32(newPosition)
s := make(sortSlice, 0, len(o.config.CommunityDescription.Categories))
for catID, category := range o.config.CommunityDescription.Categories {
position := category.Position
if category.CategoryId != categoryID && position >= int32(newPosition) {
position = position + 1
}
s = append(s, sorterHelperIdx{
pos: position,
catID: catID,
})
}
changes := o.emptyCommunityChanges()
o.setModifiedCategories(changes, s)
o.increaseClock()
return changes, nil
}
func (o *Community) setModifiedCategories(changes *CommunityChanges, s sortSlice) {
sort.Sort(s)
for i, catSortHelper := range s {
if o.config.CommunityDescription.Categories[catSortHelper.catID].Position != int32(i) {
o.config.CommunityDescription.Categories[catSortHelper.catID].Position = int32(i)
changes.CategoriesModified[catSortHelper.catID] = o.config.CommunityDescription.Categories[catSortHelper.catID]
}
}
}
func (o *Community) ReorderChat(categoryID string, chatID string, newPosition int) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
if _, exists := o.config.CommunityDescription.Categories[categoryID]; !exists {
return nil, ErrCategoryNotFound
}
var chat *protobuf.CommunityChat
var exists bool
if chat, exists = o.config.CommunityDescription.Chats[chatID]; !exists {
return nil, ErrChatNotFound
}
oldCategoryID := chat.CategoryId
chat.CategoryId = categoryID
changes := o.emptyCommunityChanges()
o.SortCategoryChats(changes, oldCategoryID)
o.insertAndSort(changes, categoryID, chat, newPosition)
o.increaseClock()
return changes, nil
}
func (o *Community) SortCategoryChats(changes *CommunityChanges, categoryID string) {
var catChats []string
for k, c := range o.config.CommunityDescription.Chats {
if c.CategoryId == categoryID {
catChats = append(catChats, k)
}
}
sortedChats := make(sortSlice, 0, len(catChats))
for _, k := range catChats {
sortedChats = append(sortedChats, sorterHelperIdx{
pos: o.config.CommunityDescription.Chats[k].Position,
chatID: k,
})
}
sort.Sort(sortedChats)
for i, chatSortHelper := range sortedChats {
if o.config.CommunityDescription.Chats[chatSortHelper.chatID].Position != int32(i) {
o.config.CommunityDescription.Chats[chatSortHelper.chatID].Position = int32(i)
if changes.ChatsModified[chatSortHelper.chatID] != nil {
changes.ChatsModified[chatSortHelper.chatID].PositionModified = i
} else {
changes.ChatsModified[chatSortHelper.chatID] = &CommunityChatChanges{
PositionModified: i,
MembersAdded: make(map[string]*protobuf.CommunityMember),
MembersRemoved: make(map[string]*protobuf.CommunityMember),
}
}
}
}
}
func (o *Community) insertAndSort(changes *CommunityChanges, categoryID string, chat *protobuf.CommunityChat, newPosition int) {
var catChats []string
for k, c := range o.config.CommunityDescription.Chats {
if c.CategoryId == categoryID {
catChats = append(catChats, k)
}
}
if newPosition > 0 && newPosition >= len(catChats) {
newPosition = len(catChats) - 1
} else if newPosition < 0 {
newPosition = 0
}
sortedChats := make(sortSlice, 0, len(catChats))
for _, k := range catChats {
position := chat.Position
if o.config.CommunityDescription.Chats[k] != chat && position >= int32(newPosition) {
position = position + 1
}
sortedChats = append(sortedChats, sorterHelperIdx{
pos: position,
chatID: k,
})
}
sort.Sort(sortedChats)
for i, chatSortHelper := range sortedChats {
if o.config.CommunityDescription.Chats[chatSortHelper.chatID].Position != int32(i) {
o.config.CommunityDescription.Chats[chatSortHelper.chatID].Position = int32(i)
if changes.ChatsModified[chatSortHelper.chatID] != nil {
changes.ChatsModified[chatSortHelper.chatID].PositionModified = i
} else {
changes.ChatsModified[chatSortHelper.chatID] = &CommunityChatChanges{
MembersAdded: make(map[string]*protobuf.CommunityMember),
MembersRemoved: make(map[string]*protobuf.CommunityMember),
PositionModified: i,
}
}
}
}
}
func (o *Community) getCategoryChatCount(categoryID string) int {
result := 0
for _, chat := range o.config.CommunityDescription.Chats {
if chat.CategoryId == categoryID {
result = result + 1
}
}
return result
}
func (o *Community) DeleteCategory(categoryID string) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
if _, exists := o.config.CommunityDescription.Categories[categoryID]; !exists {
return nil, ErrCategoryNotFound
}
changes := o.emptyCommunityChanges()
emptyCategoryChatCount := o.getCategoryChatCount("")
i := 0
for _, chat := range o.config.CommunityDescription.Chats {
if chat.CategoryId == categoryID {
i++
chat.CategoryId = ""
chat.Position = int32(emptyCategoryChatCount + i)
}
}
o.SortCategoryChats(changes, "")
delete(o.config.CommunityDescription.Categories, categoryID)
changes.CategoriesRemoved = append(changes.CategoriesRemoved, categoryID)
// Reorder
s := make(sortSlice, 0, len(o.config.CommunityDescription.Categories))
for _, cat := range o.config.CommunityDescription.Categories {
s = append(s, sorterHelperIdx{
pos: cat.Position,
catID: cat.CategoryId,
})
}
o.setModifiedCategories(changes, s)
o.increaseClock()
return changes, nil
}

View File

@ -0,0 +1,246 @@
package communities
import (
"github.com/status-im/status-go/protocol/protobuf"
)
func (s *CommunitySuite) TestCreateCategory() {
newCategoryID := "new-category-id"
newCategoryName := "new-category-name"
org := s.buildCommunity(&s.identity.PublicKey)
org.config.PrivateKey = nil
_, err := org.CreateCategory(newCategoryID, newCategoryName, []string{})
s.Require().Equal(ErrNotAdmin, err)
org.config.PrivateKey = s.identity
changes, err := org.CreateCategory(newCategoryID, newCategoryName, []string{})
description := org.config.CommunityDescription
s.Require().NoError(err)
s.Require().NotNil(description.Categories)
s.Require().NotNil(description.Categories[newCategoryID])
s.Require().Equal(newCategoryName, description.Categories[newCategoryID].Name)
s.Require().Equal(newCategoryID, description.Categories[newCategoryID].CategoryId)
s.Require().Equal(int32(len(description.Categories)-1), description.Categories[newCategoryID].Position)
s.Require().NotNil(changes)
s.Require().NotNil(changes.CategoriesAdded[newCategoryID])
s.Require().Equal(description.Categories[newCategoryID], changes.CategoriesAdded[newCategoryID])
s.Require().Nil(changes.CategoriesModified[newCategoryID])
_, err = org.CreateCategory(newCategoryID, newCategoryName, []string{})
s.Require().Equal(ErrCategoryAlreadyExists, err)
newCategoryID2 := "new-category-id2"
newCategoryName2 := "new-category-name2"
changes, err = org.CreateCategory(newCategoryID2, newCategoryName2, []string{})
s.Require().NoError(err)
s.Require().Equal(int32(len(description.Categories)-1), description.Categories[newCategoryID2].Position)
s.Require().NotNil(changes.CategoriesAdded[newCategoryID2])
s.Require().Nil(changes.CategoriesModified[newCategoryID2])
newCategoryID3 := "new-category-id3"
newCategoryName3 := "new-category-name3"
_, err = org.CreateCategory(newCategoryID3, newCategoryName3, []string{"some-chat-id"})
s.Require().Equal(ErrChatNotFound, err)
newChatID := "new-chat-id"
identity := &protobuf.ChatIdentity{
DisplayName: "new-chat-display-name",
Description: "new-chat-description",
}
permissions := &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
}
_, _ = org.CreateChat(newChatID, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
changes, err = org.CreateCategory(newCategoryID3, newCategoryName3, []string{newChatID})
s.Require().NoError(err)
s.Require().NotNil(changes.ChatsModified[newChatID])
s.Require().Equal(newCategoryID3, changes.ChatsModified[newChatID].CategoryModified)
newCategoryID4 := "new-category-id4"
newCategoryName4 := "new-category-name4"
_, err = org.CreateCategory(newCategoryID4, newCategoryName4, []string{newChatID})
s.Require().Equal(ErrChatAlreadyAssigned, err)
}
func (s *CommunitySuite) TestEditCategory() {
newCategoryID := "new-category-id"
newCategoryName := "new-category-name"
editedCategoryName := "edited-category-name"
org := s.buildCommunity(&s.identity.PublicKey)
org.config.PrivateKey = s.identity
_, _ = org.CreateCategory(newCategoryID, newCategoryName, []string{testChatID1})
org.config.PrivateKey = nil
_, err := org.EditCategory(newCategoryID, editedCategoryName, []string{testChatID1})
s.Require().Equal(ErrNotAdmin, err)
org.config.PrivateKey = s.identity
_, err = org.EditCategory("some-random-category", editedCategoryName, []string{testChatID1})
s.Require().Equal(ErrCategoryNotFound, err)
changes, err := org.EditCategory(newCategoryID, editedCategoryName, []string{testChatID1})
description := org.config.CommunityDescription
s.Require().NoError(err)
s.Require().Equal(editedCategoryName, description.Categories[newCategoryID].Name)
s.Require().NotNil(changes)
s.Require().NotNil(changes.CategoriesModified[newCategoryID])
s.Require().Equal(description.Categories[newCategoryID], changes.CategoriesModified[newCategoryID])
s.Require().Nil(changes.CategoriesAdded[newCategoryID])
_, err = org.EditCategory(newCategoryID, editedCategoryName, []string{"some-random-chat-id"})
s.Require().Equal(ErrChatNotFound, err)
_, err = org.EditCategory(testCategoryID1, testCategoryName1, []string{testChatID1})
s.Require().Equal(ErrChatAlreadyAssigned, err)
// Edit by removing the chats
identity := &protobuf.ChatIdentity{
DisplayName: "new-chat-display-name",
Description: "new-chat-description",
}
permissions := &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
}
testChatID2 := "test-chat-id-2"
testChatID3 := "test-chat-id-3"
_, _ = org.CreateChat(testChatID2, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
_, _ = org.CreateChat(testChatID3, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
_, err = org.EditCategory(newCategoryID, editedCategoryName, []string{testChatID1, testChatID2, testChatID3})
s.Require().NoError(err)
s.Require().Equal(newCategoryID, description.Chats[testChatID1].CategoryId)
s.Require().Equal(newCategoryID, description.Chats[testChatID2].CategoryId)
s.Require().Equal(newCategoryID, description.Chats[testChatID3].CategoryId)
s.Require().Equal(int32(0), description.Chats[testChatID1].Position)
s.Require().Equal(int32(1), description.Chats[testChatID2].Position)
s.Require().Equal(int32(2), description.Chats[testChatID3].Position)
_, _ = org.EditCategory(newCategoryID, editedCategoryName, []string{testChatID1, testChatID3})
s.Require().Equal("", description.Chats[testChatID2].CategoryId)
s.Require().Equal(int32(0), description.Chats[testChatID1].Position)
s.Require().Equal(int32(1), description.Chats[testChatID3].Position)
_, _ = org.EditCategory(newCategoryID, editedCategoryName, []string{testChatID3})
s.Require().Equal("", description.Chats[testChatID1].CategoryId)
s.Require().Equal(int32(0), description.Chats[testChatID3].Position)
}
func (s *CommunitySuite) TestDeleteCategory() {
org := s.buildCommunity(&s.identity.PublicKey)
org.config.PrivateKey = s.identity
identity := &protobuf.ChatIdentity{
DisplayName: "new-chat-display-name",
Description: "new-chat-description",
}
permissions := &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
}
testChatID2 := "test-chat-id-2"
testChatID3 := "test-chat-id-3"
newCategoryID := "new-category-id"
newCategoryName := "new-category-name"
_, _ = org.CreateChat(testChatID2, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
_, _ = org.CreateChat(testChatID3, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
_, _ = org.CreateCategory(newCategoryID, newCategoryName, []string{})
description := org.config.CommunityDescription
_, _ = org.EditCategory(newCategoryID, newCategoryName, []string{testChatID2, testChatID1})
s.Require().Equal(newCategoryID, description.Chats[testChatID1].CategoryId)
s.Require().Equal(newCategoryID, description.Chats[testChatID2].CategoryId)
s.Require().Equal(int32(0), description.Chats[testChatID3].Position)
s.Require().Equal(int32(0), description.Chats[testChatID2].Position)
s.Require().Equal(int32(1), description.Chats[testChatID1].Position)
org.config.PrivateKey = nil
_, err := org.DeleteCategory(testCategoryID1)
s.Require().Equal(ErrNotAdmin, err)
org.config.PrivateKey = s.identity
_, err = org.DeleteCategory("some-category-id")
s.Require().Equal(ErrCategoryNotFound, err)
changes, err := org.DeleteCategory(newCategoryID)
s.Require().NoError(err)
s.Require().NotNil(changes)
s.Require().Equal("", description.Chats[testChatID1].CategoryId)
s.Require().Equal("", description.Chats[testChatID2].CategoryId)
s.Require().Equal("", description.Chats[testChatID3].CategoryId)
}
func (s *CommunitySuite) TestDeleteChatOrder() {
org := s.buildCommunity(&s.identity.PublicKey)
org.config.PrivateKey = s.identity
identity := &protobuf.ChatIdentity{
DisplayName: "new-chat-display-name",
Description: "new-chat-description",
}
permissions := &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
}
testChatID2 := "test-chat-id-2"
testChatID3 := "test-chat-id-3"
newCategoryID := "new-category-id"
newCategoryName := "new-category-name"
_, _ = org.CreateChat(testChatID2, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
_, _ = org.CreateChat(testChatID3, &protobuf.CommunityChat{
Identity: identity,
Permissions: permissions,
})
_, _ = org.CreateCategory(newCategoryID, newCategoryName, []string{testChatID1, testChatID2, testChatID3})
description, _ := org.DeleteChat(testChatID2)
s.Require().Equal(int32(0), description.Chats[testChatID1].Position)
s.Require().Equal(int32(1), description.Chats[testChatID3].Position)
description, _ = org.DeleteChat(testChatID1)
s.Require().Equal(int32(0), description.Chats[testChatID3].Position)
_, err := org.DeleteChat(testChatID3)
s.Require().NoError(err)
}

View File

@ -18,6 +18,8 @@ func TestCommunitySuite(t *testing.T) {
}
const testChatID1 = "chat-id-1"
const testCategoryID1 = "category-id-1"
const testCategoryName1 = "category-name-1"
const testChatID2 = "chat-id-2"
type CommunitySuite struct {
@ -131,9 +133,9 @@ func (s *CommunitySuite) TestCreateChat() {
s.Require().NoError(err)
s.Require().NotNil(description)
s.Require().NotNil(description.Chats[newChatID])
s.Require().NotEmpty(description.Clock)
s.Require().Equal(len(description.Chats)-1, int(description.Chats[newChatID].Position))
s.Require().Equal(permissions, description.Chats[newChatID].Permissions)
s.Require().Equal(identity, description.Chats[newChatID].Identity)
@ -705,10 +707,12 @@ func (s *CommunitySuite) emptyCommunityDescriptionWithChat() *protobuf.Community
Members: make(map[string]*protobuf.CommunityMember),
Clock: 1,
Chats: make(map[string]*protobuf.CommunityChat),
Categories: make(map[string]*protobuf.CommunityCategory),
Permissions: &protobuf.CommunityPermissions{},
}
desc.Chats[testChatID1] = &protobuf.CommunityChat{Permissions: &protobuf.CommunityPermissions{}, Members: make(map[string]*protobuf.CommunityMember)}
desc.Categories[testCategoryID1] = &protobuf.CommunityCategory{CategoryId: testCategoryID1, Name: testCategoryName1, Position: 0}
desc.Chats[testChatID1] = &protobuf.CommunityChat{Position: 0, Permissions: &protobuf.CommunityPermissions{}, Members: make(map[string]*protobuf.CommunityMember)}
desc.Members[common.PubkeyToHex(&s.member1.PublicKey)] = &protobuf.CommunityMember{}
desc.Chats[testChatID1].Members[common.PubkeyToHex(&s.member1.PublicKey)] = &protobuf.CommunityMember{}

View File

@ -3,8 +3,11 @@ package communities
import "errors"
var ErrChatNotFound = errors.New("chat not found")
var ErrCategoryNotFound = errors.New("category not found")
var ErrChatAlreadyAssigned = errors.New("chat already assigned to a category")
var ErrOrgNotFound = errors.New("community not found")
var ErrChatAlreadyExists = errors.New("chat already exists")
var ErrCategoryAlreadyExists = errors.New("category already exists")
var ErrCantRequestAccess = errors.New("can't request access")
var ErrInvalidCommunityDescription = errors.New("invalid community description")
var ErrInvalidCommunityDescriptionNoOrgPermissions = errors.New("invalid community description no org permissions")
@ -12,6 +15,9 @@ var ErrInvalidCommunityDescriptionNoChatPermissions = errors.New("invalid commun
var ErrInvalidCommunityDescriptionUnknownChatAccess = errors.New("invalid community description unknown chat access")
var ErrInvalidCommunityDescriptionUnknownOrgAccess = errors.New("invalid community description unknown org access")
var ErrInvalidCommunityDescriptionMemberInChatButNotInOrg = errors.New("invalid community description member in chat but not in org")
var ErrInvalidCommunityDescriptionCategoryNoID = errors.New("invalid community category id")
var ErrInvalidCommunityDescriptionCategoryNoName = errors.New("invalid community category name")
var ErrInvalidCommunityDescriptionUnknownChatCategory = errors.New("invalid community category in chat")
var ErrNotAdmin = errors.New("no admin privileges for this community")
var ErrInvalidGrant = errors.New("invalid grant")
var ErrNotAuthorized = errors.New("not authorized")

View File

@ -310,6 +310,131 @@ func (m *Manager) CreateChat(communityID types.HexBytes, chat *protobuf.Communit
return community, changes, nil
}
func (m *Manager) CreateCategory(request *requests.CreateCommunityCategory) (*Community, *CommunityChanges, error) {
community, err := m.GetByID(request.CommunityID)
if err != nil {
return nil, nil, err
}
if community == nil {
return nil, nil, ErrOrgNotFound
}
categoryID := uuid.New().String()
changes, err := community.CreateCategory(categoryID, request.CategoryName, request.ChatIDs)
if err != nil {
return nil, nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, nil, err
}
// Advertise changes
m.publish(&Subscription{Community: community})
return community, changes, nil
}
func (m *Manager) EditCategory(request *requests.EditCommunityCategory) (*Community, *CommunityChanges, error) {
community, err := m.GetByID(request.CommunityID)
if err != nil {
return nil, nil, err
}
if community == nil {
return nil, nil, ErrOrgNotFound
}
changes, err := community.EditCategory(request.CategoryID, request.CategoryName, request.ChatIDs)
if err != nil {
return nil, nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, nil, err
}
// Advertise changes
m.publish(&Subscription{Community: community})
return community, changes, nil
}
func (m *Manager) ReorderCategories(request *requests.ReorderCommunityCategories) (*Community, *CommunityChanges, error) {
community, err := m.GetByID(request.CommunityID)
if err != nil {
return nil, nil, err
}
if community == nil {
return nil, nil, ErrOrgNotFound
}
changes, err := community.ReorderCategories(request.CategoryID, request.Position)
if err != nil {
return nil, nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, nil, err
}
// Advertise changes
m.publish(&Subscription{Community: community})
return community, changes, nil
}
func (m *Manager) ReorderChat(request *requests.ReorderCommunityChat) (*Community, *CommunityChanges, error) {
community, err := m.GetByID(request.CommunityID)
if err != nil {
return nil, nil, err
}
if community == nil {
return nil, nil, ErrOrgNotFound
}
changes, err := community.ReorderChat(request.CategoryID, request.ChatID, request.Position)
if err != nil {
return nil, nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, nil, err
}
// Advertise changes
m.publish(&Subscription{Community: community})
return community, changes, nil
}
func (m *Manager) DeleteCategory(request *requests.DeleteCommunityCategory) (*Community, *CommunityChanges, error) {
community, err := m.GetByID(request.CommunityID)
if err != nil {
return nil, nil, err
}
if community == nil {
return nil, nil, ErrOrgNotFound
}
changes, err := community.DeleteCategory(request.CategoryID)
if err != nil {
return nil, nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, nil, err
}
// Advertise changes
m.publish(&Subscription{Community: community})
return community, changes, nil
}
func (m *Manager) HandleCommunityDescriptionMessage(signer *ecdsa.PublicKey, description *protobuf.CommunityDescription, payload []byte) (*CommunityResponse, error) {
id := crypto.CompressPubkey(signer)
community, err := m.persistence.GetByID(m.identity, id)

View File

@ -15,6 +15,12 @@ func validateCommunityChat(desc *protobuf.CommunityDescription, chat *protobuf.C
return ErrInvalidCommunityDescriptionUnknownChatAccess
}
if len(chat.CategoryId) != 0 {
if _, exists := desc.Categories[chat.CategoryId]; !exists {
return ErrInvalidCommunityDescriptionUnknownChatCategory
}
}
for pk := range chat.Members {
if desc.Members == nil {
return ErrInvalidCommunityDescriptionMemberInChatButNotInOrg
@ -28,6 +34,18 @@ func validateCommunityChat(desc *protobuf.CommunityDescription, chat *protobuf.C
return nil
}
func validateCommunityCategory(category *protobuf.CommunityCategory) error {
if len(category.CategoryId) == 0 {
return ErrInvalidCommunityDescriptionCategoryNoID
}
if len(category.Name) == 0 {
return ErrInvalidCommunityDescriptionCategoryNoName
}
return nil
}
func ValidateCommunityDescription(desc *protobuf.CommunityDescription) error {
if desc == nil {
return ErrInvalidCommunityDescription
@ -39,6 +57,12 @@ func ValidateCommunityDescription(desc *protobuf.CommunityDescription) error {
return ErrInvalidCommunityDescriptionUnknownOrgAccess
}
for _, category := range desc.Categories {
if err := validateCommunityCategory(category); err != nil {
return err
}
}
for _, chat := range desc.Chats {
if err := validateCommunityChat(desc, chat); err != nil {
return err

View File

@ -186,6 +186,26 @@ func (s *MessengerCommunitiesSuite) TestJoinCommunity() {
s.Require().NotEmpty(createdChat.Timestamp)
s.Require().True(strings.HasPrefix(createdChat.ID, community.IDString()))
// Make sure the changes are reflect in the community
community = response.Communities()[0]
var chatIds []string
for k := range community.Chats() {
chatIds = append(chatIds, k)
}
category := &requests.CreateCommunityCategory{
CommunityID: community.ID(),
CategoryName: "category-name",
ChatIDs: chatIds,
}
response, err = s.bob.CreateCommunityCategory(category)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Communities()[0].Categories(), 1)
// Make sure the changes are reflect in the community
community = response.Communities()[0]
chats := community.Chats()
@ -231,6 +251,12 @@ func (s *MessengerCommunitiesSuite) TestJoinCommunity() {
s.Require().Len(response.Communities(), 1)
s.Require().True(response.Communities()[0].Joined())
s.Require().Len(response.Chats(), 1)
s.Require().Len(response.Communities()[0].Categories(), 1)
var categoryID string
for k := range response.Communities()[0].Categories() {
categoryID = k
}
// The chat should be created
createdChat = response.Chats()[0]
@ -238,6 +264,7 @@ func (s *MessengerCommunitiesSuite) TestJoinCommunity() {
s.Require().Equal(orgChat.Identity.DisplayName, createdChat.Name)
s.Require().NotEmpty(createdChat.ID)
s.Require().Equal(ChatTypeCommunityChat, createdChat.ChatType)
s.Require().Equal(categoryID, createdChat.CategoryID)
s.Require().True(createdChat.Active)
s.Require().NotEmpty(createdChat.Timestamp)
s.Require().True(strings.HasPrefix(createdChat.ID, community.IDString()))
@ -464,6 +491,15 @@ func (s *MessengerCommunitiesSuite) TestImportCommunity() {
community := response.Communities()[0]
category := &requests.CreateCommunityCategory{
CommunityID: community.ID(),
CategoryName: "category-name",
ChatIDs: []string{},
}
response, err = s.bob.CreateCommunityCategory(category)
community = response.Communities()[0]
privateKey, err := s.bob.ExportCommunity(community.ID())
s.Require().NoError(err)
@ -496,6 +532,7 @@ func (s *MessengerCommunitiesSuite) TestImportCommunity() {
s.Require().NoError(err)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Communities()[0].Categories(), 1)
community = response.Communities()[0]
s.Require().True(community.Joined())
s.Require().True(community.IsAdmin())

View File

@ -235,6 +235,86 @@ func (m *Messenger) RequestToJoinCommunity(request *requests.RequestToJoinCommun
return response, nil
}
func (m *Messenger) CreateCommunityCategory(request *requests.CreateCommunityCategory) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
var response MessengerResponse
community, changes, err := m.communitiesManager.CreateCategory(request)
if err != nil {
return nil, err
}
response.AddCommunity(community)
response.CommunityChanges = []*communities.CommunityChanges{changes}
return &response, nil
}
func (m *Messenger) EditCommunityCategory(request *requests.EditCommunityCategory) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
var response MessengerResponse
community, changes, err := m.communitiesManager.EditCategory(request)
if err != nil {
return nil, err
}
response.AddCommunity(community)
response.CommunityChanges = []*communities.CommunityChanges{changes}
return &response, nil
}
func (m *Messenger) ReorderCommunityCategories(request *requests.ReorderCommunityCategories) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
var response MessengerResponse
community, changes, err := m.communitiesManager.ReorderCategories(request)
if err != nil {
return nil, err
}
response.AddCommunity(community)
response.CommunityChanges = []*communities.CommunityChanges{changes}
return &response, nil
}
func (m *Messenger) ReorderCommunityChat(request *requests.ReorderCommunityChat) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
var response MessengerResponse
community, changes, err := m.communitiesManager.ReorderChat(request)
if err != nil {
return nil, err
}
response.AddCommunity(community)
response.CommunityChanges = []*communities.CommunityChanges{changes}
return &response, nil
}
func (m *Messenger) DeleteCommunityCategory(request *requests.DeleteCommunityCategory) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
var response MessengerResponse
community, changes, err := m.communitiesManager.DeleteCategory(request)
if err != nil {
return nil, err
}
response.AddCommunity(community)
response.CommunityChanges = []*communities.CommunityChanges{changes}
return &response, nil
}
func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequestToJoinCommunity) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err

View File

@ -238,15 +238,16 @@ func (m *CommunityPermissions) GetAccess() CommunityPermissions_Access {
}
type CommunityDescription struct {
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
Members map[string]*CommunityMember `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Permissions *CommunityPermissions `protobuf:"bytes,3,opt,name=permissions,proto3" json:"permissions,omitempty"`
Identity *ChatIdentity `protobuf:"bytes,5,opt,name=identity,proto3" json:"identity,omitempty"`
Chats map[string]*CommunityChat `protobuf:"bytes,6,rep,name=chats,proto3" json:"chats,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
BanList []string `protobuf:"bytes,7,rep,name=ban_list,json=banList,proto3" json:"ban_list,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
Members map[string]*CommunityMember `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Permissions *CommunityPermissions `protobuf:"bytes,3,opt,name=permissions,proto3" json:"permissions,omitempty"`
Identity *ChatIdentity `protobuf:"bytes,5,opt,name=identity,proto3" json:"identity,omitempty"`
Chats map[string]*CommunityChat `protobuf:"bytes,6,rep,name=chats,proto3" json:"chats,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
BanList []string `protobuf:"bytes,7,rep,name=ban_list,json=banList,proto3" json:"ban_list,omitempty"`
Categories map[string]*CommunityCategory `protobuf:"bytes,8,rep,name=categories,proto3" json:"categories,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CommunityDescription) Reset() { *m = CommunityDescription{} }
@ -316,10 +317,19 @@ func (m *CommunityDescription) GetBanList() []string {
return nil
}
func (m *CommunityDescription) GetCategories() map[string]*CommunityCategory {
if m != nil {
return m.Categories
}
return nil
}
type CommunityChat struct {
Members map[string]*CommunityMember `protobuf:"bytes,1,rep,name=members,proto3" json:"members,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Permissions *CommunityPermissions `protobuf:"bytes,2,opt,name=permissions,proto3" json:"permissions,omitempty"`
Identity *ChatIdentity `protobuf:"bytes,3,opt,name=identity,proto3" json:"identity,omitempty"`
CategoryId string `protobuf:"bytes,4,opt,name=category_id,json=categoryId,proto3" json:"category_id,omitempty"`
Position int32 `protobuf:"varint,5,opt,name=position,proto3" json:"position,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -371,6 +381,75 @@ func (m *CommunityChat) GetIdentity() *ChatIdentity {
return nil
}
func (m *CommunityChat) GetCategoryId() string {
if m != nil {
return m.CategoryId
}
return ""
}
func (m *CommunityChat) GetPosition() int32 {
if m != nil {
return m.Position
}
return 0
}
type CommunityCategory struct {
CategoryId string `protobuf:"bytes,1,opt,name=category_id,json=categoryId,proto3" json:"category_id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Position int32 `protobuf:"varint,3,opt,name=position,proto3" json:"position,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CommunityCategory) Reset() { *m = CommunityCategory{} }
func (m *CommunityCategory) String() string { return proto.CompactTextString(m) }
func (*CommunityCategory) ProtoMessage() {}
func (*CommunityCategory) Descriptor() ([]byte, []int) {
return fileDescriptor_f937943d74c1cd8b, []int{5}
}
func (m *CommunityCategory) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CommunityCategory.Unmarshal(m, b)
}
func (m *CommunityCategory) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CommunityCategory.Marshal(b, m, deterministic)
}
func (m *CommunityCategory) XXX_Merge(src proto.Message) {
xxx_messageInfo_CommunityCategory.Merge(m, src)
}
func (m *CommunityCategory) XXX_Size() int {
return xxx_messageInfo_CommunityCategory.Size(m)
}
func (m *CommunityCategory) XXX_DiscardUnknown() {
xxx_messageInfo_CommunityCategory.DiscardUnknown(m)
}
var xxx_messageInfo_CommunityCategory proto.InternalMessageInfo
func (m *CommunityCategory) GetCategoryId() string {
if m != nil {
return m.CategoryId
}
return ""
}
func (m *CommunityCategory) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *CommunityCategory) GetPosition() int32 {
if m != nil {
return m.Position
}
return 0
}
type CommunityInvitation struct {
CommunityDescription []byte `protobuf:"bytes,1,opt,name=community_description,json=communityDescription,proto3" json:"community_description,omitempty"`
Grant []byte `protobuf:"bytes,2,opt,name=grant,proto3" json:"grant,omitempty"`
@ -385,7 +464,7 @@ func (m *CommunityInvitation) Reset() { *m = CommunityInvitation{} }
func (m *CommunityInvitation) String() string { return proto.CompactTextString(m) }
func (*CommunityInvitation) ProtoMessage() {}
func (*CommunityInvitation) Descriptor() ([]byte, []int) {
return fileDescriptor_f937943d74c1cd8b, []int{5}
return fileDescriptor_f937943d74c1cd8b, []int{6}
}
func (m *CommunityInvitation) XXX_Unmarshal(b []byte) error {
@ -448,7 +527,7 @@ func (m *CommunityRequestToJoin) Reset() { *m = CommunityRequestToJoin{}
func (m *CommunityRequestToJoin) String() string { return proto.CompactTextString(m) }
func (*CommunityRequestToJoin) ProtoMessage() {}
func (*CommunityRequestToJoin) Descriptor() ([]byte, []int) {
return fileDescriptor_f937943d74c1cd8b, []int{6}
return fileDescriptor_f937943d74c1cd8b, []int{7}
}
func (m *CommunityRequestToJoin) XXX_Unmarshal(b []byte) error {
@ -511,7 +590,7 @@ func (m *CommunityRequestToJoinResponse) Reset() { *m = CommunityRequest
func (m *CommunityRequestToJoinResponse) String() string { return proto.CompactTextString(m) }
func (*CommunityRequestToJoinResponse) ProtoMessage() {}
func (*CommunityRequestToJoinResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f937943d74c1cd8b, []int{7}
return fileDescriptor_f937943d74c1cd8b, []int{8}
}
func (m *CommunityRequestToJoinResponse) XXX_Unmarshal(b []byte) error {
@ -567,10 +646,12 @@ func init() {
proto.RegisterType((*CommunityMember)(nil), "protobuf.CommunityMember")
proto.RegisterType((*CommunityPermissions)(nil), "protobuf.CommunityPermissions")
proto.RegisterType((*CommunityDescription)(nil), "protobuf.CommunityDescription")
proto.RegisterMapType((map[string]*CommunityCategory)(nil), "protobuf.CommunityDescription.CategoriesEntry")
proto.RegisterMapType((map[string]*CommunityChat)(nil), "protobuf.CommunityDescription.ChatsEntry")
proto.RegisterMapType((map[string]*CommunityMember)(nil), "protobuf.CommunityDescription.MembersEntry")
proto.RegisterType((*CommunityChat)(nil), "protobuf.CommunityChat")
proto.RegisterMapType((map[string]*CommunityMember)(nil), "protobuf.CommunityChat.MembersEntry")
proto.RegisterType((*CommunityCategory)(nil), "protobuf.CommunityCategory")
proto.RegisterType((*CommunityInvitation)(nil), "protobuf.CommunityInvitation")
proto.RegisterType((*CommunityRequestToJoin)(nil), "protobuf.CommunityRequestToJoin")
proto.RegisterType((*CommunityRequestToJoinResponse)(nil), "protobuf.CommunityRequestToJoinResponse")
@ -581,53 +662,59 @@ func init() {
}
var fileDescriptor_f937943d74c1cd8b = []byte{
// 758 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0xef, 0x6e, 0xda, 0x48,
0x10, 0x8f, 0x31, 0x7f, 0xcc, 0x40, 0x88, 0xb3, 0xf9, 0xe7, 0x70, 0xba, 0x1c, 0x67, 0xdd, 0x49,
0x9c, 0x4e, 0xc7, 0x49, 0x44, 0x27, 0x9d, 0xaa, 0x36, 0x2d, 0x4d, 0xad, 0xd4, 0x0d, 0x98, 0x64,
0x81, 0x56, 0xfd, 0x64, 0x19, 0xb3, 0x6d, 0xad, 0xc0, 0x9a, 0x7a, 0x0d, 0x12, 0x0f, 0x50, 0xa9,
0x8f, 0xd0, 0x0f, 0xfd, 0xdc, 0xe7, 0xea, 0x1b, 0xf4, 0x15, 0x2a, 0xef, 0x02, 0x76, 0x52, 0x48,
0x23, 0x55, 0xfd, 0x64, 0xcf, 0xce, 0xcc, 0x6f, 0x67, 0x7e, 0x33, 0xfb, 0x83, 0x6d, 0xd7, 0x1f,
0x8d, 0x26, 0xd4, 0x0b, 0x3d, 0xc2, 0x6a, 0xe3, 0xc0, 0x0f, 0x7d, 0xa4, 0xf0, 0x4f, 0x7f, 0xf2,
0xaa, 0xbc, 0xe3, 0xbe, 0x71, 0x42, 0xdb, 0x1b, 0x10, 0x1a, 0x7a, 0xe1, 0x4c, 0xb8, 0xf5, 0x29,
0x64, 0xce, 0x02, 0x87, 0x86, 0xe8, 0x77, 0x28, 0x2e, 0x92, 0x67, 0xb6, 0x37, 0xd0, 0xa4, 0x8a,
0x54, 0x2d, 0xe2, 0xc2, 0xf2, 0xcc, 0x1c, 0xa0, 0x5f, 0x20, 0x3f, 0x22, 0xa3, 0x3e, 0x09, 0x22,
0x7f, 0x8a, 0xfb, 0x15, 0x71, 0x60, 0x0e, 0xd0, 0x01, 0xe4, 0xe6, 0xf8, 0x9a, 0x5c, 0x91, 0xaa,
0x79, 0x9c, 0x8d, 0x4c, 0x73, 0x80, 0x76, 0x21, 0xe3, 0x0e, 0x7d, 0xf7, 0x4a, 0x4b, 0x57, 0xa4,
0x6a, 0x1a, 0x0b, 0x43, 0x7f, 0x2f, 0xc1, 0xd6, 0xe9, 0x02, 0xbb, 0xc5, 0x41, 0xd0, 0x7f, 0x90,
0x09, 0xfc, 0x21, 0x61, 0x9a, 0x54, 0x91, 0xab, 0xa5, 0xfa, 0x6f, 0xb5, 0x45, 0xe9, 0xb5, 0x1b,
0x91, 0x35, 0x1c, 0x85, 0x61, 0x11, 0xad, 0x9f, 0x40, 0x86, 0xdb, 0x48, 0x85, 0x62, 0xcf, 0x3a,
0xb7, 0xda, 0x2f, 0x2c, 0x1b, 0xb7, 0x9b, 0x86, 0xba, 0x81, 0x8a, 0xa0, 0x44, 0x7f, 0x76, 0xa3,
0xd9, 0x54, 0x25, 0xb4, 0x07, 0xdb, 0xdc, 0x6a, 0x35, 0xac, 0xc6, 0x99, 0x61, 0xf7, 0x3a, 0x06,
0xee, 0xa8, 0x29, 0xfd, 0xb3, 0x04, 0xbb, 0xcb, 0x0b, 0x2e, 0x48, 0x30, 0xf2, 0x18, 0xf3, 0x7c,
0xca, 0xd0, 0x21, 0x28, 0x84, 0x32, 0xdb, 0xa7, 0xc3, 0x19, 0xa7, 0x43, 0xc1, 0x39, 0x42, 0x59,
0x9b, 0x0e, 0x67, 0x48, 0x83, 0xdc, 0x38, 0xf0, 0xa6, 0x4e, 0x48, 0x38, 0x11, 0x0a, 0x5e, 0x98,
0xe8, 0x01, 0x64, 0x1d, 0xd7, 0x25, 0x8c, 0x71, 0x1a, 0x4a, 0xf5, 0x3f, 0x57, 0x74, 0x91, 0xb8,
0xa4, 0xd6, 0xe0, 0xc1, 0x78, 0x9e, 0xa4, 0x77, 0x21, 0x2b, 0x4e, 0x10, 0x82, 0xd2, 0xa2, 0x9b,
0xc6, 0xe9, 0xa9, 0xd1, 0xe9, 0xa8, 0x1b, 0x68, 0x1b, 0x36, 0xad, 0xb6, 0xdd, 0x32, 0x5a, 0x8f,
0x0d, 0xdc, 0x79, 0x6a, 0x5e, 0xa8, 0x12, 0xda, 0x81, 0x2d, 0xd3, 0x7a, 0x6e, 0x76, 0x1b, 0x5d,
0xb3, 0x6d, 0xd9, 0x6d, 0xab, 0xf9, 0x52, 0x4d, 0xa1, 0x12, 0x40, 0xdb, 0xb2, 0xb1, 0x71, 0xd9,
0x33, 0x3a, 0x5d, 0x55, 0xd6, 0xbf, 0xc8, 0x89, 0x16, 0x9f, 0x10, 0xe6, 0x06, 0xde, 0x38, 0xf4,
0x7c, 0x1a, 0x0f, 0x47, 0x4a, 0x0c, 0x07, 0x19, 0x90, 0x13, 0x73, 0x65, 0x5a, 0xaa, 0x22, 0x57,
0x0b, 0xf5, 0xbf, 0x57, 0x34, 0x91, 0x80, 0xa9, 0x89, 0xb1, 0x30, 0x83, 0x86, 0xc1, 0x0c, 0x2f,
0x72, 0xd1, 0x23, 0x28, 0x8c, 0xe3, 0x4e, 0x39, 0x1f, 0x85, 0xfa, 0xd1, 0xed, 0x7c, 0xe0, 0x64,
0x0a, 0xaa, 0x83, 0xb2, 0xd8, 0x57, 0x2d, 0xc3, 0xd3, 0xf7, 0x13, 0xe9, 0x7c, 0xbf, 0x84, 0x17,
0x2f, 0xe3, 0xd0, 0x43, 0xc8, 0x44, 0x9b, 0xc7, 0xb4, 0x2c, 0x2f, 0xfd, 0xaf, 0xef, 0x94, 0x1e,
0xa1, 0xcc, 0x0b, 0x17, 0x79, 0xd1, 0xd8, 0xfb, 0x0e, 0xb5, 0x87, 0x1e, 0x0b, 0xb5, 0x5c, 0x45,
0xae, 0xe6, 0x71, 0xae, 0xef, 0xd0, 0xa6, 0xc7, 0xc2, 0x72, 0x0f, 0x8a, 0xc9, 0x56, 0x91, 0x0a,
0xf2, 0x15, 0x11, 0xcb, 0x91, 0xc7, 0xd1, 0x2f, 0xfa, 0x17, 0x32, 0x53, 0x67, 0x38, 0x11, 0x6b,
0x51, 0xa8, 0x1f, 0xae, 0xdd, 0x61, 0x2c, 0xe2, 0xee, 0xa5, 0xfe, 0x97, 0xca, 0x97, 0x00, 0x71,
0x19, 0x2b, 0x40, 0xff, 0xb9, 0x0e, 0x7a, 0xb0, 0x02, 0x34, 0xca, 0x4f, 0x40, 0xea, 0x1f, 0x53,
0xb0, 0x79, 0xcd, 0x89, 0x4e, 0xe2, 0xa1, 0x4a, 0x9c, 0x99, 0x3f, 0xd6, 0xc0, 0xdc, 0x6d, 0x9a,
0xa9, 0x1f, 0x9b, 0xa6, 0x7c, 0xb7, 0x69, 0xfe, 0x24, 0xc6, 0xf5, 0x0f, 0x12, 0xec, 0x2c, 0xdd,
0x26, 0x9d, 0x7a, 0xa1, 0xc3, 0xdf, 0xc3, 0x31, 0xec, 0xc5, 0x2a, 0x38, 0x88, 0xd7, 0x64, 0x2e,
0x87, 0xbb, 0xee, 0x9a, 0x47, 0xf4, 0x3a, 0xd2, 0xd0, 0xb9, 0x26, 0x0a, 0x63, 0xbd, 0x20, 0xfe,
0x0a, 0x30, 0x9e, 0xf4, 0x87, 0x9e, 0x6b, 0x47, 0x9d, 0xa4, 0x79, 0x4e, 0x5e, 0x9c, 0x9c, 0x93,
0x99, 0xfe, 0x4e, 0x82, 0xfd, 0x65, 0x69, 0x98, 0xbc, 0x9d, 0x10, 0x16, 0x76, 0xfd, 0x67, 0xbe,
0xb7, 0xee, 0xb5, 0xce, 0x65, 0x8a, 0x3a, 0x23, 0xc1, 0x41, 0x9e, 0xcb, 0x94, 0xe5, 0x8c, 0xc8,
0xfa, 0x1a, 0x6e, 0xaa, 0x7d, 0xfa, 0x1b, 0xb5, 0xd7, 0x3f, 0x49, 0x70, 0xb4, 0xba, 0x0e, 0x4c,
0xd8, 0xd8, 0xa7, 0x8c, 0xac, 0xa9, 0xe7, 0x3e, 0xe4, 0x97, 0x38, 0xb7, 0xac, 0x49, 0x82, 0x41,
0x1c, 0x27, 0xa0, 0x32, 0x28, 0x91, 0x14, 0x8e, 0x43, 0x22, 0x6a, 0x56, 0xf0, 0xd2, 0x8e, 0x89,
0x4e, 0x27, 0x88, 0xee, 0x67, 0x39, 0xf6, 0xf1, 0xd7, 0x00, 0x00, 0x00, 0xff, 0xff, 0x16, 0xf8,
0x6e, 0x5b, 0xfd, 0x06, 0x00, 0x00,
// 850 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xef, 0x8e, 0xdb, 0x44,
0x10, 0xef, 0xc6, 0x71, 0xe2, 0x4c, 0xd2, 0x3b, 0xdf, 0xde, 0xb5, 0x75, 0xaf, 0xa2, 0x0d, 0x16,
0x48, 0x41, 0x88, 0x20, 0x52, 0x21, 0x21, 0x04, 0x85, 0x70, 0x58, 0xc5, 0x34, 0xe7, 0xb4, 0x9b,
0x1c, 0x08, 0xbe, 0x58, 0x8e, 0xb3, 0x94, 0x55, 0x13, 0xdb, 0x78, 0x9d, 0x93, 0xf2, 0x00, 0x48,
0x3c, 0x02, 0x12, 0xdf, 0x79, 0x27, 0xbe, 0xf1, 0x28, 0x68, 0x77, 0xe3, 0x3f, 0xb9, 0x4b, 0xda,
0x93, 0x50, 0x3f, 0xc5, 0xb3, 0xbb, 0xf3, 0x9b, 0xdf, 0xfc, 0x66, 0x32, 0x03, 0x47, 0x61, 0xbc,
0x5c, 0xae, 0x22, 0x96, 0x31, 0xca, 0xfb, 0x49, 0x1a, 0x67, 0x31, 0x36, 0xe4, 0xcf, 0x6c, 0xf5,
0xcb, 0xe9, 0x71, 0xf8, 0x6b, 0x90, 0xf9, 0x6c, 0x4e, 0xa3, 0x8c, 0x65, 0x6b, 0x75, 0x6d, 0x5f,
0x82, 0xfe, 0x34, 0x0d, 0xa2, 0x0c, 0xbf, 0x0b, 0x9d, 0xdc, 0x79, 0xed, 0xb3, 0xb9, 0x85, 0xba,
0xa8, 0xd7, 0x21, 0xed, 0xe2, 0xcc, 0x9d, 0xe3, 0x07, 0xd0, 0x5a, 0xd2, 0xe5, 0x8c, 0xa6, 0xe2,
0xbe, 0x26, 0xef, 0x0d, 0x75, 0xe0, 0xce, 0xf1, 0x3d, 0x68, 0x6e, 0xf0, 0x2d, 0xad, 0x8b, 0x7a,
0x2d, 0xd2, 0x10, 0xa6, 0x3b, 0xc7, 0x27, 0xa0, 0x87, 0x8b, 0x38, 0x7c, 0x65, 0xd5, 0xbb, 0xa8,
0x57, 0x27, 0xca, 0xb0, 0xff, 0x40, 0x70, 0x78, 0x96, 0x63, 0x9f, 0x4b, 0x10, 0xfc, 0x29, 0xe8,
0x69, 0xbc, 0xa0, 0xdc, 0x42, 0x5d, 0xad, 0x77, 0x30, 0x78, 0xd4, 0xcf, 0xa9, 0xf7, 0xaf, 0xbc,
0xec, 0x13, 0xf1, 0x8c, 0xa8, 0xd7, 0xf6, 0x13, 0xd0, 0xa5, 0x8d, 0x4d, 0xe8, 0x5c, 0x78, 0xcf,
0xbc, 0xf1, 0x8f, 0x9e, 0x4f, 0xc6, 0x23, 0xc7, 0xbc, 0x85, 0x3b, 0x60, 0x88, 0x2f, 0x7f, 0x38,
0x1a, 0x99, 0x08, 0xdf, 0x81, 0x23, 0x69, 0x9d, 0x0f, 0xbd, 0xe1, 0x53, 0xc7, 0xbf, 0x98, 0x38,
0x64, 0x62, 0xd6, 0xec, 0x7f, 0x11, 0x9c, 0x14, 0x01, 0x9e, 0xd3, 0x74, 0xc9, 0x38, 0x67, 0x71,
0xc4, 0xf1, 0x7d, 0x30, 0x68, 0xc4, 0xfd, 0x38, 0x5a, 0xac, 0xa5, 0x1c, 0x06, 0x69, 0xd2, 0x88,
0x8f, 0xa3, 0xc5, 0x1a, 0x5b, 0xd0, 0x4c, 0x52, 0x76, 0x19, 0x64, 0x54, 0x0a, 0x61, 0x90, 0xdc,
0xc4, 0x5f, 0x42, 0x23, 0x08, 0x43, 0xca, 0xb9, 0x94, 0xe1, 0x60, 0xf0, 0xfe, 0x8e, 0x2c, 0x2a,
0x41, 0xfa, 0x43, 0xf9, 0x98, 0x6c, 0x9c, 0xec, 0x29, 0x34, 0xd4, 0x09, 0xc6, 0x70, 0x90, 0x67,
0x33, 0x3c, 0x3b, 0x73, 0x26, 0x13, 0xf3, 0x16, 0x3e, 0x82, 0xdb, 0xde, 0xd8, 0x3f, 0x77, 0xce,
0xbf, 0x71, 0xc8, 0xe4, 0x3b, 0xf7, 0xb9, 0x89, 0xf0, 0x31, 0x1c, 0xba, 0xde, 0x0f, 0xee, 0x74,
0x38, 0x75, 0xc7, 0x9e, 0x3f, 0xf6, 0x46, 0x3f, 0x99, 0x35, 0x7c, 0x00, 0x30, 0xf6, 0x7c, 0xe2,
0xbc, 0xb8, 0x70, 0x26, 0x53, 0x53, 0xb3, 0xff, 0xd2, 0x2b, 0x29, 0x7e, 0x4b, 0x79, 0x98, 0xb2,
0x24, 0x63, 0x71, 0x54, 0x16, 0x07, 0x55, 0x8a, 0x83, 0x1d, 0x68, 0xaa, 0xba, 0x72, 0xab, 0xd6,
0xd5, 0x7a, 0xed, 0xc1, 0x87, 0x3b, 0x92, 0xa8, 0xc0, 0xf4, 0x55, 0x59, 0xb8, 0x13, 0x65, 0xe9,
0x9a, 0xe4, 0xbe, 0xf8, 0x6b, 0x68, 0x27, 0x65, 0xa6, 0x52, 0x8f, 0xf6, 0xe0, 0xe1, 0xeb, 0xf5,
0x20, 0x55, 0x17, 0x3c, 0x00, 0x23, 0xef, 0x57, 0x4b, 0x97, 0xee, 0x77, 0x2b, 0xee, 0xb2, 0xbf,
0xd4, 0x2d, 0x29, 0xde, 0xe1, 0xaf, 0x40, 0x17, 0x9d, 0xc7, 0xad, 0x86, 0xa4, 0xfe, 0xc1, 0x1b,
0xa8, 0x0b, 0x94, 0x0d, 0x71, 0xe5, 0x27, 0xca, 0x3e, 0x0b, 0x22, 0x7f, 0xc1, 0x78, 0x66, 0x35,
0xbb, 0x5a, 0xaf, 0x45, 0x9a, 0xb3, 0x20, 0x1a, 0x31, 0x9e, 0x61, 0x0f, 0x20, 0x0c, 0x32, 0xfa,
0x32, 0x4e, 0x19, 0xe5, 0x96, 0x21, 0x03, 0xf4, 0xdf, 0x14, 0xa0, 0x70, 0x50, 0x51, 0x2a, 0x08,
0xa7, 0x17, 0xd0, 0xa9, 0x4a, 0x87, 0x4d, 0xd0, 0x5e, 0x51, 0xd5, 0x6c, 0x2d, 0x22, 0x3e, 0xf1,
0xc7, 0xa0, 0x5f, 0x06, 0x8b, 0x95, 0x6a, 0xb3, 0xf6, 0xe0, 0xfe, 0xde, 0xff, 0x04, 0x51, 0xef,
0x3e, 0xaf, 0x7d, 0x86, 0x4e, 0x5f, 0x00, 0x94, 0x69, 0xed, 0x00, 0xfd, 0x68, 0x1b, 0xf4, 0xde,
0x0e, 0x50, 0xe1, 0x5f, 0x85, 0xfc, 0x19, 0x0e, 0xaf, 0x24, 0xb2, 0x03, 0xf7, 0x93, 0x6d, 0xdc,
0x07, 0xbb, 0x70, 0x15, 0xc8, 0xba, 0x82, 0x6d, 0xff, 0x53, 0x83, 0xdb, 0x5b, 0x81, 0xf1, 0x93,
0xb2, 0x01, 0x91, 0x14, 0xf9, 0xbd, 0x3d, 0x14, 0x6f, 0xd6, 0x79, 0xb5, 0xff, 0xd7, 0x79, 0xda,
0x0d, 0x3b, 0xef, 0x11, 0xb4, 0x37, 0xb5, 0x95, 0x13, 0xb4, 0x2e, 0x85, 0xc9, 0xcb, 0x2d, 0x06,
0xe8, 0x29, 0x18, 0x49, 0xcc, 0x99, 0x68, 0x0b, 0xd9, 0xce, 0x3a, 0x29, 0xec, 0xb7, 0xd4, 0x0a,
0xf6, 0x1c, 0x8e, 0xae, 0x69, 0x7f, 0x95, 0x28, 0xba, 0x46, 0x14, 0x43, 0x3d, 0x0a, 0x96, 0x2a,
0x52, 0x8b, 0xc8, 0xef, 0x2d, 0xf2, 0xda, 0x36, 0x79, 0xfb, 0x4f, 0x04, 0xc7, 0x45, 0x18, 0x37,
0xba, 0x64, 0x59, 0x20, 0xc7, 0xcb, 0x63, 0xb8, 0x53, 0x2e, 0x95, 0x79, 0xf9, 0xa7, 0xd8, 0x6c,
0x97, 0x93, 0x70, 0xcf, 0x4c, 0x7a, 0x29, 0x56, 0xd2, 0x66, 0xc5, 0x28, 0x63, 0xff, 0x7e, 0x79,
0x07, 0x20, 0x59, 0xcd, 0x16, 0x2c, 0xf4, 0x85, 0x5e, 0x75, 0xe9, 0xd3, 0x52, 0x27, 0xcf, 0xe8,
0xda, 0xfe, 0x1d, 0xc1, 0xdd, 0x82, 0x1a, 0xa1, 0xbf, 0xad, 0x28, 0xcf, 0xa6, 0xf1, 0xf7, 0x31,
0xdb, 0x37, 0xfc, 0x36, 0x53, 0xbf, 0x92, 0xbf, 0x98, 0xfa, 0x9e, 0x90, 0x60, 0x2f, 0x87, 0xab,
0xcb, 0xb3, 0x7e, 0x6d, 0x79, 0xda, 0x7f, 0x23, 0x78, 0xb8, 0x9b, 0x07, 0xa1, 0x3c, 0x89, 0x23,
0x4e, 0xf7, 0xf0, 0xf9, 0x02, 0x5a, 0x05, 0xce, 0x6b, 0x3a, 0xb9, 0xa2, 0x20, 0x29, 0x1d, 0x44,
0xd5, 0xc4, 0x66, 0x49, 0x32, 0xaa, 0x38, 0x1b, 0xa4, 0xb0, 0x4b, 0xa1, 0xeb, 0x15, 0xa1, 0x67,
0x0d, 0x89, 0xfd, 0xf8, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x77, 0xbe, 0x23, 0x4c, 0x08,
0x00, 0x00,
}

View File

@ -41,12 +41,21 @@ message CommunityDescription {
ChatIdentity identity = 5;
map<string,CommunityChat> chats = 6;
repeated string ban_list = 7;
map<string,CommunityCategory> categories = 8;
}
message CommunityChat {
map<string,CommunityMember> members = 1;
CommunityPermissions permissions = 2;
ChatIdentity identity = 3;
string category_id = 4;
int32 position = 5;
}
message CommunityCategory {
string category_id = 1;
string name = 2;
int32 position = 3;
}
message CommunityInvitation {

View File

@ -0,0 +1,28 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrCreateCommunityCategoryInvalidCommunityID = errors.New("create-community-category: invalid community id")
var ErrCreateCommunityCategoryInvalidName = errors.New("create-community-category: invalid category name")
type CreateCommunityCategory struct {
CommunityID types.HexBytes `json:"communityId"`
CategoryName string `json:"categoryName"`
ChatIDs []string `json:"chatIds"`
}
func (j *CreateCommunityCategory) Validate() error {
if len(j.CommunityID) == 0 {
return ErrCreateCommunityCategoryInvalidCommunityID
}
if len(j.CategoryName) == 0 {
return ErrCreateCommunityCategoryInvalidName
}
return nil
}

View File

@ -0,0 +1,28 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrDeleteCommunityCategoryInvalidCommunityID = errors.New("set-community-chat-category: invalid community id")
var ErrDeleteCommunityCategoryInvalidCategoryID = errors.New("set-community-chat-category: invalid category id")
type DeleteCommunityCategory struct {
CommunityID types.HexBytes `json:"communityId"`
CategoryID string `json:"categoryId"`
}
func (j *DeleteCommunityCategory) Validate() error {
if len(j.CommunityID) == 0 {
return ErrDeleteCommunityCategoryInvalidCommunityID
}
if len(j.CategoryID) == 0 {
return ErrDeleteCommunityCategoryInvalidCategoryID
}
return nil
}

View File

@ -0,0 +1,34 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrEditCommunityCategoryInvalidCommunityID = errors.New("edit-community-category: invalid community id")
var ErrEditCommunityCategoryInvalidCategoryID = errors.New("edit-community-category: invalid category id")
var ErrEditCommunityCategoryInvalidName = errors.New("edit-community-category: invalid category name")
type EditCommunityCategory struct {
CommunityID types.HexBytes `json:"communityId"`
CategoryID string `json:"categoryId"`
CategoryName string `json:"categoryName"`
ChatIDs []string `json:"chatIds"`
}
func (j *EditCommunityCategory) Validate() error {
if len(j.CommunityID) == 0 {
return ErrEditCommunityCategoryInvalidCommunityID
}
if len(j.CategoryID) == 0 {
return ErrEditCommunityCategoryInvalidCategoryID
}
if len(j.CategoryName) == 0 {
return ErrEditCommunityCategoryInvalidName
}
return nil
}

View File

@ -0,0 +1,33 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrReorderCommunityCategoryInvalidCommunityID = errors.New("edit-community-category: invalid community id")
var ErrReorderCommunityCategoryInvalidCategoryID = errors.New("edit-community-category: invalid category id")
var ErrReorderCommunityCategoryInvalidPosition = errors.New("edit-community-category: invalid position")
type ReorderCommunityCategories struct {
CommunityID types.HexBytes `json:"communityId"`
CategoryID string `json:"categoryId"`
Position int `json:"position"`
}
func (j *ReorderCommunityCategories) Validate() error {
if len(j.CommunityID) == 0 {
return ErrReorderCommunityCategoryInvalidCommunityID
}
if len(j.CategoryID) == 0 {
return ErrEditCommunityCategoryInvalidCategoryID
}
if j.Position < 0 {
return ErrReorderCommunityCategoryInvalidPosition
}
return nil
}

View File

@ -0,0 +1,39 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
)
var ErrReorderCommunityChatInvalidCommunityID = errors.New("edit-community-category: invalid community id")
var ErrReorderCommunityChatInvalidCategoryID = errors.New("edit-community-category: invalid category id")
var ErrReorderCommunityChatInvalidChatID = errors.New("edit-community-category: invalid chat id")
var ErrReorderCommunityChatInvalidPosition = errors.New("edit-community-category: invalid position")
type ReorderCommunityChat struct {
CommunityID types.HexBytes `json:"communityId"`
CategoryID string `json:"categoryId"`
ChatID string `json:"chatId"`
Position int `json:"position"`
}
func (j *ReorderCommunityChat) Validate() error {
if len(j.CommunityID) == 0 {
return ErrReorderCommunityChatInvalidCommunityID
}
if len(j.CategoryID) == 0 {
return ErrReorderCommunityChatInvalidCategoryID
}
if len(j.ChatID) == 0 {
return ErrReorderCommunityChatInvalidChatID
}
if j.Position < 0 {
return ErrReorderCommunityCategoryInvalidPosition
}
return nil
}

View File

@ -412,6 +412,31 @@ func (api *PublicAPI) RequestToJoinCommunity(request *requests.RequestToJoinComm
return api.service.messenger.RequestToJoinCommunity(request)
}
// CreateCommunityCategory creates a category within a particular community
func (api *PublicAPI) CreateCommunityCategory(request *requests.CreateCommunityCategory) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateCommunityCategory(request)
}
// ReorderCommunityCategories is used to change the order of the categories of a community
func (api *PublicAPI) ReorderCommunityCategories(request *requests.ReorderCommunityCategories) (*protocol.MessengerResponse, error) {
return api.service.messenger.ReorderCommunityCategories(request)
}
// ReorderCommunityChat allows changing the order of the chat or switching its category
func (api *PublicAPI) ReorderCommunityChat(request *requests.ReorderCommunityChat) (*protocol.MessengerResponse, error) {
return api.service.messenger.ReorderCommunityChat(request)
}
// EditCommunityCategory modifies a category within a particular community
func (api *PublicAPI) EditCommunityCategory(request *requests.EditCommunityCategory) (*protocol.MessengerResponse, error) {
return api.service.messenger.EditCommunityCategory(request)
}
// DeleteCommunityCategory deletes a category within a particular community and removes this category from any chat that has it
func (api *PublicAPI) DeleteCommunityCategory(request *requests.DeleteCommunityCategory) (*protocol.MessengerResponse, error) {
return api.service.messenger.DeleteCommunityCategory(request)
}
type ApplicationMessagesResponse struct {
Messages []*common.Message `json:"messages"`
Cursor string `json:"cursor"`