better collapsible rules; bumped discord

This commit is contained in:
diamondburned 2021-01-06 17:12:10 -08:00
parent 9cb3f71e77
commit 776b58b6dc
9 changed files with 134 additions and 38 deletions

2
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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))

View File

@ -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.

View File

@ -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)
}

View File

@ -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)

View File

@ -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))