landed community results when browsing categories - missing pagination buttons, community activity, proper member counts

This commit is contained in:
2025-10-19 23:16:01 -06:00
parent 71da667361
commit e5ad827f24
4 changed files with 188 additions and 6 deletions
+97
View File
@@ -10,6 +10,7 @@
package database
import (
"database/sql"
"errors"
"fmt"
"slices"
@@ -139,6 +140,18 @@ func (c *Community) Host() (*User, error) {
return AmGetUser(*c.HostUid)
}
// HostQ returns the reference to the community's host, quietly.
func (c *Community) HostQ() *User {
if c.HostUid == nil {
return nil
}
u, err := AmGetUser(*c.HostUid)
if err != nil {
return nil
}
return u
}
func (c *Community) LanguageTag() (*language.Tag, error) {
if c.Language == nil {
return nil, nil
@@ -196,6 +209,26 @@ func (c *Community) Membership(u *User) (bool, bool, uint16, error) {
return false, false, uint16(0), err
}
// MemberCountQ returns the number of members in the community, quietly.
func (c *Community) MemberCountQ(hidden bool) int {
var rs *sql.Rows
var err error
if hidden {
rs, err = amdb.Query("SELECT COUNT(*) FROM commmember WHERE commid = ?", c.Id)
} else {
rs, err = amdb.Query("SELECT COUNT(*) FROM commmember WHERE commid = ? AND hidden = 0", c.Id)
}
if err != nil {
return -1
}
if rs.Next() {
var rc int
rs.Scan(&rc)
return rc
}
return -1
}
/* TestPermission is shorthand that tests if a user has a permission with respect to the community.
* Parameters:
* user - The user to be checked.
@@ -620,3 +653,67 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
fmt.Sprintf("name=%s", comm.Name), fmt.Sprintf("alias=%s", comm.Alias))
return comm, nil
}
/* AmGetCommunitiesForCategory returns a list of communities for the specified category.
* Parameters:
* catid - Category ID to search for.
* offset - Number of communities to skip at beginning of list.
* max - Maximum number of communities to return.
* showAll - Include communities that are "hidden in directory."
* Returns:
* Array of Community pointers representing the return elements.
* The total number of communities matching this query (could be greater than max)
* Standard Go error status.
*/
func AmGetCommunitiesForCategory(catid int32, offset int, max int, showAll bool) ([]*Community, int, error) {
var rs *sql.Rows
var err error
if showAll {
rs, err = amdb.Query("SELECT COUNT(*) FROM communities WHERE catid = ?", catid)
} else {
rs, err = amdb.Query("SELECT COUNT(*) FROM communities WHERE catid = ? AND hide_dir = 0", catid)
}
if err != nil {
return nil, -1, err
}
if !rs.Next() {
return nil, -1, errors.New("internal error getting total match count")
}
var total int
rs.Scan(&total)
if total == 0 {
return make([]*Community, 0), 0, nil // short-circuit return
}
if showAll {
if offset > 0 {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? ORDER BY commname LIMIT ? OFFSET ?",
catid, max, offset)
} else {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? ORDER BY commname LIMIT ?", catid, max)
}
} else {
if offset > 0 {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? AND hide_dir = 0 ORDER BY commname LIMIT ? OFFSET ?",
catid, max, offset)
} else {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? AND hide_dir = 0 ORDER BY commname LIMIT ?", catid, max)
}
}
if err != nil {
return nil, total, err
}
rcCap := max
if rcCap > 10000 {
rcCap = 10000
}
rc := make([]*Community, 0, rcCap)
for rs.Next() {
var commid int32
rs.Scan(&commid)
c, err := AmGetCommunity(commid)
if err == nil {
rc = append(rc, c)
}
}
return rc, total, nil
}
+36 -4
View File
@@ -10,6 +10,7 @@
package main
import (
"fmt"
"strconv"
"git.erbosoft.com/amy/amsterdam/database"
@@ -17,7 +18,7 @@ import (
)
// loadCategoryInformation loads the current category information to the context.
func loadCategoryInformation(ctxt ui.AmContext) error {
func loadCategoryInformation(ctxt ui.AmContext, offset int) error {
u := ctxt.CurrentUser()
catid := int32(-1)
p := ctxt.Parameter("catid")
@@ -42,7 +43,8 @@ func loadCategoryInformation(ctxt ui.AmContext) error {
}
ctxt.SetSession("find.catid", catid)
ctxt.VarMap().Set("catid", catid)
ctxt.VarMap().Set("showHiddenCat", database.AmTestPermission("Global.ShowHiddenCategories", u.BaseLevel))
showHidden := database.AmTestPermission("Global.ShowHiddenCategories", u.BaseLevel)
ctxt.VarMap().Set("showHiddenCat", showHidden)
hier, err := database.AmGetCategoryHierarchy(catid)
if err != nil {
return err
@@ -53,7 +55,29 @@ func loadCategoryInformation(ctxt ui.AmContext) error {
return err
}
ctxt.VarMap().Set("catSubs", subs)
// TODO: set matching communities as well
ctxt.VarMap().Set("displayCats", true)
if catid > -1 {
// search for communities in this category
listMax := int(ctxt.Globals().MaxSearchPage)
commList, numComm, err := database.AmGetCommunitiesForCategory(catid, offset*listMax, listMax, showHidden)
if err != nil {
return err
}
if len(commList) == 0 {
ctxt.VarMap().Set("resultHeader", "Communities in Category (None)")
} else {
ctxt.VarMap().Set("resultHeader", fmt.Sprintf("Communities in Category (Displaying %d-%d of %d)",
offset*listMax+1, offset*listMax+len(commList), numComm))
ctxt.VarMap().Set("resultList", commList)
if offset > 0 {
ctxt.VarMap().Set("resultShowPrev", true)
}
if offset*listMax+len(commList) < numComm {
ctxt.VarMap().Set("resultShowNext", true)
}
}
}
return nil
}
@@ -79,6 +103,14 @@ func FindPage(ctxt ui.AmContext) (string, any, error) {
if mode == "" {
mode = "COM"
}
ofs := 0
p = ctxt.Parameter("ofs")
if p != "" {
v, err := strconv.Atoi(p)
if err == nil {
ofs = v
}
}
ctxt.SetSession("find.mode", mode)
ctxt.VarMap().Set("mode", mode)
switch mode {
@@ -86,7 +118,7 @@ func FindPage(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("field", "name")
ctxt.VarMap().Set("oper", "st")
ctxt.VarMap().Set("term", "")
err := loadCategoryInformation(ctxt)
err := loadCategoryInformation(ctxt, ofs)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
+1 -1
View File
@@ -64,7 +64,7 @@
<strong>Category:</strong>
{{ range i := categories }}
{{ if i > 0 }}: {{ end }}
<a href="/TODO/find/communities-for-category"
<a href="/find?mode=COM&catid={{ .CatId }}"
class="text-blue-700 hover:text-blue-900">{{ .Name }}</a>
{{ end }}
</div>
+54 -1
View File
@@ -139,7 +139,7 @@
</div>
<!-- Category Listing -->
{{ if mode == "COM" }}
{{ if isset(displayCats) }}
<div class="max-w-3xl">
<hr class="border-gray-400 mb-4">
@@ -181,4 +181,57 @@
{{ end }}
</div>
{{ end }}
{{ if isset(resultHeader) }}
<!-- Search results -->
<hr class="border-gray-400 mb-4">
<div class="flex justify-between items-center mb-4">
<div class="text-sm text-black font-bold">{{ resultHeader }}</div>
</div>
{{ if isset(resultList) }}
<!-- Results List -->
<div class="bg-gray-50 p-6 rounded-lg">
<div class="space-y-4">
{{ range _, rx := resultList }}
{{ if mode == "COM" }}
<!-- Community Result -->
<div class="flex items-start gap-3">
<span class="text-sm pt-0.5 flex-shrink-0">🟣</span>
<div class="flex-1">
<div class="mb-2">
<a href="/comm/{{ rx.Alias }}/profile"
class="text-blue-700 hover:text-blue-900 font-bold text-base">{{ rx.Name }}</a>
</div>
<div class="text-sm text-gray-700 space-y-1">
<div>
<span class="font-medium">Host:</span>
{{ h := rx.HostQ() }}
<a href="/user/{{ h.Username }}" class="text-blue-700 hover:text-blue-900">{{ h.Username }}</a>
<span class="mx-2">-</span>
{{ n := rx.MemberCountQ(false) }}
{{ if n == 1 }}
<span>1 member</span>
{{ else }}
<span>{{ n }} members</span>
{{ end }}
</div>
<div>
<span class="font-medium">Latest activity:</span> [Today, 9:52:48 PM]
</div>
<div class="italic text-gray-600 mt-2">{{ rx.Synopsis }}</div>
</div>
</div>
</div>
{{ else if mode == "USR" }}
TODO: I don't know USR yet
{{ else if mode == "CAT" }}
TODO: I don't know CAT yet
{{ else if mode == "PST" }}
TODO: I don't know PST yet
{{ end }}
{{ end }}
</div>
</div>
{{ end }}
{{ end }}
</div>