Show the names of the participants on the convo list instead of the placeholder text.

Shorten some things to save a bit of memory and to standardise things.
This commit is contained in:
Azareal 2019-08-28 16:47:54 +10:00
parent cae7e00b0c
commit ddb917761f
13 changed files with 245 additions and 119 deletions

View File

@ -3,8 +3,9 @@ package common
import ( import (
"errors" "errors"
"time" "time"
//"log"
//"strconv" "strconv"
"database/sql" "database/sql"
qgen "github.com/Azareal/Gosora/query_gen" qgen "github.com/Azareal/Gosora/query_gen"
@ -20,7 +21,7 @@ type ConvoStmts struct {
edit *sql.Stmt edit *sql.Stmt
create *sql.Stmt create *sql.Stmt
delete *sql.Stmt delete *sql.Stmt
has *sql.Stmt has *sql.Stmt
editPost *sql.Stmt editPost *sql.Stmt
createPost *sql.Stmt createPost *sql.Stmt
@ -37,7 +38,7 @@ func init() {
countPosts: acc.Count("conversations_posts").Where("cid = ?").Prepare(), countPosts: acc.Count("conversations_posts").Where("cid = ?").Prepare(),
edit: acc.Update("conversations").Set("lastReplyBy = ?, lastReplyAt = ?").Where("cid = ?").Prepare(), edit: acc.Update("conversations").Set("lastReplyBy = ?, lastReplyAt = ?").Where("cid = ?").Prepare(),
create: acc.Insert("conversations").Columns("createdAt, lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), create: acc.Insert("conversations").Columns("createdAt, lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
has: acc.Count("conversations_participants").Where("uid = ? AND cid = ?").Prepare(), has: acc.Count("conversations_participants").Where("uid = ? AND cid = ?").Prepare(),
editPost: acc.Update("conversations_posts").Set("body = ?, post = ?").Where("pid = ?").Prepare(), editPost: acc.Update("conversations_posts").Set("body = ?, post = ?").Where("pid = ?").Prepare(),
createPost: acc.Insert("conversations_posts").Columns("cid, body, post, createdBy").Fields("?,?,?,?").Prepare(), createPost: acc.Insert("conversations_posts").Columns("cid, body, post, createdBy").Fields("?,?,?,?").Prepare(),
@ -130,9 +131,15 @@ func (co *Conversation) Create() (int, error) {
return int(lastID), err return int(lastID), err
} }
type ConversationExtra struct {
*Conversation
Users []*User
}
type ConversationStore interface { type ConversationStore interface {
Get(id int) (*Conversation, error) Get(id int) (*Conversation, error)
GetUser(uid int, offset int) (cos []*Conversation, err error) GetUser(uid int, offset int) (cos []*Conversation, err error)
GetUserExtra(uid int, offset int) (cos []*ConversationExtra, err error)
GetUserCount(uid int) (count int) GetUserCount(uid int) (count int)
Delete(id int) error Delete(id int) error
Count() (count int) Count() (count int)
@ -140,35 +147,35 @@ type ConversationStore interface {
} }
type DefaultConversationStore struct { type DefaultConversationStore struct {
get *sql.Stmt get *sql.Stmt
getUser *sql.Stmt getUser *sql.Stmt
getUserCount *sql.Stmt getUserCount *sql.Stmt
delete *sql.Stmt delete *sql.Stmt
deletePosts *sql.Stmt deletePosts *sql.Stmt
deleteParticipants *sql.Stmt deleteParticipants *sql.Stmt
create *sql.Stmt create *sql.Stmt
addParticipant *sql.Stmt addParticipant *sql.Stmt
count *sql.Stmt count *sql.Stmt
} }
func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConversationStore, error) { func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConversationStore, error) {
return &DefaultConversationStore{ return &DefaultConversationStore{
get: acc.Select("conversations").Columns("createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?").Prepare(), get: acc.Select("conversations").Columns("createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?").Prepare(),
getUser: acc.SimpleInnerJoin("conversations_participants AS cp", "conversations AS c", "cp.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt", "cp.cid = c.cid", "cp.uid = ?", "c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC", "?,?"), getUser: acc.SimpleInnerJoin("conversations_participants AS cp", "conversations AS c", "cp.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt", "cp.cid = c.cid", "cp.uid = ?", "c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC", "?,?"),
getUserCount: acc.Count("conversations_participants").Where("uid = ?").Prepare(), getUserCount: acc.Count("conversations_participants").Where("uid = ?").Prepare(),
delete: acc.Delete("conversations").Where("cid = ?").Prepare(), delete: acc.Delete("conversations").Where("cid = ?").Prepare(),
deletePosts: acc.Delete("conversations_posts").Where("cid = ?").Prepare(), deletePosts: acc.Delete("conversations_posts").Where("cid = ?").Prepare(),
deleteParticipants: acc.Delete("conversations_participants").Where("cid = ?").Prepare(), deleteParticipants: acc.Delete("conversations_participants").Where("cid = ?").Prepare(),
create: acc.Insert("conversations").Columns("createdBy, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), create: acc.Insert("conversations").Columns("createdBy, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
addParticipant: acc.Insert("conversations_participants").Columns("uid, cid").Fields("?,?").Prepare(), addParticipant: acc.Insert("conversations_participants").Columns("uid, cid").Fields("?,?").Prepare(),
count: acc.Count("conversations").Prepare(), count: acc.Count("conversations").Prepare(),
}, acc.FirstError() }, acc.FirstError()
} }
func (s *DefaultConversationStore) Get(id int) (*Conversation, error) { func (s *DefaultConversationStore) Get(id int) (*Conversation, error) {
convo := &Conversation{ID: id} co := &Conversation{ID: id}
err := s.get.QueryRow(id).Scan(&convo.CreatedBy, &convo.CreatedAt, &convo.LastReplyBy, &convo.LastReplyAt) err := s.get.QueryRow(id).Scan(&co.CreatedBy, &co.CreatedAt, &co.LastReplyBy, &co.LastReplyAt)
return convo, err return co, err
} }
func (s *DefaultConversationStore) GetUser(uid int, offset int) (cos []*Conversation, err error) { func (s *DefaultConversationStore) GetUser(uid int, offset int) (cos []*Conversation, err error) {
@ -190,6 +197,109 @@ func (s *DefaultConversationStore) GetUser(uid int, offset int) (cos []*Conversa
return cos, rows.Err() return cos, rows.Err()
} }
func (s *DefaultConversationStore) GetUserExtra(uid int, offset int) (cos []*ConversationExtra, err error) {
raw, err := s.GetUser(uid, offset)
if err != nil {
return nil, err
}
//log.Printf("raw: %+v\n", raw)
if len(raw) == 0 {
//log.Println("r0")
return nil, sql.ErrNoRows
}
if len(raw) == 1 {
//log.Print("r0b2")
uids, err := raw[0].Uids()
if err != nil {
return nil, err
}
//log.Println("r1b2")
umap, err := Users.BulkGetMap(uids)
if err != nil {
return nil, err
}
//log.Println("r2b2")
users := make([]*User,len(umap))
var i int
for _, user := range umap {
users[i] = user
i++
}
return []*ConversationExtra{&ConversationExtra{raw[0],users}}, nil
}
//log.Println("1")
cmap := make(map[int]*ConversationExtra,len(raw))
for _, co := range raw {
cmap[co.ID] = &ConversationExtra{co,nil}
}
// TODO: Add a function for the q stuff
var q string
idList := make([]interface{},len(raw))
for i, co := range raw {
idList[i] = strconv.Itoa(co.ID)
q += "?,"
}
q = q[0 : len(q)-1]
rows, err := qgen.NewAcc().Select("conversations_participants").Columns("uid,cid").Where("cid IN(" + q + ")").Query(idList...)
if err != nil {
return nil, err
}
defer rows.Close()
//log.Println("2")
idmap := make(map[int][]int) // cid: []uid
puidmap := make(map[int]struct{})
for rows.Next() {
var uid, cid int
err := rows.Scan(&uid, &cid)
if err != nil {
return nil, err
}
idmap[cid] = append(idmap[cid],uid)
puidmap[uid] = struct{}{}
}
err = rows.Err()
if err != nil {
return nil, err
}
//log.Println("3")
//log.Printf("idmap: %+v\n", idmap)
//log.Printf("puidmap: %+v\n",puidmap)
puids := make([]int,len(puidmap))
var i int
for puid, _ := range puidmap {
puids[i] = puid
i++
}
umap, err := Users.BulkGetMap(puids)
if err != nil {
return nil, err
}
//log.Println("4")
//log.Printf("umap: %+v\n", umap)
for cid, uids := range idmap {
co := cmap[cid]
for _, uid := range uids {
co.Users = append(co.Users,umap[uid])
}
//log.Printf("co.Conversation: %+v\n", co.Conversation)
//log.Printf("co.Users: %+v\n", co.Users)
cmap[cid] = co
}
//log.Printf("cmap: %+v\n", cmap)
for _, ra := range raw {
cos = append(cos,cmap[ra.ID])
}
//log.Printf("cos: %+v\n", cos)
return cos, rows.Err()
}
func (s *DefaultConversationStore) GetUserCount(uid int) (count int) { func (s *DefaultConversationStore) GetUserCount(uid int) (count int) {
err := s.getUserCount.QueryRow(uid).Scan(&count) err := s.getUserCount.QueryRow(uid).Scan(&count)
if err != nil { if err != nil {
@ -225,10 +335,7 @@ func (s *DefaultConversationStore) Create(content string, createdBy int, partici
return 0, err return 0, err
} }
post := &ConversationPost{} post := &ConversationPost{CID: int(lastID), Body: content, CreatedBy: createdBy}
post.CID = int(lastID)
post.Body = content
post.CreatedBy = createdBy
_, err = post.Create() _, err = post.Create()
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -81,7 +81,7 @@ func (s *MemoryForumPermsStore) Reload(fid int) error {
} }
defer rows.Close() defer rows.Close()
var forumPerms = make(map[int]*ForumPerms) forumPerms := make(map[int]*ForumPerms)
for rows.Next() { for rows.Next() {
var gid int var gid int
var perms []byte var perms []byte

View File

@ -265,7 +265,7 @@ type ResetPage struct {
type ConvoListPage struct { type ConvoListPage struct {
*Header *Header
Convos []*Conversation Convos []*ConversationExtra
Paginator Paginator
} }

View File

@ -145,15 +145,15 @@ func (s *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err error)
} }
// TODO: Add a function for the qlist stuff // TODO: Add a function for the qlist stuff
var qlist string var q string
idList := make([]interface{},len(ids)) idList := make([]interface{},len(ids))
for i, id := range ids { for i, id := range ids {
idList[i] = strconv.Itoa(id) idList[i] = strconv.Itoa(id)
qlist += "?," q += "?,"
} }
qlist = qlist[0 : len(qlist)-1] q = q[0 : len(q)-1]
rows, err := qgen.NewAcc().Select("polls").Columns("pollID, parentID, parentTable, type, options, votes").Where("pollID IN(" + qlist + ")").Query(idList...) rows, err := qgen.NewAcc().Select("polls").Columns("pollID,parentID,parentTable,type,options,votes").Where("pollID IN(" + q + ")").Query(idList...)
if err != nil { if err != nil {
return list, err return list, err
} }

View File

@ -16,7 +16,6 @@ import (
) )
// TODO: Add the watchdog goroutine // TODO: Add the watchdog goroutine
// TODO: Add BulkGetMap
// TODO: Add some sort of update method // TODO: Add some sort of update method
// ? - Should we add stick, lock, unstick, and unlock methods? These might be better on the Topics not the TopicStore // ? - Should we add stick, lock, unstick, and unlock methods? These might be better on the Topics not the TopicStore
var Topics TopicStore var Topics TopicStore
@ -104,7 +103,7 @@ func (s *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
// TODO: Avoid duplicating much of this logic from user_store.go // TODO: Avoid duplicating much of this logic from user_store.go
func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err error) { func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err error) {
var idCount = len(ids) idCount := len(ids)
list = make(map[int]*Topic) list = make(map[int]*Topic)
if idCount == 0 { if idCount == 0 {
return list, nil return list, nil
@ -136,15 +135,15 @@ func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err erro
} }
// TODO: Add a function for the qlist stuff // TODO: Add a function for the qlist stuff
var qlist string var q string
idList := make([]interface{},len(ids)) idList := make([]interface{},len(ids))
for i, id := range ids { for i, id := range ids {
idList[i] = strconv.Itoa(id) idList[i] = strconv.Itoa(id)
qlist += "?," q += "?,"
} }
qlist = qlist[0 : len(qlist)-1] q = q[0 : len(q)-1]
rows, err := qgen.NewAcc().Select("topics").Columns("tid, title, content, createdBy, createdAt, lastReplyBy, lastReplyAt, lastReplyID, is_closed, sticky, parentID, ipaddress, views, postCount, likeCount, attachCount, poll, data").Where("tid IN(" + qlist + ")").Query(idList...) rows, err := qgen.NewAcc().Select("topics").Columns("tid,title,content,createdBy,createdAt,lastReplyBy,lastReplyAt,lastReplyID,is_closed,sticky,parentID,ipaddress,views,postCount,likeCount,attachCount,poll,data").Where("tid IN(" + q + ")").Query(idList...)
if err != nil { if err != nil {
return list, err return list, err
} }

View File

@ -63,78 +63,67 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
}, acc.FirstError() }, acc.FirstError()
} }
func (mus *DefaultUserStore) DirtyGet(id int) *User { func (s *DefaultUserStore) DirtyGet(id int) *User {
user, err := mus.cache.Get(id) user, err := s.Get(id)
if err == nil { if err == nil {
return user return user
} }
/*if mus.OutOfBounds(id) { /*if s.OutOfBounds(id) {
return BlankUser() return BlankUser()
}*/ }*/
user = &User{ID: id, Loggedin: true}
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.RawAvatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup)
user.Init()
if err == nil {
mus.cache.Set(user)
return user
}
return BlankUser() return BlankUser()
} }
// TODO: Log weird cache errors? Not just here but in every *Cache? // TODO: Log weird cache errors? Not just here but in every *Cache?
func (mus *DefaultUserStore) Get(id int) (*User, error) { func (s *DefaultUserStore) Get(id int) (*User, error) {
user, err := mus.cache.Get(id) u, err := s.cache.Get(id)
if err == nil { if err == nil {
//log.Print("cached user") //log.Print("cached user")
//log.Print(string(debug.Stack())) //log.Print(string(debug.Stack()))
//log.Println("") //log.Println("")
return user, nil return u, nil
} }
//log.Print("uncached user") //log.Print("uncached user")
user = &User{ID: id, Loggedin: true} u = &User{ID: id, Loggedin: true}
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.RawAvatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup) err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score,&u.Liked, &u.LastIP, &u.TempGroup)
user.Init()
if err == nil { if err == nil {
mus.cache.Set(user) u.Init()
s.cache.Set(u)
} }
return user, err return u, err
} }
// TODO: Log weird cache errors? Not just here but in every *Cache? // TODO: Log weird cache errors? Not just here but in every *Cache?
// ! This bypasses the cache, use frugally // ! This bypasses the cache, use frugally
func (mus *DefaultUserStore) GetByName(name string) (*User, error) { func (s *DefaultUserStore) GetByName(name string) (*User, error) {
user := &User{Loggedin: true} u := &User{Loggedin: true}
err := mus.getByName.QueryRow(name).Scan(&user.ID, &user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.RawAvatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup) err := s.getByName.QueryRow(name).Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup)
user.Init()
if err == nil { if err == nil {
mus.cache.Set(user) u.Init()
s.cache.Set(u)
} }
return user, err return u, err
} }
// TODO: Optimise this, so we don't wind up hitting the database every-time for small gaps // TODO: Optimise this, so we don't wind up hitting the database every-time for small gaps
// TODO: Make this a little more consistent with DefaultGroupStore's GetRange method // TODO: Make this a little more consistent with DefaultGroupStore's GetRange method
func (store *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User, err error) { func (s *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User, err error) {
rows, err := store.getOffset.Query(offset, perPage) rows, err := s.getOffset.Query(offset, perPage)
if err != nil { if err != nil {
return users, err return users, err
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
user := &User{Loggedin: true} u := &User{Loggedin: true}
err := rows.Scan(&user.ID, &user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.RawAvatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup) err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user.Init() u.Init()
store.cache.Set(user) s.cache.Set(u)
users = append(users, user) users = append(users, u)
} }
return users, rows.Err() return users, rows.Err()
} }
@ -142,7 +131,7 @@ func (store *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts? // TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
// TODO: ID of 0 should always error? // TODO: ID of 0 should always error?
func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) { func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) {
var idCount = len(ids) idCount := len(ids)
list = make(map[int]*User) list = make(map[int]*User)
if idCount == 0 { if idCount == 0 {
return list, nil return list, nil
@ -173,16 +162,16 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error)
return list, nil return list, nil
} }
// TODO: Add a function for the qlist stuff // TODO: Add a function for the q stuff
var qlist string var q string
idList := make([]interface{},len(ids)) idList := make([]interface{},len(ids))
for i, id := range ids { for i, id := range ids {
idList[i] = strconv.Itoa(id) idList[i] = strconv.Itoa(id)
qlist += "?," q += "?,"
} }
qlist = qlist[0 : len(qlist)-1] q = q[0 : len(q)-1]
rows, err := qgen.NewAcc().Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(idList...) rows, err := qgen.NewAcc().Select("users").Columns("uid,name,group,active,is_super_admin,session,email,avatar,message,url_prefix,url_name,level,score,liked,last_ip,temp_group").Where("uid IN(" + q + ")").Query(idList...)
if err != nil { if err != nil {
return list, err return list, err
} }
@ -221,29 +210,28 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error)
return list, err return list, err
} }
func (mus *DefaultUserStore) BypassGet(id int) (*User, error) { func (s *DefaultUserStore) BypassGet(id int) (*User, error) {
user := &User{ID: id, Loggedin: true} u := &User{ID: id, Loggedin: true}
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.RawAvatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup) err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup)
u.Init()
user.Init() return u, err
return user, err
} }
func (mus *DefaultUserStore) Reload(id int) error { func (s *DefaultUserStore) Reload(id int) error {
user := &User{ID: id, Loggedin: true} u := &User{ID: id, Loggedin: true}
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.RawAvatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup) err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup)
if err != nil { if err != nil {
mus.cache.Remove(id) s.cache.Remove(id)
return err return err
} }
user.Init() u.Init()
_ = mus.cache.Set(user) _ = s.cache.Set(u)
TopicListThaw.Thaw() TopicListThaw.Thaw()
return nil return nil
} }
func (mus *DefaultUserStore) Exists(id int) bool { func (s *DefaultUserStore) Exists(id int) bool {
err := mus.exists.QueryRow(id).Scan(&id) err := s.exists.QueryRow(id).Scan(&id)
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
LogError(err) LogError(err)
} }
@ -252,7 +240,7 @@ func (mus *DefaultUserStore) Exists(id int) bool {
// TODO: Change active to a bool? // TODO: Change active to a bool?
// TODO: Use unique keys for the usernames // TODO: Use unique keys for the usernames
func (mus *DefaultUserStore) Create(username string, password string, email string, group int, active bool) (int, error) { func (s *DefaultUserStore) Create(username string, password string, email string, group int, active bool) (int, error) {
// TODO: Strip spaces? // TODO: Strip spaces?
// ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory? // ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory?
@ -261,7 +249,7 @@ func (mus *DefaultUserStore) Create(username string, password string, email stri
} }
// Is this username already taken..? // Is this username already taken..?
err := mus.usernameExists.QueryRow(username).Scan(&username) err := s.usernameExists.QueryRow(username).Scan(&username)
if err != ErrNoRows { if err != ErrNoRows {
return 0, ErrAccountExists return 0, ErrAccountExists
} }
@ -274,7 +262,7 @@ func (mus *DefaultUserStore) Create(username string, password string, email stri
return 0, err return 0, err
} }
res, err := mus.register.Exec(username, email, string(hashedPassword), salt, group, active) res, err := s.register.Exec(username, email, string(hashedPassword), salt, group, active)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -16,24 +16,24 @@ func NewDefaultWidgetStore() *DefaultWidgetStore {
return &DefaultWidgetStore{widgets: make(map[int]*Widget)} return &DefaultWidgetStore{widgets: make(map[int]*Widget)}
} }
func (widgets *DefaultWidgetStore) Get(id int) (*Widget, error) { func (w *DefaultWidgetStore) Get(id int) (*Widget, error) {
widgets.RLock() w.RLock()
defer widgets.RUnlock() defer w.RUnlock()
widget, ok := widgets.widgets[id] widget, ok := w.widgets[id]
if !ok { if !ok {
return widget, sql.ErrNoRows return widget, sql.ErrNoRows
} }
return widget, nil return widget, nil
} }
func (widgets *DefaultWidgetStore) set(widget *Widget) { func (w *DefaultWidgetStore) set(widget *Widget) {
widgets.Lock() w.Lock()
defer widgets.Unlock() defer w.Unlock()
widgets.widgets[widget.ID] = widget w.widgets[widget.ID] = widget
} }
func (widgets *DefaultWidgetStore) delete(id int) { func (w *DefaultWidgetStore) delete(id int) {
widgets.Lock() w.Lock()
defer widgets.Unlock() defer w.Unlock()
delete(widgets.widgets, id) delete(w.widgets, id)
} }

View File

@ -538,6 +538,7 @@
"convos_head":"Conversations", "convos_head":"Conversations",
"convo_head":"Conversation", "convo_head":"Conversation",
"convo_users":"Participants",
"create_convo_head":"Create Conversation", "create_convo_head":"Create Conversation",
"create_convo_button":"Create Convo", "create_convo_button":"Create Convo",

View File

@ -100,12 +100,12 @@ func (b *accUpdateBuilder) Prepare() *sql.Stmt {
return b.build.prepare(b.build.adapter.SimpleUpdate(b.up)) return b.build.prepare(b.build.adapter.SimpleUpdate(b.up))
} }
func (u *accUpdateBuilder) Exec(args ...interface{}) (res sql.Result, err error) { func (b *accUpdateBuilder) Exec(args ...interface{}) (res sql.Result, err error) {
query, err := u.build.adapter.SimpleUpdate(u.up) query, err := b.build.adapter.SimpleUpdate(b.up)
if err != nil { if err != nil {
return res, err return res, err
} }
return u.build.exec(query, args...) return b.build.exec(query, args...)
} }
type AccSelectBuilder struct { type AccSelectBuilder struct {
@ -145,7 +145,8 @@ func (b *AccSelectBuilder) In(column string, inList []int) *AccSelectBuilder {
return b return b
} }
var where = column + " IN(" // TODO: Optimise this
where := column + " IN("
for _, item := range inList { for _, item := range inList {
where += strconv.Itoa(item) + "," where += strconv.Itoa(item) + ","
} }
@ -158,6 +159,29 @@ func (b *AccSelectBuilder) In(column string, inList []int) *AccSelectBuilder {
return b return b
} }
// TODO: Don't implement the SQL at the accumulator level but the adapter level
func (b *AccSelectBuilder) InPQuery(column string, inList []int) (*sql.Rows, error) {
if len(inList) == 0 {
return nil, sql.ErrNoRows
}
// TODO: Optimise this
where := column + " IN("
idList := make([]interface{},len(inList))
for i, id := range inList {
idList[i] = strconv.Itoa(id)
where += "?,"
}
where = where[0 : len(where)-1] + ")"
if b.where != "" {
where += " AND " + b.where
}
b.where = where
return b.Query(idList...)
}
func (b *AccSelectBuilder) InQ(column string, subBuilder *AccSelectBuilder) *AccSelectBuilder { func (b *AccSelectBuilder) InQ(column string, subBuilder *AccSelectBuilder) *AccSelectBuilder {
b.inChain = subBuilder b.inChain = subBuilder
b.inColumn = column b.inColumn = column

View File

@ -2,10 +2,11 @@ package routes
import ( import (
"database/sql" "database/sql"
"errors"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"errors" //"log"
c "github.com/Azareal/Gosora/common" c "github.com/Azareal/Gosora/common"
p "github.com/Azareal/Gosora/common/phrases" p "github.com/Azareal/Gosora/common/phrases"
@ -13,13 +14,15 @@ import (
func Convos(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { func Convos(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
accountEditHead("convos", w, r, &user, header) accountEditHead("convos", w, r, &user, header)
header.AddSheet(header.Theme.Name + "/convo.css")
header.AddNotice("convo_dev") header.AddNotice("convo_dev")
ccount := c.Convos.GetUserCount(user.ID) ccount := c.Convos.GetUserCount(user.ID)
page, _ := strconv.Atoi(r.FormValue("page")) page, _ := strconv.Atoi(r.FormValue("page"))
offset, page, lastPage := c.PageOffset(ccount, page, c.Config.ItemsPerPage) offset, page, lastPage := c.PageOffset(ccount, page, c.Config.ItemsPerPage)
pageList := c.Paginate(page, lastPage, 5) pageList := c.Paginate(page, lastPage, 5)
convos, err := c.Convos.GetUser(user.ID, offset) convos, err := c.Convos.GetUserExtra(user.ID, offset)
//log.Printf("convos: %+v\n", convos)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return c.NotFound(w, r, header) return c.NotFound(w, r, header)
} else if err != nil { } else if err != nil {
@ -74,7 +77,7 @@ func Convo(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header
} else if err != nil { } else if err != nil {
return c.InternalError(err, w, r) return c.InternalError(err, w, r)
} }
users := make([]*c.User,len(umap)) users := make([]*c.User, len(umap))
i := 0 i := 0
for _, user := range umap { for _, user := range umap {
users[i] = user users[i] = user

View File

@ -5,11 +5,11 @@
</div> </div>
<div class="colstack_item parti"> <div class="colstack_item parti">
<div class="rowitem"> <div class="rowitem">
<div>Participants:&nbsp;</div> <div>{{lang "convo_users"}}:&nbsp;</div>
{{range .Users}}<div class="parti_user"><a href="{{.Link}}">{{.Name}}</a></div>&nbsp;{{end}} {{range .Users}}<div class="parti_user"><a href="{{.Link}}">{{.Name}}</a></div>&nbsp;{{end}}
</div> </div>
</div> </div>
<div class="colstack_item">{{template "convo_row.html" .}}</div> <div class="colstack_item convo_row_box">{{template "convo_row.html" .}}</div>
{{if not .CurrentUser.IsBanned}} {{if not .CurrentUser.IsBanned}}
<form action="/user/convo/create/submit/{{.Convo.ID}}?s={{.CurrentUser.Session}}" method="post"> <form action="/user/convo/create/submit/{{.Convo.ID}}?s={{.CurrentUser.Session}}" method="post">
<div class="colstack_item topic_reply_form" style="border-top:none;"> <div class="colstack_item topic_reply_form" style="border-top:none;">

View File

@ -8,7 +8,7 @@
{{range .Convos}} {{range .Convos}}
<div class="rowitem"> <div class="rowitem">
<span class="to_left"> <span class="to_left">
<a href="/user/convo/{{.ID}}">Message</a> <a href="/user/convo/{{.ID}}">{{range .Users}}<span class="convos_item_user">{{.Name}}</span>&nbsp;{{end}}</a></span></a>
</span> </span>
<span title="{{abstime .LastReplyAt}}" class="to_right">{{reltime .LastReplyAt}}</span> <span title="{{abstime .LastReplyAt}}" class="to_right">{{reltime .LastReplyAt}}</span>
</div>{{end}} </div>{{end}}

View File

@ -1,3 +1,7 @@
.convos_item_user:not(:last-child):after {
content: ",";
}
.parti { .parti {
margin-bottom: 8px; margin-bottom: 8px;
} }