Landed the topic list page (no topics yet, so appearances are deceiving)

This commit is contained in:
2025-10-28 16:32:05 -06:00
parent 86540e00b1
commit 086954f7b0
9 changed files with 443 additions and 5 deletions
+23
View File
@@ -10,6 +10,7 @@
package database
import (
"errors"
"fmt"
"sync"
"time"
@@ -204,6 +205,28 @@ func AmGetConferenceByAlias(alias string) (*Conference, error) {
return AmGetConference(int32(confid.(int)))
}
/* AmGetConferenceByAliasInCommunity returns a conference in a community given its alias.
* Parameters:
* cid - The community to look inside.
* alias - The alias to look up.
* Returns:
* Pointer to the conference, or nil.
* Standard Go error status.
*/
func AmGetConferenceByAliasInCommunity(cid int32, alias string) (*Conference, error) {
rs, err := amdb.Query(`SELECT c.confid FROM commtoconf c, confalias a WHERE c.confid = a.confid
AND c.commid = ? AND a.alias = ?`, cid, alias)
if err != nil {
return nil, err
}
if !rs.Next() {
return nil, errors.New("conference not found")
}
var confid int32
rs.Scan(&confid)
return AmGetConference(confid)
}
/* AmGetCommunityConferences returns all conferences for a given community.
* Parameters:
* cid - Community ID to get conferences for.
+160 -1
View File
@@ -9,8 +9,13 @@
// The database package contains database management and storage logic.
package database
import "time"
import (
"errors"
"strings"
"time"
)
// Topic is the top-level structure detailing topics.
type Topic struct {
TopicId int32 `db:"topicid"`
ConfId int32 `db:"confid"`
@@ -25,6 +30,7 @@ type Topic struct {
Name string `db:"name"`
}
// TopicSettings contains per-user settings for topics, including the "last read" message pointer.
type TopicSettings struct {
TopicId int32 `db:"topicid"`
Uid int32 `db:"uid"`
@@ -34,3 +40,156 @@ type TopicSettings struct {
LastPost *time.Time `db:"last_post"`
Subscribe bool `db:"subscribe"`
}
// TopicSummary is a smaller data structure that gets topic information to create the topic list display.
type TopicSummary struct {
TopicID int32
Number int16
Name string
Unread int32
Total int32
LastUpdate time.Time
Frozen bool
Archived bool
Subscribed bool
}
// View and sort constants for AmListTopics.
const (
TopicViewAll = 0
TopicViewNew = 1
TopicViewActive = 2
TopicViewAllVisible = 3
TopicViewHidden = 4
TopicViewArchive = 5
TopicSortID = 0
TopicSortNumber = 1
TopicSortName = 2
TopicSortUnread = 3
TopicSortTotal = 4
TopicSortDate = 5
)
/* AmListTopics produces a list of topic summary information according to specific options.
* Parameters:
* confid - The ID of the conference to list topics in.
* uid - The UID of the user to consider the settings of.
* viewOption - One of the following constants:
* TopicViewAll - List all topics.
* TopicViewNew - List only visible topics with new messages.
* TopicViewActive - List only visible topics, with "active" ones coming first.
* TopicViewAllVisible - List only visible topics.
* TopicViewHidden - List only hidden topics (including archived ones).
* TopicViewArchive - List only archived, non-hidden topics.
* sortOption - One of the following constants:
* TopicSortID - Sort by topic ID.
* TopicSortNumber - Sort by topic number in the conference. May be negated to sort in reverse order.
* TopicSortName - Sort by topic name. May be negated to sort in reverse order.
* TopicSortUnread - Sort by number of unread messages. May be negated to sort in reverse order.
* TopicSortTotal - Sort by total number of messages. May be negated to sort in reverse order.
* TopicSortDate - Sort by last topic update date. May be negated to sort in reverse order.
* ignoreSticky - If false, sticky topics will precede nonsticky ones; if true, stickiness is ignored.
* Returns:
* List of TopicSummary pointers.
* Standard Go error status.
*/
func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignoreSticky bool) ([]*TopicSummary, error) {
// Decode the viewOption into a WHERE clause.
var whereClause string
switch viewOption {
case TopicViewAll:
whereClause = ""
case TopicViewNew:
tail := "t.top_message > IFNULL(s.last_message,-1)"
if !ignoreSticky {
tail = "(t.sticky = 1 OR " + tail + ")"
}
whereClause = "t.archived = 0 AND IFNULL(s.hidden,0) = 0 AND " + tail
case TopicViewActive, TopicViewAllVisible:
whereClause = "t.archived = 0 AND IFNULL(s.hidden,0) = 0"
case TopicViewHidden:
whereClause = "IFNULL(s.hidden,0) = 1"
case TopicViewArchive:
whereClause = "t.archived = 1 AND IFNULL(s.hidden,0) = 0"
default:
return nil, errors.New("invalid view option specified")
}
// Decode the sortOption into an ORDER BY clause.
var reverse bool = false
if sortOption < 0 {
reverse = true
sortOption = -sortOption
}
var orderByClause string
switch sortOption {
case TopicSortID:
orderByClause = "t.topicid ASC"
case TopicSortNumber:
if reverse {
orderByClause = "t.num DESC"
} else {
orderByClause = "t.num ASC"
}
case TopicSortName:
if reverse {
orderByClause = "t.name DESC, t.num DESC"
} else {
orderByClause = "t.name ASC, t.num ASC"
}
case TopicSortUnread:
if reverse {
orderByClause = "unread ASC, t.num DESC"
} else {
orderByClause = "unread DESC, t.num ASC"
}
case TopicSortTotal:
if reverse {
orderByClause = "total ASC, t.num DESC"
} else {
orderByClause = "total DESC, t.num ASC"
}
case TopicSortDate:
if reverse {
orderByClause = "t.lastupdate ASC, t.num DESC"
} else {
orderByClause = "t.lastupdate DESC, t.num ASC"
}
default:
return nil, errors.New("invalid sort option specified")
}
// Build the full SQL statement
var fullStatement strings.Builder
fullStatement.WriteString("SELECT t.topicid, t.num, t.name, (t.top_message - IFNULL(s.last_message,-1)) AS unread, ")
fullStatement.WriteString("(t.top_message + 1) AS total, t.lastupdate, t.frozen, t.archived, IFNULL(s.subscribe,0) AS subscribe, ")
fullStatement.WriteString("t.sticky, GREATEST(SIGN(t.top_message - IFNULL(s.last_message,-1)),0) AS newflag ")
fullStatement.WriteString("FROM topics t LEFT JOIN topicsettings s ON t.topicid = s.topicid AND s.uid = ? WHERE t.confid = ? ")
if whereClause != "" {
fullStatement.WriteString("AND ")
fullStatement.WriteString(whereClause)
}
fullStatement.WriteString(" ORDER BY ")
if ignoreSticky {
fullStatement.WriteString("t.sticky DESC, ")
}
if viewOption == TopicViewActive {
fullStatement.WriteString("newflag DESC, ")
}
fullStatement.WriteString(orderByClause)
// Execute and capture results
rs, err := amdb.Query(fullStatement.String(), uid, confid)
if err != nil {
return nil, err
}
rc := make([]*TopicSummary, 0)
for rs.Next() {
var rec TopicSummary
rs.Scan(&rec.TopicID, &rec.Number, &rec.Name, &rec.Unread, &rec.Total, &rec.LastUpdate, &rec.Frozen, &rec.Archived,
&rec.Subscribed)
rc = append(rc, &rec)
}
return rc, nil
}