better collapsible rules; bumped discord
This commit is contained in:
parent
9cb3f71e77
commit
776b58b6dc
2
go.mod
2
go.mod
|
@ -13,7 +13,7 @@ require (
|
|||
github.com/Xuanwo/go-locale v1.0.0
|
||||
github.com/alecthomas/chroma v0.7.3
|
||||
github.com/diamondburned/cchat v0.3.17
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20210106052627-13f87a764b33
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20210107010729-8edbbcc24992
|
||||
github.com/diamondburned/cchat-mock v0.0.0-20201115033644-df8d1b10f9db
|
||||
github.com/diamondburned/gspell v0.0.0-20201229064336-e43698fd5828
|
||||
github.com/diamondburned/handy v0.0.0-20201229063418-ec23c1370374
|
||||
|
|
2
go.sum
2
go.sum
|
@ -94,6 +94,8 @@ github.com/diamondburned/cchat-discord v0.0.0-20210106050254-2c7b56ab6e00 h1:4LO
|
|||
github.com/diamondburned/cchat-discord v0.0.0-20210106050254-2c7b56ab6e00/go.mod h1:YjAiN/4zl5Z84Atsjw1h7G3wjcFxnXwk/qIYyLCjII8=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20210106052627-13f87a764b33 h1:Pl7cAMfz20UamYIeIKNFsD/GdgttEdQWCViEGVFqIgw=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20210106052627-13f87a764b33/go.mod h1:g+RqSLt/ccZZVZbsukOiCwQCqKM/9HfHMJVboIUW+tU=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20210107010729-8edbbcc24992 h1:njLrtjrCpaU/AVVj5TS7mhuDrm60f8CBFqvDFdpH4uI=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20210107010729-8edbbcc24992/go.mod h1:g+RqSLt/ccZZVZbsukOiCwQCqKM/9HfHMJVboIUW+tU=
|
||||
github.com/diamondburned/cchat-mock v0.0.0-20201115033644-df8d1b10f9db h1:VQI2PdbsdsRJ7d669kp35GbCUO44KZ0Xfqdu4o/oqVg=
|
||||
github.com/diamondburned/cchat-mock v0.0.0-20201115033644-df8d1b10f9db/go.mod h1:M87kjNzWVPlkZycFNzpGPKQXzkHNnZphuwMf3E9ckgc=
|
||||
github.com/diamondburned/gotk3 v0.0.0-20201209182406-e7291341a091 h1:lQpSWzbi3rQf66aMSip/rIypasIFwqCqF0Wfn5og6gw=
|
||||
|
|
|
@ -55,6 +55,8 @@ type Container interface {
|
|||
LatestMessageFrom(authorID string) (msgID string, ok bool)
|
||||
// Message finds and returns the message, if any.
|
||||
Message(id cchat.ID, nonce string) MessageRow
|
||||
// FindMessage finds a message that satisfies the given callback.
|
||||
FindMessage(isMessage func(MessageRow) bool) MessageRow
|
||||
|
||||
// Highlight temporarily highlights the given message for a short while.
|
||||
Highlight(msg MessageRow)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cozy
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container"
|
||||
|
@ -49,7 +51,7 @@ var messageConstructors = container.Constructor{
|
|||
func NewMessage(
|
||||
msg cchat.MessageCreate, before container.MessageRow) container.MessageRow {
|
||||
|
||||
if gridMessageIsAuthor(before, msg.Author()) {
|
||||
if isCollapsible(before, msg) {
|
||||
return NewCollapsedMessage(msg)
|
||||
}
|
||||
|
||||
|
@ -59,7 +61,7 @@ func NewMessage(
|
|||
func NewPresendMessage(
|
||||
msg input.PresendMessage, before container.MessageRow) container.PresendMessageRow {
|
||||
|
||||
if gridMessageIsAuthor(before, msg.Author()) {
|
||||
if isCollapsible(before, msg) {
|
||||
return NewCollapsedSendingMessage(msg)
|
||||
}
|
||||
|
||||
|
@ -105,14 +107,34 @@ func (c *Container) reuseAvatar(authorID, avatarURL string, full *FullMessage) {
|
|||
|
||||
// lastMessageIsAuthor removed - assuming index before insertion is harmful.
|
||||
|
||||
func gridMessageIsAuthor(gridMsg container.MessageRow, author cchat.Author) bool {
|
||||
if gridMsg == nil {
|
||||
type authoredMessage interface {
|
||||
cchat.MessageHeader
|
||||
Author() cchat.Author
|
||||
}
|
||||
|
||||
var (
|
||||
_ authoredMessage = (cchat.MessageCreate)(nil)
|
||||
_ authoredMessage = (input.PresendMessage)(nil)
|
||||
_ authoredMessage = (container.MessageRow)(nil)
|
||||
_ authoredMessage = (container.PresendMessageRow)(nil)
|
||||
)
|
||||
|
||||
const splitDuration = 10 * time.Minute
|
||||
|
||||
// isCollapsible returns true if the given lastMsg has matching conditions with
|
||||
// the given msg.
|
||||
func isCollapsible(lastMsg container.MessageRow, msg authoredMessage) bool {
|
||||
if lastMsg == nil || msg == nil {
|
||||
return false
|
||||
}
|
||||
leftAuthor := gridMsg.Author()
|
||||
|
||||
lastAuthor := lastMsg.Author()
|
||||
thisAuthor := msg.Author()
|
||||
|
||||
return true &&
|
||||
leftAuthor.ID() == author.ID() &&
|
||||
leftAuthor.Name().String() == author.Name().String()
|
||||
lastAuthor.ID() == thisAuthor.ID() &&
|
||||
lastAuthor.Name().String() == thisAuthor.Name().String() &&
|
||||
lastMsg.Time().Add(splitDuration).After(msg.Time())
|
||||
}
|
||||
|
||||
func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
||||
|
@ -147,7 +169,7 @@ func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
|||
// second message.
|
||||
if first := c.ListContainer.FirstMessage(); first != nil && first.ID() == msg.ID() {
|
||||
// If the author is the same, then collapse.
|
||||
if sec := c.NthMessage(1); sec != nil && gridMessageIsAuthor(sec, msg.Author()) {
|
||||
if sec := c.NthMessage(1); sec != nil && isCollapsible(sec, msg) {
|
||||
c.compact(sec)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/diamondburned/cchat"
|
||||
|
@ -19,6 +21,44 @@ type messageKey struct {
|
|||
func nonceKey(nonce string) messageKey { return messageKey{nonce, true} }
|
||||
func idKey(id cchat.ID) messageKey { return messageKey{id, false} }
|
||||
|
||||
func parseKeyFromNamer(n primitives.Namer) messageKey {
|
||||
name, err := n.GetName()
|
||||
if err != nil {
|
||||
panic("BUG: failed to get primitive name: " + err.Error())
|
||||
}
|
||||
|
||||
parts := strings.SplitN(name, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return messageKey{id: name}
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case "id":
|
||||
return messageKey{id: parts[1]}
|
||||
case "nonce":
|
||||
return messageKey{id: parts[1], nonce: true}
|
||||
default:
|
||||
panic("Unknown prefix in row name " + parts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func (key messageKey) expand() (id, nonce string) {
|
||||
if key.nonce {
|
||||
return "", key.id
|
||||
}
|
||||
return key.id, ""
|
||||
}
|
||||
|
||||
func (key messageKey) name() string {
|
||||
if key.nonce {
|
||||
return "nonce:" + key.id
|
||||
}
|
||||
return "id:" + key.id
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface.
|
||||
func (key messageKey) String() string { return key.name() }
|
||||
|
||||
var messageListCSS = primitives.PrepareClassCSS("message-list", `
|
||||
.message-list { background: transparent; }
|
||||
`)
|
||||
|
@ -59,9 +99,9 @@ func NewListStore(ctrl Controller, constr Constructor) *ListStore {
|
|||
return
|
||||
}
|
||||
|
||||
id, _ := r.GetName()
|
||||
id := parseKeyFromNamer(r)
|
||||
|
||||
msg := listStore.Message(id, "")
|
||||
msg := listStore.Message(id.expand())
|
||||
if msg == nil {
|
||||
return
|
||||
}
|
||||
|
@ -134,18 +174,18 @@ func (c *ListStore) around(aroundID cchat.ID) (before, after *messageRow) {
|
|||
var next bool
|
||||
|
||||
primitives.ForeachChildBackwards(c.ListBox, func(v interface{}) (stop bool) {
|
||||
id := primitives.GetName(v.(primitives.Namer))
|
||||
id := parseKeyFromNamer(v.(primitives.Namer))
|
||||
if next {
|
||||
after = c.message(id, "")
|
||||
after = c.message(id.expand())
|
||||
return true
|
||||
}
|
||||
if id == aroundID {
|
||||
if !id.nonce && id.id == aroundID {
|
||||
before = last
|
||||
next = true
|
||||
return false
|
||||
}
|
||||
|
||||
last = c.message(id, "")
|
||||
last = c.message(id.expand())
|
||||
return false
|
||||
})
|
||||
|
||||
|
@ -176,9 +216,9 @@ func (c *ListStore) findIndex(findID cchat.ID) (found *messageRow, index int) {
|
|||
index = c.MessagesLen() - 1
|
||||
|
||||
primitives.ForeachChildBackwards(c.ListBox, func(v interface{}) (stop bool) {
|
||||
id := primitives.GetName(v.(primitives.Namer))
|
||||
if id == findID {
|
||||
found = c.message(findID, "")
|
||||
id := parseKeyFromNamer(v.(primitives.Namer))
|
||||
if !id.nonce && id.id == findID {
|
||||
found = c.message(id.expand())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -201,8 +241,8 @@ func (c *ListStore) findMessage(presend bool, fn func(*messageRow) bool) (*messa
|
|||
var i = c.MessagesLen() - 1
|
||||
|
||||
primitives.ForeachChildBackwards(c.ListBox, func(v interface{}) (stop bool) {
|
||||
id := primitives.GetName(v.(primitives.Namer))
|
||||
gridMsg := c.message(id, "")
|
||||
id := parseKeyFromNamer(v.(primitives.Namer))
|
||||
gridMsg := c.message(id.expand())
|
||||
|
||||
// If gridMsg is actually nil, then we have bigger issues.
|
||||
if gridMsg != nil {
|
||||
|
@ -240,8 +280,8 @@ func (c *ListStore) nthMessage(n int) *messageRow {
|
|||
return nil
|
||||
}
|
||||
|
||||
id := primitives.GetName(v.(primitives.Namer))
|
||||
return c.message(id, "")
|
||||
id := parseKeyFromNamer(v.(primitives.Namer))
|
||||
return c.message(id.expand())
|
||||
}
|
||||
|
||||
// NthMessage returns the nth message.
|
||||
|
@ -280,6 +320,7 @@ func (c *ListStore) message(msgID cchat.ID, nonce string) *messageRow {
|
|||
// Replace the nonce key with ID.
|
||||
delete(c.messages, nonceKey(nonce))
|
||||
c.messages[idKey(msgID)] = m
|
||||
c.bindMessage(m)
|
||||
|
||||
// Set the right ID.
|
||||
m.presend.SetDone(msgID)
|
||||
|
@ -301,12 +342,34 @@ func (c *ListStore) ensureEmpty() {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *ListStore) bindMessage(msgc *messageRow) {
|
||||
// Bind the message ID to the row so we can easily do a lookup.
|
||||
var key messageKey
|
||||
if id := msgc.ID(); id != "" {
|
||||
key.id = id
|
||||
} else {
|
||||
key.id = msgc.Nonce()
|
||||
key.nonce = true
|
||||
}
|
||||
|
||||
msgc.Row().SetName(key.name())
|
||||
msgc.SetReferenceHighlighter(c)
|
||||
c.Controller.BindMenu(msgc.MessageRow)
|
||||
}
|
||||
|
||||
// AddPresendMessage inserts an input.PresendMessage into the container and
|
||||
// returning a wrapped widget interface.
|
||||
func (c *ListStore) AddPresendMessage(msg input.PresendMessage) PresendMessageRow {
|
||||
c.ensureEmpty()
|
||||
|
||||
before := c.LastMessage()
|
||||
|
||||
if before != nil {
|
||||
log.Println("Found before:", before.Author().Name())
|
||||
} else {
|
||||
log.Println("Before is nil")
|
||||
}
|
||||
|
||||
presend := c.Construct.NewPresendMessage(msg, before)
|
||||
|
||||
msgc := &messageRow{
|
||||
|
@ -319,14 +382,9 @@ func (c *ListStore) AddPresendMessage(msg input.PresendMessage) PresendMessageRo
|
|||
// Set the NONCE into the message map.
|
||||
c.messages[nonceKey(msgc.Nonce())] = msgc
|
||||
|
||||
return presend
|
||||
}
|
||||
c.bindMessage(msgc)
|
||||
|
||||
func (c *ListStore) bindMessage(msgc *messageRow) {
|
||||
// Bind the message ID to the row so we can easily do a lookup.
|
||||
msgc.Row().SetName(msgc.ID())
|
||||
msgc.SetReferenceHighlighter(c)
|
||||
c.Controller.BindMenu(msgc.MessageRow)
|
||||
return presend
|
||||
}
|
||||
|
||||
// Many attempts were made to have CreateMessageUnsafe return an index. That is
|
||||
|
@ -436,8 +494,8 @@ func (c *ListStore) DeleteEarliest(n int) {
|
|||
// Since container/list nils out the next element, we can't just call Next
|
||||
// after deleting, so we have to call Next manually before Removing.
|
||||
primitives.ForeachChild(c.ListBox, func(v interface{}) (stop bool) {
|
||||
id := primitives.GetName(v.(primitives.Namer))
|
||||
gridMsg := c.message(id, "")
|
||||
id := parseKeyFromNamer(v.(primitives.Namer))
|
||||
gridMsg := c.message(id.expand())
|
||||
|
||||
if id := gridMsg.ID(); id != "" {
|
||||
delete(c.messages, idKey(id))
|
||||
|
|
|
@ -22,6 +22,7 @@ type Controller interface {
|
|||
AddPresendMessage(msg PresendMessage) (onErr func(error))
|
||||
LatestMessageFrom(userID cchat.ID) (messageID cchat.ID, ok bool)
|
||||
MessageAuthor(msgID cchat.ID) cchat.Author
|
||||
Author(authorID cchat.ID) cchat.Author
|
||||
}
|
||||
|
||||
// LabelBorrower is an interface that allows the caller to borrow a label.
|
||||
|
|
|
@ -65,7 +65,7 @@ func (f *Field) sendInput() {
|
|||
|
||||
// Derive the author. Prefer the author of the current user from the message
|
||||
// buffer over the one in the username feed, unless we can't find any.
|
||||
var author cchat.Author = f.ctrl.MessageAuthor(f.UserID)
|
||||
var author = f.ctrl.Author(f.UserID)
|
||||
if author == nil {
|
||||
author = newAuthor(f)
|
||||
}
|
||||
|
|
|
@ -397,6 +397,7 @@ func (v *View) AuthorEvent(author cchat.Author) {
|
|||
}
|
||||
}
|
||||
|
||||
// MessageAuthor returns the author from the message with the given ID.
|
||||
func (v *View) MessageAuthor(msgID cchat.ID) cchat.Author {
|
||||
msg := v.Container.Message(msgID, "")
|
||||
if msg == nil {
|
||||
|
@ -406,6 +407,18 @@ func (v *View) MessageAuthor(msgID cchat.ID) cchat.Author {
|
|||
return msg.Author()
|
||||
}
|
||||
|
||||
// Author returns the author from the message list with the given author ID.
|
||||
func (v *View) Author(authorID cchat.ID) cchat.Author {
|
||||
msg := v.Container.FindMessage(func(msg container.MessageRow) bool {
|
||||
return msg.Author().ID() == authorID
|
||||
})
|
||||
if msg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return msg.Author()
|
||||
}
|
||||
|
||||
// LatestMessageFrom returns the last message ID with that author.
|
||||
func (v *View) LatestMessageFrom(userID string) (msgID string, ok bool) {
|
||||
return v.Container.LatestMessageFrom(userID)
|
||||
|
|
|
@ -57,13 +57,11 @@ func NthChild(w Container, n int) interface{} {
|
|||
children := w.GetChildren()
|
||||
defer children.Free()
|
||||
|
||||
// Bound check!
|
||||
if n < 0 || int(children.Length()) >= n {
|
||||
return nil
|
||||
}
|
||||
length := int(children.Length())
|
||||
|
||||
if n == 0 {
|
||||
return children.Data()
|
||||
// Bound check!
|
||||
if !(0 <= n && n < length) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return children.NthData(uint(n))
|
||||
|
|
Loading…
Reference in New Issue