diff --git a/internal/ui/messages/container/container.go b/internal/ui/messages/container/container.go index 67b4a56..9bcf200 100644 --- a/internal/ui/messages/container/container.go +++ b/internal/ui/messages/container/container.go @@ -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) diff --git a/internal/ui/messages/container/cozy/cozy.go b/internal/ui/messages/container/cozy/cozy.go index b846fa7..ebdc47a 100644 --- a/internal/ui/messages/container/cozy/cozy.go +++ b/internal/ui/messages/container/cozy/cozy.go @@ -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) + } + } + + }) }) } diff --git a/internal/ui/messages/container/grid.go b/internal/ui/messages/container/grid.go index e1f0dff..d37b7cd 100644 --- a/internal/ui/messages/container/grid.go +++ b/internal/ui/messages/container/grid.go @@ -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() } diff --git a/internal/ui/messages/sadface/sadface.go b/internal/ui/messages/sadface/sadface.go index b89ba22..64d39c9 100644 --- a/internal/ui/messages/sadface/sadface.go +++ b/internal/ui/messages/sadface/sadface.go @@ -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) { diff --git a/internal/ui/messages/view.go b/internal/ui/messages/view.go index 50be090..d8bc03a 100644 --- a/internal/ui/messages/view.go +++ b/internal/ui/messages/view.go @@ -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) diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 275d6f2..98805d1 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -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) { diff --git a/main.go b/main.go index f5edb4c..76ba7ea 100644 --- a/main.go +++ b/main.go @@ -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() } diff --git a/profile.go b/profile.go index ce57450..1450c3c 100644 --- a/profile.go +++ b/profile.go @@ -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)) + }() }