more message container improvements

This commit is contained in:
diamondburned 2020-11-05 20:01:00 -08:00
parent 2f64bb509a
commit d921470f00
8 changed files with 70 additions and 82 deletions

View File

@ -37,6 +37,9 @@ type Container interface {
// Thread-unsafe methods.
// Reset resets the message container to its original state.
Reset()
// CreateMessageUnsafe creates a new message and returns the index that is
// the location the message is added to.
CreateMessageUnsafe(cchat.MessageCreate)

View File

@ -1,6 +1,9 @@
package cozy
import (
"context"
"runtime/pprof"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-gtk/internal/gts"
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container"
@ -126,42 +129,47 @@ func (c *Container) lastMessageIsAuthor(id string, offset int) bool {
return last != nil && last.AuthorID() == id
}
var createMessageLabel = pprof.Labels("cozy", "createMessage")
func (c *Container) CreateMessage(msg cchat.MessageCreate) {
gts.ExecAsync(func() {
// Create the message in the parent's handler. This handler will also
// wipe old messages.
c.GridContainer.CreateMessageUnsafe(msg)
pprof.Do(context.Background(), createMessageLabel, func(context.Context) {
// Did the handler wipe old messages? It will only do so if the user is
// scrolled to the bottom.
if c.GridContainer.CleanMessages() {
// We need to uncollapse the first (top) message. No length check is
// needed here, as we just inserted a message.
c.uncompact(c.FirstMessage())
}
// Create the message in the parent's handler. This handler will also
// wipe old messages.
c.GridContainer.CreateMessageUnsafe(msg)
switch msg.ID() {
// Should we collapse this message? Yes, if the current message is
// inserted at the end and its author is the same as the last author.
case c.GridContainer.LastMessage().ID():
if c.lastMessageIsAuthor(msg.Author().ID(), -1) {
c.compact(c.GridContainer.LastMessage())
// Did the handler wipe old messages? It will only do so if the user is
// scrolled to the bottom.
if c.GridContainer.CleanMessages() {
// We need to uncollapse the first (top) message. No length check is
// needed here, as we just inserted a message.
c.uncompact(c.FirstMessage())
}
// If we've prepended the message, then see if we need to collapse the
// second message.
case c.GridContainer.FirstMessage().ID():
if sec := c.NthMessage(1); sec != nil {
// If the author isn't the same, then ignore.
if sec.AuthorID() != msg.Author().ID() {
return
switch msg.ID() {
// Should we collapse this message? Yes, if the current message is
// inserted at the end and its author is the same as the last author.
case c.GridContainer.LastMessage().ID():
if c.lastMessageIsAuthor(msg.Author().ID(), -1) {
c.compact(c.GridContainer.LastMessage())
}
// The author is the same; collapse.
c.compact(sec)
}
}
// If we've prepended the message, then see if we need to collapse the
// second message.
case c.GridContainer.FirstMessage().ID():
if sec := c.NthMessage(1); sec != nil {
// If the author isn't the same, then ignore.
if sec.AuthorID() != msg.Author().ID() {
return
}
// The author is the same; collapse.
c.compact(sec)
}
}
})
})
}

View File

@ -17,6 +17,8 @@ type GridStore struct {
Construct Constructor
Controller Controller
resetMe bool
messages map[string]*gridMessage
messageList *list.List
}
@ -35,11 +37,17 @@ func NewGridStore(constr Constructor, ctrl Controller) *GridStore {
Grid: grid,
Construct: constr,
Controller: ctrl,
messages: map[string]*gridMessage{},
messages: make(map[string]*gridMessage, BacklogLimit+1),
messageList: list.New(),
}
}
func (c *GridStore) Reset() {
primitives.RemoveChildren(c.Grid)
c.messages = make(map[string]*gridMessage, BacklogLimit+1)
c.messageList = list.New()
}
func (c *GridStore) MessagesLen() int {
return c.messageList.Len()
}

View File

@ -30,7 +30,6 @@ func New(parent gtk.IWidget, placeholder gtk.IWidget) *FaceView {
stack, _ := gtk.StackNew()
stack.SetTransitionType(gtk.STACK_TRANSITION_TYPE_CROSSFADE)
stack.SetTransitionDuration(75)
stack.AddNamed(parent, "main")
stack.AddNamed(placeholder, "placeholder")
stack.AddNamed(c, "face")
@ -45,21 +44,21 @@ func New(parent gtk.IWidget, placeholder gtk.IWidget) *FaceView {
// Reset brings the view to an empty box.
func (v *FaceView) Reset() {
v.ensurePlaceholderDestroyed()
v.Loading.Spinner.Stop()
v.Stack.SetVisibleChildName("empty")
v.ensurePlaceholderDestroyed()
}
func (v *FaceView) SetMain() {
v.ensurePlaceholderDestroyed()
v.Loading.Spinner.Stop()
v.Stack.SetVisibleChildName("main")
v.ensurePlaceholderDestroyed()
}
func (v *FaceView) SetLoading() {
v.ensurePlaceholderDestroyed()
v.Loading.Spinner.Start()
v.Stack.SetVisibleChildName("loading")
v.ensurePlaceholderDestroyed()
}
func (v *FaceView) SetError(err error) {

View File

@ -93,7 +93,11 @@ var messageStack = primitives.PrepareClassCSS("message-stack", `
var messageScroller = primitives.PrepareClassCSS("message-scroller", ``)
func NewView(c Controller) *View {
view := &View{ctrl: c}
view := &View{
ctrl: c,
contType: -1, // force recreate
}
view.Typing = typing.New()
view.Typing.Show()
@ -188,6 +192,13 @@ func NewView(c Controller) *View {
}
func (v *View) createMessageContainer() {
// If we still want the same type of message container, then we don't need
// to remake a new one.
if v.contType == msgIndex {
v.Container.Reset()
return
}
// Remove the old message container.
if v.Container != nil {
v.MsgBox.Remove(v.Container)

View File

@ -172,12 +172,12 @@ func (app *App) GoBack() {
func (app *App) OnMessageBusy() {
// Disable the server list because we don't want the user to switch around
// while we're loading.
gts.App.Window.SetSensitive(false)
app.Services.SetSensitive(false)
}
func (app *App) OnMessageDone() {
// Re-enable the server list.
gts.App.Window.SetSensitive(true)
app.Services.SetSensitive(true)
}
func (app *App) AuthenticateSession(list *service.List, ssvc *service.Service) {

View File

@ -11,9 +11,6 @@ import (
_ "github.com/diamondburned/cchat-mock"
)
// destructor is used for debugging and profiling.
var destructor = func() {}
func main() {
gts.Main(func() gts.MainApplication {
var app = ui.NewApplication()
@ -36,6 +33,4 @@ func main() {
return app
})
destructor()
}

View File

@ -1,51 +1,15 @@
// +build prof
// Code generated by goprofiler. DO NOT EDIT.
package main
import (
"net/http"
_ "net/http/pprof"
_ "github.com/ianlancetaylor/cgosymbolizer"
)
import "C"
const ProfileAddr = "localhost:49583"
func init() {
C.HeapProfilerStart()
destructor = func() { C.HeapProfilerStop() }
// runtime.SetBlockProfileRate(1)
// go func() {
// log.Println("Listening to profiler at", ProfileAddr)
// if err := http.ListenAndServe(ProfileAddr, nil); err != nil {
// log.Error(errors.Wrap(err, "Failed to start profiling HTTP server"))
// }
// }()
// f, _ := os.Create("/tmp/cchat.pprof")
// p := pprof.Lookup("block")
// destructor = func() {
// log.Println("==destructor==")
// if err := p.WriteTo(f, 2); err != nil {
// log.Println("Profile writeTo error:", err)
// }
// f.Close()
// }
// f, _ := os.Create("/tmp/cchat.pprof")
// if err := pprof.StartCPUProfile(f); err != nil {
// panic(err)
// }
// destructor = func() {
// pprof.StopCPUProfile()
// f.Close()
// }
go func() {
println("Serving HTTP at 127.0.0.1:48574 for profiler at /debug/pprof")
panic(http.ListenAndServe("127.0.0.1:48574", nil))
}()
}