212 lines
6.3 KiB
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
|
|
}
|