patched up community display in find.jet

This commit is contained in:
2025-10-20 22:16:10 -06:00
parent 8b00cfce1f
commit e6f157c337
6 changed files with 139 additions and 13 deletions
+9
View File
@@ -239,6 +239,9 @@ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) {
flags.Set(database.CommunityFlagPicturesInPosts, dlg.Field("pic_in_post").IsChecked()) flags.Set(database.CommunityFlagPicturesInPosts, dlg.Field("pic_in_post").IsChecked())
err = comm.SaveFlags(flags) err = comm.SaveFlags(flags)
} }
if err == nil {
err = comm.TouchUpdate()
}
if err != nil { if err != nil {
ctxt.ClearCommunityContext() ctxt.ClearCommunityContext()
return dlg.RenderError(ctxt, err.Error()) return dlg.RenderError(ctxt, err.Error())
@@ -321,6 +324,9 @@ func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) {
photourl := fmt.Sprintf("/img/store/%d", img.ImgId) photourl := fmt.Sprintf("/img/store/%d", img.ImgId)
ci.PhotoURL = &photourl ci.PhotoURL = &photourl
_, err = ci.Save() _, err = ci.Save()
if err == nil {
err = comm.TouchUpdate()
}
if err == nil { if err == nil {
return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil
} }
@@ -457,6 +463,9 @@ func CreateCommunity(ctxt ui.AmContext) (string, any, error) {
if err == nil { if err == nil {
err = comm.SetContactID(ci.ContactId) err = comm.SetContactID(ci.ContactId)
} }
if err == nil {
err = comm.TouchUpdate()
}
if err != nil { if err != nil {
return dlg.RenderError(ctxt, err.Error()) return dlg.RenderError(ctxt, err.Error())
} }
+41 -6
View File
@@ -209,8 +209,8 @@ func (c *Community) Membership(u *User) (bool, bool, uint16, error) {
return false, false, uint16(0), err return false, false, uint16(0), err
} }
// MemberCountQ returns the number of members in the community, quietly. // MemberCount returns the number of members in the community, quietly.
func (c *Community) MemberCountQ(hidden bool) int { func (c *Community) MemberCount(hidden bool) (int, error) {
var rs *sql.Rows var rs *sql.Rows
var err error var err error
if hidden { if hidden {
@@ -219,14 +219,14 @@ func (c *Community) MemberCountQ(hidden bool) int {
rs, err = amdb.Query("SELECT COUNT(*) FROM commmember WHERE commid = ? AND hidden = 0", c.Id) rs, err = amdb.Query("SELECT COUNT(*) FROM commmember WHERE commid = ? AND hidden = 0", c.Id)
} }
if err != nil { if err != nil {
return -1 return -1, err
} }
if rs.Next() { if rs.Next() {
var rc int var rc int
rs.Scan(&rc) rs.Scan(&rc)
return rc return rc, nil
} }
return -1 return -1, errors.New("internal error reading member count")
} }
/* TestPermission is shorthand that tests if a user has a permission with respect to the community. /* TestPermission is shorthand that tests if a user has a permission with respect to the community.
@@ -254,7 +254,7 @@ func (c *Community) TestPermission(perm string, level uint16) bool {
} }
} }
// PermissionLevel returns trhe permission level for a permission name. // PermissionLevel returns the permission level for a permission name.
func (c *Community) PermissionLevel(perm string) uint16 { func (c *Community) PermissionLevel(perm string) uint16 {
switch perm { switch perm {
case "Community.Read": case "Community.Read":
@@ -348,6 +348,41 @@ func (c *Community) SetContactID(cid int32) error {
return nil return nil
} }
// Touch updates the last access time of the community.
func (c *Community) Touch() error {
c.Mutex.Lock()
defer c.Mutex.Unlock()
_, err := amdb.Exec("UPDATE communities SET lastaccess = NOW() WHERE commid = ?", c.Id)
if err == nil {
rs, err := amdb.Query("SELECT lastaccess FROM communities WHERE commid = ?", c.Id)
if err == nil {
rs.Next()
var na time.Time
rs.Scan(&na)
c.LastAccess = &na
}
}
return err
}
// TouchUpdate updates the last access and last update times of the community.
func (c *Community) TouchUpdate() error {
c.Mutex.Lock()
defer c.Mutex.Unlock()
_, err := amdb.Exec("UPDATE communities SET lastaccess = NOW(), lastupdate = NOW() WHERE commid = ?", c.Id)
if err == nil {
rs, err := amdb.Query("SELECT lastaccess, lastupdate FROM communities WHERE commid = ?", c.Id)
if err != nil {
rs.Next()
var na, nu time.Time
rs.Scan(&na, &nu)
c.LastAccess = &na
c.LastUpdate = &nu
}
}
return err
}
/* AmGetCommunity returns a reference to the specified community. /* AmGetCommunity returns a reference to the specified community.
* Parameters: * Parameters:
* id - The ID of the community. * id - The ID of the community.
+3
View File
@@ -284,6 +284,9 @@ func (c *amContext) SubRender(name string) ([]byte, error) {
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err = view.Execute(buf, c.VarMap(), c) err = view.Execute(buf, c.VarMap(), c)
if err != nil {
log.Errorf("template \"%s\" failed subrender exec: %v", name, err)
}
return buf.Bytes(), err return buf.Bytes(), err
} }
+40 -5
View File
@@ -107,6 +107,35 @@ func extractCommunityLogo(a jet.Arguments) reflect.Value {
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
} }
func displayActivity(a jet.Arguments) reflect.Value {
timeval := a.Get(0).Convert(reflect.TypeFor[*time.Time]()).Interface().(*time.Time)
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
prefs, err := ctxt.CurrentUser().Prefs()
if err == nil {
return reflect.ValueOf(util.AmActivityString(timeval, prefs.Localizer()))
}
return reflect.ValueOf(fmt.Sprintf("<<%v>>", err))
}
func displayMemberCount(a jet.Arguments) reflect.Value {
showHidden := false
comm := a.Get(0).Convert(reflect.TypeFor[*database.Community]()).Interface().(*database.Community)
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
level := ctxt.CurrentUser().BaseLevel
mbr, _, clevel, err := comm.Membership(ctxt.CurrentUser())
if err == nil {
if mbr && clevel > level {
level = clevel
}
showHidden = comm.TestPermission("Community.ShowHiddenMembers", level)
}
count, err := comm.MemberCount(showHidden)
if err != nil {
return reflect.ValueOf(-1)
}
return reflect.ValueOf(count)
}
// SetupTemplates is called to set up the template renderer after the configuration is loaded. // SetupTemplates is called to set up the template renderer after the configuration is loaded.
func SetupTemplates() { func SetupTemplates() {
views = jet.NewSet( views = jet.NewSet(
@@ -122,17 +151,19 @@ func SetupTemplates() {
views.AddGlobalFunc("MakeIntRange", makeIntRange) views.AddGlobalFunc("MakeIntRange", makeIntRange)
views.AddGlobalFunc("MakeYearRange", makeYearRange) views.AddGlobalFunc("MakeYearRange", makeYearRange)
views.AddGlobalFunc("ExtractCommunityLogo", extractCommunityLogo) views.AddGlobalFunc("ExtractCommunityLogo", extractCommunityLogo)
views.AddGlobalFunc("DisplayActivity", displayActivity)
views.AddGlobalFunc("DisplayMemberCount", displayMemberCount)
views.AddGlobalFunc("GetCountryList", func(a jet.Arguments) reflect.Value { views.AddGlobalFunc("GetCountryList", func(jet.Arguments) reflect.Value {
return reflect.ValueOf(util.AmCountryList()) return reflect.ValueOf(util.AmCountryList())
}) })
views.AddGlobalFunc("GetLanguageList", func(a jet.Arguments) reflect.Value { views.AddGlobalFunc("GetLanguageList", func(jet.Arguments) reflect.Value {
return reflect.ValueOf(util.AmLanguageList()) return reflect.ValueOf(util.AmLanguageList())
}) })
views.AddGlobalFunc("GetTimeZoneList", func(a jet.Arguments) reflect.Value { views.AddGlobalFunc("GetTimeZoneList", func(jet.Arguments) reflect.Value {
return reflect.ValueOf(util.AmTimeZoneList()) return reflect.ValueOf(util.AmTimeZoneList())
}) })
views.AddGlobalFunc("GetMonthList", func(a jet.Arguments) reflect.Value { views.AddGlobalFunc("GetMonthList", func(jet.Arguments) reflect.Value {
return reflect.ValueOf(util.AmMonthList()) return reflect.ValueOf(util.AmMonthList())
}) })
views.AddGlobalFunc("AmMenu", func(a jet.Arguments) reflect.Value { views.AddGlobalFunc("AmMenu", func(a jet.Arguments) reflect.Value {
@@ -173,5 +204,9 @@ func (r *TemplateRenderer) Render(w io.Writer, name string, data any, c echo.Con
if amctxt != nil { if amctxt != nil {
vmap = amctxt.VarMap() vmap = amctxt.VarMap()
} }
return view.Execute(w, vmap, data) err = view.Execute(w, vmap, data)
if err != nil {
log.Errorf("Template \"%s\" failed exec: %v", name, err)
}
return err
} }
+2 -2
View File
@@ -208,7 +208,7 @@
{{ h := rx.HostQ() }} {{ h := rx.HostQ() }}
<a href="/user/{{ h.Username }}" class="text-blue-700 hover:text-blue-900">{{ h.Username }}</a> <a href="/user/{{ h.Username }}" class="text-blue-700 hover:text-blue-900">{{ h.Username }}</a>
<span class="mx-2">-</span> <span class="mx-2">-</span>
{{ n := rx.MemberCountQ(false) }} {{ n := DisplayMemberCount(rx, .) }}
{{ if n == 1 }} {{ if n == 1 }}
<span>1 member</span> <span>1 member</span>
{{ else }} {{ else }}
@@ -216,7 +216,7 @@
{{ end }} {{ end }}
</div> </div>
<div> <div>
<span class="font-medium">Latest activity:</span> [Today, 9:52:48 PM] <span class="font-medium">Latest activity:</span> {{ DisplayActivity(rx.LastAccess, .)}}
</div> </div>
<div class="italic text-gray-600 mt-2">{{ rx.Synopsis }}</div> <div class="italic text-gray-600 mt-2">{{ rx.Synopsis }}</div>
</div> </div>
+44
View File
@@ -11,10 +11,12 @@
package util package util
import ( import (
"fmt"
"slices" "slices"
"sync" "sync"
"time" "time"
"github.com/klauspost/lctime"
"github.com/tkuchiki/go-timezone" "github.com/tkuchiki/go-timezone"
) )
@@ -51,6 +53,48 @@ func AmMonthList() []string {
return rc return rc
} }
/* AmActivityString generates a string to represent the activity based on the given timestamp
* and the current time.
* Parameters:
* timeval - The time value representing the last point of activity.
* loc - The localizer used to format the time.
* Returns:
* The string activity equivalent.
*/
func AmActivityString(timeval *time.Time, loc lctime.Localizer) string {
if timeval == nil {
return "Never"
}
now := time.Now().In(timeval.Location())
day := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
if timeval.Compare(day) == 1 {
return "Today, " + loc.Strftime("%X", *timeval)
}
day = time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location())
if timeval.Compare(day) == 1 {
return "Yesterday, " + loc.Strftime("%X", *timeval)
}
duration := now.Sub(*timeval)
days := duration.Hours() / 24.0
day = time.Date(now.Year(), now.Month()-1, now.Day(), 0, 0, 0, 0, now.Location())
if timeval.Compare(day) == 1 {
return fmt.Sprintf("%d days ago", int(days))
}
day = time.Date(now.Year()-1, now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
if timeval.Compare(day) == 1 {
nm := int(days / 30.0)
if nm == 1 {
return "1 month ago"
}
return fmt.Sprintf("%d months ago", nm)
}
ny := int(days / 365.25)
if ny == 1 {
return "1 year ago"
}
return fmt.Sprintf("%d years ago", ny)
}
// init preloads the time zone list. // init preloads the time zone list.
func init() { func init() {
go AmTimeZoneList() go AmTimeZoneList()