2020-07-14 07:24:55 +00:00
|
|
|
package session
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/humanize"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/spinner"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
2020-08-28 07:16:03 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
2020-07-14 07:24:55 +00:00
|
|
|
"github.com/gotk3/gotk3/gtk"
|
|
|
|
"github.com/gotk3/gotk3/pango"
|
|
|
|
)
|
|
|
|
|
|
|
|
const FaceSize = 48 // gtk.ICON_SIZE_DIALOG
|
2020-08-28 07:16:03 +00:00
|
|
|
const ListWidth = 200
|
2020-07-14 07:24:55 +00:00
|
|
|
|
|
|
|
// Servers wraps around a list of servers inherited from Children. It's the
|
|
|
|
// container that's displayed on the right of the service sidebar.
|
|
|
|
type Servers struct {
|
|
|
|
*gtk.Box
|
|
|
|
Children *server.Children
|
|
|
|
spinner *spinner.Boxed // non-nil if loading.
|
|
|
|
|
|
|
|
// state
|
2020-10-15 06:32:11 +00:00
|
|
|
ServerList cchat.Lister
|
2020-07-14 07:24:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var toplevelCSS = primitives.PrepareClassCSS("top-level", `
|
2020-08-29 01:42:28 +00:00
|
|
|
.top-level {
|
|
|
|
margin: 0 3px;
|
|
|
|
margin-top: 3px;
|
|
|
|
}
|
2020-07-14 07:24:55 +00:00
|
|
|
`)
|
|
|
|
|
2020-07-17 18:17:21 +00:00
|
|
|
func NewServers(p traverse.Breadcrumber, ctrl server.Controller) *Servers {
|
2020-07-14 07:24:55 +00:00
|
|
|
c := server.NewChildren(p, ctrl)
|
|
|
|
c.SetMarginStart(0) // children is top level; there is no main row
|
2020-08-28 07:16:03 +00:00
|
|
|
c.SetVExpand(true)
|
2020-07-14 07:24:55 +00:00
|
|
|
c.Show()
|
|
|
|
toplevelCSS(c)
|
|
|
|
|
|
|
|
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
|
|
|
|
|
|
|
return &Servers{
|
|
|
|
Box: b,
|
|
|
|
Children: c,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Servers) Reset() {
|
|
|
|
// Reset isn't necessarily called while loading, so we do a check.
|
|
|
|
if s.spinner != nil {
|
|
|
|
s.spinner.Stop()
|
|
|
|
s.spinner = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the state.
|
|
|
|
s.ServerList = nil
|
|
|
|
// Remove all children.
|
|
|
|
primitives.RemoveChildren(s)
|
|
|
|
// Reset the children container.
|
|
|
|
s.Children.Reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsLoading returns true if the servers container is loading.
|
|
|
|
func (s *Servers) IsLoading() bool {
|
|
|
|
return s.spinner != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetList indicates that the server list has been loaded. Unlike
|
|
|
|
// server.Children, this method will load immediately.
|
2020-10-15 06:32:11 +00:00
|
|
|
func (s *Servers) SetList(slist cchat.Lister) {
|
2020-07-14 07:24:55 +00:00
|
|
|
primitives.RemoveChildren(s)
|
|
|
|
s.ServerList = slist
|
|
|
|
s.load()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Servers) load() {
|
|
|
|
// Return if we're loading.
|
|
|
|
if s.IsLoading() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark the servers list as loading.
|
|
|
|
s.setLoading()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err := s.ServerList.Servers(s.Children)
|
|
|
|
gts.ExecAsync(func() {
|
|
|
|
if err != nil {
|
|
|
|
s.setFailed(err)
|
|
|
|
} else {
|
|
|
|
s.setDone()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// setDone changes the view to show the servers.
|
|
|
|
func (s *Servers) setDone() {
|
|
|
|
primitives.RemoveChildren(s)
|
|
|
|
|
|
|
|
// stop the spinner.
|
|
|
|
s.spinner.Stop()
|
|
|
|
s.spinner = nil
|
|
|
|
|
|
|
|
s.Add(s.Children)
|
|
|
|
}
|
|
|
|
|
|
|
|
// setLoading shows a loading spinner. Use this after the session row is
|
|
|
|
// connected.
|
|
|
|
func (s *Servers) setLoading() {
|
|
|
|
primitives.RemoveChildren(s)
|
|
|
|
|
|
|
|
s.spinner = spinner.New()
|
|
|
|
s.spinner.SetSizeRequest(FaceSize, FaceSize)
|
|
|
|
s.spinner.Show()
|
|
|
|
s.spinner.Start()
|
|
|
|
|
|
|
|
s.Add(s.spinner)
|
|
|
|
}
|
|
|
|
|
|
|
|
// setFailed shows a sad face with the error. Use this when the session row has
|
|
|
|
// failed to load.
|
|
|
|
func (s *Servers) setFailed(err error) {
|
|
|
|
primitives.RemoveChildren(s)
|
|
|
|
|
|
|
|
// stop the spinner. Let this SEGFAULT if nil, as that's undefined behavior.
|
|
|
|
s.spinner.Stop()
|
|
|
|
s.spinner = nil
|
|
|
|
|
|
|
|
// Create a BLANK label for padding.
|
|
|
|
ltop, _ := gtk.LabelNew("")
|
|
|
|
ltop.Show()
|
|
|
|
|
|
|
|
// Create a retry button.
|
|
|
|
btn, _ := gtk.ButtonNewFromIconName("view-refresh-symbolic", gtk.ICON_SIZE_DIALOG)
|
|
|
|
btn.Show()
|
|
|
|
btn.Connect("clicked", s.load)
|
|
|
|
|
|
|
|
// Create a bottom label for the error itself.
|
|
|
|
lerr, _ := gtk.LabelNew("")
|
|
|
|
lerr.SetSingleLineMode(true)
|
|
|
|
lerr.SetEllipsize(pango.ELLIPSIZE_MIDDLE)
|
|
|
|
lerr.SetMarkup(fmt.Sprintf(
|
|
|
|
`<span color="red"><b>Error:</b> %s</span>`,
|
|
|
|
humanize.Error(err),
|
|
|
|
))
|
|
|
|
lerr.Show()
|
|
|
|
|
|
|
|
// Add these items into the box.
|
|
|
|
s.PackStart(ltop, false, false, 0)
|
|
|
|
s.PackStart(btn, false, false, 10) // pad
|
|
|
|
s.PackStart(lerr, false, false, 0)
|
|
|
|
}
|