Show avatars for 1-1 conversations in the convo list.
Don't show your own name in conversation titles. Don't record the same participant in a conversation twice.
This commit is contained in:
parent
9f0333ddc4
commit
01b7a18ea9
|
@ -54,6 +54,7 @@ func init() {
|
||||||
|
|
||||||
type Conversation struct {
|
type Conversation struct {
|
||||||
ID int
|
ID int
|
||||||
|
Link string
|
||||||
CreatedBy int
|
CreatedBy int
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
LastReplyBy int
|
LastReplyBy int
|
||||||
|
@ -133,6 +134,10 @@ func (co *Conversation) Create() (int, error) {
|
||||||
return int(lastID), err
|
return int(lastID), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildConvoURL(coid int) string {
|
||||||
|
return "/user/convo/" + strconv.Itoa(coid)
|
||||||
|
}
|
||||||
|
|
||||||
type ConversationExtra struct {
|
type ConversationExtra struct {
|
||||||
*Conversation
|
*Conversation
|
||||||
Users []*User
|
Users []*User
|
||||||
|
@ -141,7 +146,7 @@ type ConversationExtra struct {
|
||||||
type ConversationStore interface {
|
type ConversationStore interface {
|
||||||
Get(id int) (*Conversation, error)
|
Get(id int) (*Conversation, error)
|
||||||
GetUser(uid, offset int) (cos []*Conversation, err error)
|
GetUser(uid, offset int) (cos []*Conversation, err error)
|
||||||
GetUserExtra(uid int, offset int) (cos []*ConversationExtra, err error)
|
GetUserExtra(uid, 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)
|
||||||
|
@ -330,7 +335,7 @@ func (s *DefaultConversationStore) Create(content string, createdBy int, partici
|
||||||
if len(participants) == 0 {
|
if len(participants) == 0 {
|
||||||
return 0, errors.New("no participants set")
|
return 0, errors.New("no participants set")
|
||||||
}
|
}
|
||||||
res, err := s.create.Exec(createdBy,createdBy)
|
res, err := s.create.Exec(createdBy, createdBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -346,6 +351,9 @@ func (s *DefaultConversationStore) Create(content string, createdBy int, partici
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range participants {
|
for _, p := range participants {
|
||||||
|
if p == createdBy {
|
||||||
|
continue
|
||||||
|
}
|
||||||
_, err := s.addParticipant.Exec(p, lastID)
|
_, err := s.addParticipant.Exec(p, lastID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -283,9 +283,15 @@ type ResetPage struct {
|
||||||
MFA bool
|
MFA bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConvoListRow struct {
|
||||||
|
*ConversationExtra
|
||||||
|
ShortUsers []*User
|
||||||
|
OneOnOne bool
|
||||||
|
}
|
||||||
|
|
||||||
type ConvoListPage struct {
|
type ConvoListPage struct {
|
||||||
*Header
|
*Header
|
||||||
Convos []*ConversationExtra
|
Convos []ConvoListRow
|
||||||
Paginator
|
Paginator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -350,13 +350,17 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
|
||||||
t.AddStd("account", "c.Account", accountPage)
|
t.AddStd("account", "c.Account", accountPage)
|
||||||
|
|
||||||
parti := []*User{&user}
|
parti := []*User{&user}
|
||||||
convo := &Conversation{1, user.ID, time.Now(), 0, time.Now()}
|
convo := &Conversation{1, BuildConvoURL(1), user.ID, time.Now(), 0, time.Now()}
|
||||||
convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
|
convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
|
||||||
convoPage := ConvoViewPage{header, convo, convoItems, parti, true, Paginator{[]int{1}, 1, 1}}
|
convoPage := ConvoViewPage{header, convo, convoItems, parti, true, Paginator{[]int{1}, 1, 1}}
|
||||||
t.AddStd("convo", "c.ConvoViewPage", convoPage)
|
t.AddStd("convo", "c.ConvoViewPage", convoPage)
|
||||||
|
|
||||||
convos := []*ConversationExtra{&ConversationExtra{&Conversation{}, []*User{&user}}}
|
convos := []*ConversationExtra{&ConversationExtra{&Conversation{}, []*User{&user}}}
|
||||||
convoListPage := ConvoListPage{header, convos, Paginator{[]int{1}, 1, 1}}
|
var cRows []ConvoListRow
|
||||||
|
for _, convo := range convos {
|
||||||
|
cRows = append(cRows, ConvoListRow{convo, convo.Users, false})
|
||||||
|
}
|
||||||
|
convoListPage := ConvoListPage{header, cRows, Paginator{[]int{1}, 1, 1}}
|
||||||
t.AddStd("convos", "c.ConvoListPage", convoListPage)
|
t.AddStd("convos", "c.ConvoListPage", convoListPage)
|
||||||
|
|
||||||
basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID}
|
basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID}
|
||||||
|
@ -557,7 +561,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
|
||||||
t.AddStd("topic_c_poll_input", "c.TopicCPollInput", TopicCPollInput{Index: 0})
|
t.AddStd("topic_c_poll_input", "c.TopicCPollInput", TopicCPollInput{Index: 0})
|
||||||
|
|
||||||
parti := []*User{&user}
|
parti := []*User{&user}
|
||||||
convo := &Conversation{1, user.ID, time.Now(), 0, time.Now()}
|
convo := &Conversation{1, BuildConvoURL(1), user.ID, time.Now(), 0, time.Now()}
|
||||||
convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
|
convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
|
||||||
convoPage := ConvoViewPage{header, convo, convoItems, parti, true, Paginator{[]int{1}, 1, 1}}
|
convoPage := ConvoViewPage{header, convo, convoItems, parti, true, Paginator{[]int{1}, 1, 1}}
|
||||||
t.AddStd("convo", "c.ConvoViewPage", convoPage)
|
t.AddStd("convo", "c.ConvoViewPage", convoPage)
|
||||||
|
|
|
@ -30,7 +30,24 @@ func Convos(w http.ResponseWriter, r *http.Request, user c.User, h *c.Header) c.
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := c.Account{h, "dashboard", "convos", c.ConvoListPage{h, convos, c.Paginator{pageList, page, lastPage}}}
|
var cRows []c.ConvoListRow
|
||||||
|
for _, convo := range convos {
|
||||||
|
var parti []*c.User
|
||||||
|
notMe := false
|
||||||
|
for _, u := range convo.Users {
|
||||||
|
if u.ID == user.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parti = append(parti, u)
|
||||||
|
notMe = true
|
||||||
|
}
|
||||||
|
if !notMe {
|
||||||
|
parti = convo.Users
|
||||||
|
}
|
||||||
|
cRows = append(cRows, c.ConvoListRow{convo, parti, len(parti) == 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
pi := c.Account{h, "dashboard", "convos", c.ConvoListPage{h, cRows, c.Paginator{pageList, page, lastPage}}}
|
||||||
return renderTemplate("account", w, r, h, pi)
|
return renderTemplate("account", w, r, h, pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,16 +155,29 @@ func ConvosCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.R
|
||||||
return c.NoPermissions(w, r, user)
|
return c.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
recps := c.SanitiseSingleLine(r.PostFormValue("recp"))
|
sRecps := c.SanitiseSingleLine(r.PostFormValue("recp"))
|
||||||
body := c.PreparseMessage(r.PostFormValue("body"))
|
body := c.PreparseMessage(r.PostFormValue("body"))
|
||||||
rlist := []int{}
|
rlist := []int{}
|
||||||
|
|
||||||
|
// De-dupe recipients
|
||||||
|
var recps []string
|
||||||
|
unames := make(map[string]struct{})
|
||||||
|
for _, recp := range strings.Split(sRecps, ",") {
|
||||||
|
recp = strings.TrimSpace(recp)
|
||||||
|
_, exists := unames[recp]
|
||||||
|
if !exists {
|
||||||
|
recps = append(recps, recp)
|
||||||
|
unames[recp] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
max := 10 // max number of recipients that can be added at once
|
max := 10 // max number of recipients that can be added at once
|
||||||
for i, recp := range strings.Split(recps, ",") {
|
for i, recp := range recps {
|
||||||
if i >= max {
|
if i >= max {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := c.Users.GetByName(strings.TrimSpace(recp))
|
u, err := c.Users.GetByName(recp)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return c.LocalError("One of the recipients doesn't exist", w, r, user)
|
return c.LocalError("One of the recipients doesn't exist", w, r, user)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<form action="/user/convos/create/submit/?s={{.CurrentUser.Session}}" method="post">
|
<form action="/user/convos/create/submit/?s={{.CurrentUser.Session}}" method="post">
|
||||||
<div class="formrow real_first_child">
|
<div class="formrow real_first_child">
|
||||||
<div class="formitem formlabel"><a>{{lang "create_convo_recp"}}</a></div>
|
<div class="formitem formlabel"><a>{{lang "create_convo_recp"}}</a></div>
|
||||||
<div class="formitem"><input name="recp" type="text" /></div>
|
<div class="formitem"><input name="recp" type="text"/></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
<div class="formitem"><textarea name="body"></textarea></div>
|
<div class="formitem"><textarea name="body"></textarea></div>
|
||||||
|
@ -27,7 +27,8 @@
|
||||||
{{range .Convos}}
|
{{range .Convos}}
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
<span class="to_left">
|
<span class="to_left">
|
||||||
<a href="/user/convo/{{.ID}}">{{range .Users}}<span class="convos_item_user">{{.Name}}</span> {{end}}</a></span></a>
|
{{if .OneOnOne}}{{range .ShortUsers}}<img class="bgsub" src="{{.MicroAvatar}}" height=48 width=48/>{{end}}{{end}}
|
||||||
|
<a href="/user/convo/{{.ID}}">{{range .ShortUsers}}<span class="convos_item_user">{{.Name}}</span> {{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 style="clear:both;"></div>
|
<div style="clear:both;"></div>
|
||||||
|
|
|
@ -1,6 +1,24 @@
|
||||||
.rowhead .rowitem {
|
.rowhead .rowitem, .convos_list .rowitem {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
.convos_list .to_left {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.convos_list .rowitem img {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
.convos_list .rowitem a {
|
||||||
|
/*margin-top: auto;
|
||||||
|
margin-bottom: auto;*/
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.convos_list .to_right {
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
.convos_item_user:not(:last-child):after {
|
.convos_item_user:not(:last-child):after {
|
||||||
content: ",";
|
content: ",";
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,23 @@
|
||||||
.close_form {
|
.close_form {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
.convos_list .to_left {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.convos_list .rowitem img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
.convos_list .rowitem a {
|
||||||
|
/*margin-top: auto;
|
||||||
|
margin-bottom: auto;*/
|
||||||
|
}
|
||||||
|
.convos_list .to_right {
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
.convos_item_user:not(:last-child):after {
|
.convos_item_user:not(:last-child):after {
|
||||||
content: ",";
|
content: ",";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue