owpengram-server/internal/store/memory/chatlists.go

212 lines
6.3 KiB
Go

package memory
import (
"context"
"sort"
"sync"
"telesrv/internal/domain"
)
type ChatlistStore struct {
mu sync.RWMutex
nextInvite int64
invites map[string]domain.ChatlistInvite
memberships map[chatlistMembershipKey]domain.ChatlistMembership
}
type chatlistMembershipKey struct {
userID int64
localFilterID int
}
// NewChatlistStore creates an in-memory ChatlistStore.
func NewChatlistStore() *ChatlistStore {
return &ChatlistStore{
nextInvite: 1,
invites: make(map[string]domain.ChatlistInvite),
memberships: make(map[chatlistMembershipKey]domain.ChatlistMembership),
}
}
func (s *ChatlistStore) CountInvites(_ context.Context, ownerUserID int64, filterID int) (int, error) {
s.mu.RLock()
defer s.mu.RUnlock()
count := 0
for _, invite := range s.invites {
if invite.OwnerUserID == ownerUserID && invite.FilterID == filterID && !invite.Deleted {
count++
}
}
return count, nil
}
func (s *ChatlistStore) CountActiveInvites(_ context.Context, ownerUserID int64, filterID int) (int, error) {
s.mu.RLock()
defer s.mu.RUnlock()
count := 0
for _, invite := range s.invites {
if invite.OwnerUserID == ownerUserID && invite.FilterID == filterID && !invite.Deleted && !invite.Revoked {
count++
}
}
return count, nil
}
func (s *ChatlistStore) SaveInvite(_ context.Context, invite domain.ChatlistInvite) (domain.ChatlistInvite, error) {
s.mu.Lock()
defer s.mu.Unlock()
if invite.Slug == "" {
return domain.ChatlistInvite{}, domain.ErrChatlistInviteInvalid
}
if existing, ok := s.invites[invite.Slug]; ok && (existing.OwnerUserID != invite.OwnerUserID || existing.FilterID != invite.FilterID) {
return domain.ChatlistInvite{}, domain.ErrChatlistSlugOccupied
}
if invite.ID == 0 {
if existing, ok := s.invites[invite.Slug]; ok {
invite.ID = existing.ID
if invite.Date == 0 {
invite.Date = existing.Date
}
} else {
invite.ID = s.nextInvite
s.nextInvite++
}
}
invite.Deleted = false
s.invites[invite.Slug] = cloneChatlistInvite(invite)
return cloneChatlistInvite(invite), nil
}
func (s *ChatlistStore) GetInvite(_ context.Context, ownerUserID int64, filterID int, slug string) (domain.ChatlistInvite, bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()
invite, ok := s.invites[slug]
if !ok || invite.Deleted || invite.OwnerUserID != ownerUserID || invite.FilterID != filterID {
return domain.ChatlistInvite{}, false, nil
}
return cloneChatlistInvite(invite), true, nil
}
func (s *ChatlistStore) GetInviteBySlug(_ context.Context, slug string) (domain.ChatlistInvite, bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()
invite, ok := s.invites[slug]
if !ok || invite.Deleted || invite.Revoked {
return domain.ChatlistInvite{}, false, nil
}
return cloneChatlistInvite(invite), true, nil
}
func (s *ChatlistStore) ListInvites(_ context.Context, ownerUserID int64, filterID int) ([]domain.ChatlistInvite, error) {
s.mu.RLock()
defer s.mu.RUnlock()
out := make([]domain.ChatlistInvite, 0)
for _, invite := range s.invites {
if invite.OwnerUserID == ownerUserID && invite.FilterID == filterID && !invite.Deleted {
out = append(out, cloneChatlistInvite(invite))
}
}
sort.SliceStable(out, func(i, j int) bool {
if out[i].Date != out[j].Date {
return out[i].Date < out[j].Date
}
return out[i].Slug < out[j].Slug
})
return out, nil
}
func (s *ChatlistStore) DeleteInvite(_ context.Context, ownerUserID int64, filterID int, slug string) (bool, error) {
s.mu.Lock()
defer s.mu.Unlock()
invite, ok := s.invites[slug]
if !ok || invite.Deleted || invite.OwnerUserID != ownerUserID || invite.FilterID != filterID {
return false, nil
}
invite.Deleted = true
s.invites[slug] = invite
return true, nil
}
func (s *ChatlistStore) CountMemberships(_ context.Context, userID int64) (int, error) {
s.mu.RLock()
defer s.mu.RUnlock()
count := 0
for key := range s.memberships {
if key.userID == userID {
count++
}
}
return count, nil
}
func (s *ChatlistStore) SaveMembership(_ context.Context, membership domain.ChatlistMembership) error {
s.mu.Lock()
defer s.mu.Unlock()
if membership.UserID == 0 || membership.LocalFilterID == 0 || membership.Slug == "" {
return domain.ErrChatlistInvalid
}
key := chatlistMembershipKey{userID: membership.UserID, localFilterID: membership.LocalFilterID}
for existingKey, existing := range s.memberships {
if existingKey != key && existing.UserID == membership.UserID && existing.Slug == membership.Slug {
delete(s.memberships, existingKey)
}
}
s.memberships[key] = cloneChatlistMembership(membership)
return nil
}
func (s *ChatlistStore) GetMembershipBySlug(_ context.Context, userID int64, slug string) (domain.ChatlistMembership, bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()
for _, membership := range s.memberships {
if membership.UserID == userID && membership.Slug == slug {
return cloneChatlistMembership(membership), true, nil
}
}
return domain.ChatlistMembership{}, false, nil
}
func (s *ChatlistStore) GetMembershipByLocalFilter(_ context.Context, userID int64, localFilterID int) (domain.ChatlistMembership, bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()
membership, ok := s.memberships[chatlistMembershipKey{userID: userID, localFilterID: localFilterID}]
if !ok {
return domain.ChatlistMembership{}, false, nil
}
return cloneChatlistMembership(membership), true, nil
}
func (s *ChatlistStore) DeleteMembershipByLocalFilter(_ context.Context, userID int64, localFilterID int) (bool, error) {
s.mu.Lock()
defer s.mu.Unlock()
key := chatlistMembershipKey{userID: userID, localFilterID: localFilterID}
if _, ok := s.memberships[key]; !ok {
return false, nil
}
delete(s.memberships, key)
return true, nil
}
func (s *ChatlistStore) SetMembershipHidden(_ context.Context, userID int64, localFilterID int, hidden bool) (bool, error) {
s.mu.Lock()
defer s.mu.Unlock()
key := chatlistMembershipKey{userID: userID, localFilterID: localFilterID}
membership, ok := s.memberships[key]
if !ok {
return false, nil
}
changed := membership.HiddenUpdates != hidden
membership.HiddenUpdates = hidden
s.memberships[key] = membership
return changed, nil
}
func cloneChatlistInvite(invite domain.ChatlistInvite) domain.ChatlistInvite {
invite.Peers = append([]domain.DialogFolderPeer(nil), invite.Peers...)
return invite
}
func cloneChatlistMembership(membership domain.ChatlistMembership) domain.ChatlistMembership {
return membership
}