package session import ( "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-gtk/internal/keyring" "github.com/diamondburned/cchat-gtk/internal/ui/primitives" "github.com/diamondburned/cchat-gtk/internal/ui/rich" "github.com/diamondburned/cchat-gtk/internal/ui/service/breadcrumb" "github.com/diamondburned/cchat-gtk/internal/ui/service/session/server" "github.com/diamondburned/cchat/text" "github.com/diamondburned/imgutil" "github.com/gotk3/gotk3/gtk" ) const IconSize = 32 // Controller extends server.RowController to add session. type Controller interface { MessageRowSelected(*Row, *server.Row, cchat.ServerMessage) RestoreSession(*Row, keyring.Session) // async RemoveSession(id string) } type Row struct { *gtk.Box Button *rich.ToggleButtonImage Session cchat.Session Servers *server.Children menu *gtk.Menu retry *gtk.MenuItem ctrl Controller parent breadcrumb.Breadcrumber // nil after calling SetSession() krs keyring.Session } func New(parent breadcrumb.Breadcrumber, ses cchat.Session, ctrl Controller) *Row { row := new(parent, ctrl) row.SetSession(ses) return row } func NewLoading(parent breadcrumb.Breadcrumber, name string, ctrl Controller) *Row { row := new(parent, ctrl) row.Button.SetLabelUnsafe(text.Rich{Content: name}) row.setLoading() return row } func new(parent breadcrumb.Breadcrumber, ctrl Controller) *Row { row := &Row{ ctrl: ctrl, parent: parent, } row.Servers = server.NewChildren(parent, row) row.Button = rich.NewToggleButtonImage(text.Rich{}) row.Button.Box.SetHAlign(gtk.ALIGN_START) row.Button.Image.AddProcessors(imgutil.Round(true)) // Set the loading icon. row.Button.SetRelief(gtk.RELIEF_NONE) // On click, toggle reveal. row.Button.Connect("clicked", func() { revealed := !row.Servers.GetRevealChild() row.Servers.SetRevealChild(revealed) row.Button.SetActive(revealed) }) row.Button.Show() row.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) row.Box.SetMarginStart(server.ChildrenMargin) row.Box.PackStart(row.Button, false, false, 0) row.Box.Show() primitives.AddClass(row.Box, "session") row.menu, _ = gtk.MenuNew() primitives.BindMenu(row.menu, row.Button) row.retry = primitives.HiddenMenuItem("Retry", func() { // Show the loading stuff. row.setLoading() // Reuse the failed keyring session provided. As this variable is reset // after a success, it relies of the button not triggering. ctrl.RestoreSession(row, row.krs) }) row.retry.SetSensitive(false) primitives.AppendMenuItems(row.menu, []*gtk.MenuItem{ row.retry, primitives.MenuItem("Remove", func() { ctrl.RemoveSession(row.Session.ID()) }), }) return row } func (r *Row) setLoading() { // set the loading icon r.Button.Image.SetPlaceholderIcon("content-loading-symbolic", IconSize) // restore the old label's color r.Button.SetLabelUnsafe(r.Button.GetLabel()) // blur - set the color darker r.SetSensitive(false) } // KeyringSession returns a keyring session, or nil if the session cannot be // saved. func (r *Row) KeyringSession() *keyring.Session { return keyring.GetSession(r.Session, r.Button.GetText()) } func (r *Row) SetSession(ses cchat.Session) { // Disable the retry button. r.retry.SetSensitive(false) r.retry.Hide() r.Session = ses r.Servers.SetServerList(ses) r.Button.Image.SetPlaceholderIcon("user-available-symbolic", IconSize) r.Box.PackStart(r.Servers, false, false, 0) r.SetSensitive(true) // Set the session's name to the button. r.Button.Try(ses, "session") // Wipe the keyring session off. r.krs = keyring.Session{} } func (r *Row) SetFailed(krs keyring.Session, err error) { // Set the failed keyring session. r.krs = krs // Allow the retry button to be pressed. r.retry.SetSensitive(true) r.retry.Show() r.SetSensitive(true) r.SetTooltipText(err.Error()) // Intentional side-effect of not changing the actual label state. r.Button.Label.SetMarkup(rich.MakeRed(r.Button.GetLabel())) } func (r *Row) MessageRowSelected(server *server.Row, smsg cchat.ServerMessage) { r.ctrl.MessageRowSelected(r, server, smsg) } func (r *Row) Breadcrumb() breadcrumb.Breadcrumb { return breadcrumb.Try(r.parent, r.Button.GetLabel().Content) }