mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-03-22 09:59:37 +00:00
more message container improvements
This commit is contained in:
parent
2f64bb509a
commit
d921470f00
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
5
main.go
5
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()
|
||||
}
|
||||
|
|
48
profile.go
48
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))
|
||||
}()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue