Files
amsterdam/ui/topic_traverser.go
T

101 lines
2.5 KiB
Go

/*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Package ui holds the support for the Amsterdam user interface, wrapping Echo and Jet templates.
package ui
import (
"sync"
"git.erbosoft.com/amy/amsterdam/database"
"github.com/bits-and-blooms/bitset"
)
// TopicTraverser is the data structure that allows us to navigate to "next" topics.
type TopicTraverser interface {
FirstTopic() int16
NextTopic(int16) int16
ClearTopic(int16)
UnclearTopic(int16)
}
// topicTraverser is the internal data structure that implements TopicTraverser.
type topicTraverser struct {
lock sync.RWMutex
topics []int16
active *bitset.BitSet
}
// NewTopicTraverser creates the traverser data structure from the topic listing.
func NewTopicTraverser(topics []*database.TopicSummary) TopicTraverser {
trav := topicTraverser{
topics: make([]int16, 0, len(topics)),
active: bitset.New(uint(len(topics))),
}
p := 0
for _, t := range topics {
if t.Unread > 0 {
trav.topics = append(trav.topics, t.Number)
trav.active.Set(uint(p))
p++
}
}
return &trav
}
// FirstTopic returns the first unread topic number in the traverser.
func (trav *topicTraverser) FirstTopic() int16 {
trav.lock.RLock()
defer trav.lock.RUnlock()
i, b := trav.active.NextSet(0)
if b {
return trav.topics[i]
}
return -1
}
// NextTopic returns the unread topic number in the traverser after the specified one.
func (trav *topicTraverser) NextTopic(cur int16) int16 {
trav.lock.RLock()
defer trav.lock.RUnlock()
seeking := false
for i, v := range trav.topics {
if v == cur {
seeking = true
} else if seeking && trav.active.Test(uint(i)) {
return v
}
}
return trav.FirstTopic() // look from the beginning
}
// ClearTopic clears the specified topic number from the traverser.
func (trav *topicTraverser) ClearTopic(num int16) {
trav.lock.Lock()
defer trav.lock.Unlock()
for i, v := range trav.topics {
if v == num {
trav.active.Clear(uint(i))
return
}
}
}
// UnclearTopic restores the specified topic number to the traverser.
func (trav *topicTraverser) UnclearTopic(num int16) {
trav.lock.Lock()
defer trav.lock.Unlock()
for i, v := range trav.topics {
if v == num {
trav.active.Set(uint(i))
return
}
}
}