mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-03-22 09:59:37 +00:00
Better traversal; added persistent list expansion state
This commit is contained in:
parent
8838b8e0d8
commit
35c186580b
|
@ -90,7 +90,25 @@ func Save() error {
|
|||
}
|
||||
|
||||
// Restore the global config. IsNotExist is not an error and will not be
|
||||
// returned.
|
||||
func Restore() error {
|
||||
return UnmarshalFromFile(ConfigFile, §ions)
|
||||
// logged.
|
||||
func Restore() {
|
||||
if err := UnmarshalFromFile(ConfigFile, §ions); err != nil {
|
||||
log.Error(errors.Wrap(err, "Failed to unmarshal main config.json"))
|
||||
}
|
||||
|
||||
log.Printlnf("To restore: %#v", toRestore)
|
||||
|
||||
for path, v := range toRestore {
|
||||
if err := UnmarshalFromFile(path, v); err != nil {
|
||||
log.Error(errors.Wrapf(err, "Failed to unmarshal %s", path))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var toRestore = map[string]interface{}{}
|
||||
|
||||
// RegisterConfig adds the config filename into the registry of value pointers
|
||||
// to unmarshal configs to.
|
||||
func RegisterConfig(filename string, jsonValue interface{}) {
|
||||
toRestore[filename] = jsonValue
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package config
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -33,6 +34,13 @@ func __init() {
|
|||
}
|
||||
}
|
||||
|
||||
// PrettyMarshal pretty marshals v into dst as formatted JSON.
|
||||
func PrettyMarshal(dst io.Writer, v interface{}) error {
|
||||
enc := json.NewEncoder(dst)
|
||||
enc.SetIndent("", "\t")
|
||||
return enc.Encode(v)
|
||||
}
|
||||
|
||||
// DirPath returns the config directory.
|
||||
func DirPath() string {
|
||||
// Ensure that files and folders are initialized.
|
||||
|
@ -41,6 +49,24 @@ func DirPath() string {
|
|||
return dirPath
|
||||
}
|
||||
|
||||
// SaveToFile saves the given bytes into the given filename. The filename will
|
||||
// be prepended with the config directory.
|
||||
func SaveToFile(file string, v []byte) error {
|
||||
file = filepath.Join(DirPath(), file)
|
||||
|
||||
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_SYNC|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to open file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(v); err != nil {
|
||||
return errors.Wrap(err, "Failed to write")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalToFile marshals the given interface into the given filename. The
|
||||
// filename will be prepended with the config directory.
|
||||
func MarshalToFile(file string, from interface{}) error {
|
||||
|
@ -52,10 +78,7 @@ func MarshalToFile(file string, from interface{}) error {
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", "\t")
|
||||
|
||||
if err := enc.Encode(from); err != nil {
|
||||
if err := PrettyMarshal(f, from); err != nil {
|
||||
return errors.Wrap(err, "Failed to marshal given struct")
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ func (h *Header) SetBreadcrumber(b traverse.Breadcrumber) {
|
|||
return
|
||||
}
|
||||
|
||||
h.breadcrumbs = b.Breadcrumb()
|
||||
h.breadcrumbs = traverse.TryBreadcrumb(b)
|
||||
if len(h.breadcrumbs) < 2 {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Package config contains UI widgets and renderers for cchat's Configurator
|
||||
// interface.
|
||||
package config
|
||||
|
||||
import (
|
||||
|
|
|
@ -112,7 +112,7 @@ func (h *Header) SetBreadcrumber(b traverse.Breadcrumber) {
|
|||
return
|
||||
}
|
||||
|
||||
if crumb := b.Breadcrumb(); len(crumb) > 0 {
|
||||
if crumb := traverse.TryBreadcrumb(b); len(crumb) > 0 {
|
||||
h.SvcName.SetText(crumb[0])
|
||||
} else {
|
||||
h.SvcName.SetText("")
|
||||
|
|
109
internal/ui/service/savepath/savepath.go
Normal file
109
internal/ui/service/savepath/savepath.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package savepath
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/config"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// map of services to a list of list of IDs.
|
||||
var paths = make(pathMap)
|
||||
|
||||
type pathMap map[string]pathMap
|
||||
|
||||
const configName = "savepaths.json"
|
||||
|
||||
func init() {
|
||||
config.RegisterConfig(configName, &paths)
|
||||
}
|
||||
|
||||
// ActiveSetter is an interface for all widgets that allow setting the active
|
||||
// state.
|
||||
type ActiveSetter interface {
|
||||
SetActive(bool)
|
||||
}
|
||||
|
||||
// Restore restores the expand state by calling SetActive. This is meant to be
|
||||
// used on a ToggledButton.
|
||||
func Restore(b traverse.Breadcrumber, asetter ActiveSetter) {
|
||||
if IsExpanded(b) {
|
||||
asetter.SetActive(true)
|
||||
}
|
||||
}
|
||||
|
||||
// IsExpanded returns true if the current breadcrumb node is expanded.
|
||||
func IsExpanded(b traverse.Breadcrumber) bool {
|
||||
var path = traverse.TryID(b)
|
||||
var node = paths
|
||||
|
||||
// Descend and traverse.
|
||||
var nest = 0
|
||||
for ; nest < len(path); nest++ {
|
||||
ch, ok := node[path[nest]]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
node = ch
|
||||
}
|
||||
|
||||
// Return true if there is available a path that at least matches with the
|
||||
// breadcrumb path.
|
||||
return nest == len(path)
|
||||
}
|
||||
|
||||
// SaveDelay is the delay to wait before saving.
|
||||
const SaveDelay = 5 * time.Second
|
||||
|
||||
var lastSaved int64
|
||||
|
||||
// Save saves the list of paths. This function is not thread-safe. It is also
|
||||
// non-blocking.
|
||||
func Save() {
|
||||
var now = time.Now().UnixNano()
|
||||
|
||||
if (lastSaved + int64(SaveDelay)) > now {
|
||||
return
|
||||
}
|
||||
|
||||
lastSaved = now
|
||||
|
||||
gts.AfterFunc(SaveDelay, func() {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Marshal in the same thread to avoid race conditions.
|
||||
if err := config.PrettyMarshal(&buf, paths); err != nil {
|
||||
log.Error(errors.Wrap(err, "Failed to marshal paths"))
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := config.SaveToFile(configName, buf.Bytes()); err != nil {
|
||||
log.Error(errors.Wrap(err, "Failed to save paths"))
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func Update(b traverse.Breadcrumber, expanded bool) {
|
||||
var path = traverse.TryID(b)
|
||||
var node = paths
|
||||
|
||||
// Descend and initialize.
|
||||
for i := 0; i < len(path); i++ {
|
||||
ch, ok := node[path[i]]
|
||||
if !ok {
|
||||
ch = make(pathMap)
|
||||
node[path[i]] = ch
|
||||
}
|
||||
|
||||
node = ch
|
||||
}
|
||||
|
||||
Save()
|
||||
}
|
|
@ -208,8 +208,12 @@ func (s *Service) MoveSession(id, movingID string) {
|
|||
s.SaveAllSessions()
|
||||
}
|
||||
|
||||
func (s *Service) Breadcrumb() traverse.Breadcrumb {
|
||||
return traverse.TryBreadcrumb(nil, s.service.Name().Content)
|
||||
func (s *Service) Breadcrumb() string {
|
||||
return s.service.Name().Content
|
||||
}
|
||||
|
||||
func (s *Service) ParentBreadcrumb() traverse.Breadcrumber {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) SaveAllSessions() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/loading"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/savepath"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
)
|
||||
|
@ -180,6 +181,9 @@ func (c *Children) LoadAll() {
|
|||
row.Show()
|
||||
c.Box.Add(row)
|
||||
}
|
||||
|
||||
// Restore expansion if possible.
|
||||
savepath.Restore(row, row.Button)
|
||||
}
|
||||
|
||||
// Check if we have icons.
|
||||
|
@ -227,6 +231,6 @@ func (c *Children) saveSelectedRow() (restore func()) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Children) Breadcrumb() traverse.Breadcrumb {
|
||||
return traverse.TryBreadcrumb(c.Parent)
|
||||
func (c *Children) ParentBreadcrumb() traverse.Breadcrumber {
|
||||
return c.Parent
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/roundimage"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/savepath"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/button"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||
"github.com/diamondburned/cchat/text"
|
||||
|
@ -24,7 +25,19 @@ func AssertUnhollow(hollower interface{ IsHollow() bool }) {
|
|||
}
|
||||
|
||||
type ServerRow struct {
|
||||
*Row
|
||||
*gtk.Box
|
||||
Avatar *roundimage.Avatar
|
||||
Button *button.ToggleButtonImage
|
||||
|
||||
parentcrumb traverse.Breadcrumber
|
||||
|
||||
// non-nil if server list and the function returns error
|
||||
childrenErr error
|
||||
|
||||
childrev *gtk.Revealer
|
||||
children *Children
|
||||
serverList cchat.ServerList
|
||||
|
||||
ctrl Controller
|
||||
Server cchat.Server
|
||||
|
||||
|
@ -49,7 +62,7 @@ var serverCSS = primitives.PrepareClassCSS("server", `
|
|||
// hollow children containers and rows for the given server.
|
||||
func NewHollowServer(p traverse.Breadcrumber, sv cchat.Server, ctrl Controller) *ServerRow {
|
||||
var serverRow = &ServerRow{
|
||||
Row: NewHollowRow(p),
|
||||
parentcrumb: p,
|
||||
ctrl: ctrl,
|
||||
Server: sv,
|
||||
cancelUnread: func() {},
|
||||
|
@ -84,12 +97,30 @@ func (r *ServerRow) Init() {
|
|||
}
|
||||
|
||||
// Initialize the row, which would fill up the button and others as well.
|
||||
r.Row.Init(r.Server.Name())
|
||||
r.Row.SetIconer(r.Server)
|
||||
serverCSS(r.Row)
|
||||
r.Avatar = roundimage.NewAvatar(IconSize)
|
||||
r.Avatar.SetText(r.Server.Name().Content)
|
||||
r.Avatar.Show()
|
||||
|
||||
btn := rich.NewCustomToggleButtonImage(r.Avatar, r.Server.Name())
|
||||
btn.Show()
|
||||
|
||||
r.Button = button.WrapToggleButtonImage(btn)
|
||||
r.Button.Box.SetHAlign(gtk.ALIGN_START)
|
||||
r.Button.SetRelief(gtk.RELIEF_NONE)
|
||||
r.Button.Show()
|
||||
|
||||
r.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||
r.Box.PackStart(r.Button, false, false, 0)
|
||||
serverCSS(r.Box)
|
||||
|
||||
// Ensure errors are displayed.
|
||||
r.childrenSetErr(r.childrenErr)
|
||||
|
||||
// Try to set an icon.
|
||||
r.SetIconer(r.Server)
|
||||
|
||||
// Connect the destroyer, if any.
|
||||
r.Row.Connect("destroy", r.cancelUnread)
|
||||
r.Connect("destroy", r.cancelUnread)
|
||||
|
||||
// Restore the read state.
|
||||
r.Button.SetUnreadUnsafe(r.unread, r.mentioned) // update with state
|
||||
|
@ -151,60 +182,13 @@ func (r *ServerRow) SetUnreadUnsafe(unread, mentioned bool) {
|
|||
traverse.TrySetUnread(r.parentcrumb, r.Server.ID(), r.unread, r.mentioned)
|
||||
}
|
||||
|
||||
type Row struct {
|
||||
*gtk.Box
|
||||
Avatar *roundimage.Avatar
|
||||
Button *button.ToggleButtonImage
|
||||
|
||||
parentcrumb traverse.Breadcrumber
|
||||
|
||||
// non-nil if server list and the function returns error
|
||||
childrenErr error
|
||||
|
||||
childrev *gtk.Revealer
|
||||
children *Children
|
||||
serverList cchat.ServerList
|
||||
}
|
||||
|
||||
func NewHollowRow(parent traverse.Breadcrumber) *Row {
|
||||
return &Row{
|
||||
parentcrumb: parent,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Row) IsHollow() bool {
|
||||
func (r *ServerRow) IsHollow() bool {
|
||||
return r.Box == nil
|
||||
}
|
||||
|
||||
// Init initializes the row from its initial hollow state. It does nothing after
|
||||
// the first call.
|
||||
func (r *Row) Init(name text.Rich) {
|
||||
if !r.IsHollow() {
|
||||
return
|
||||
}
|
||||
|
||||
r.Avatar = roundimage.NewAvatar(IconSize)
|
||||
r.Avatar.SetText(name.Content)
|
||||
r.Avatar.Show()
|
||||
|
||||
btn := rich.NewCustomToggleButtonImage(r.Avatar, name)
|
||||
btn.Show()
|
||||
|
||||
r.Button = button.WrapToggleButtonImage(btn)
|
||||
r.Button.Box.SetHAlign(gtk.ALIGN_START)
|
||||
r.Button.SetRelief(gtk.RELIEF_NONE)
|
||||
r.Button.Show()
|
||||
|
||||
r.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||
r.Box.PackStart(r.Button, false, false, 0)
|
||||
|
||||
// Ensure errors are displayed.
|
||||
r.childrenSetErr(r.childrenErr)
|
||||
}
|
||||
|
||||
// SetHollowServerList sets the row to a hollow server list (children) and
|
||||
// recursively create
|
||||
func (r *Row) SetHollowServerList(list cchat.ServerList, ctrl Controller) {
|
||||
func (r *ServerRow) SetHollowServerList(list cchat.ServerList, ctrl Controller) {
|
||||
r.serverList = list
|
||||
|
||||
r.children = NewHollowChildren(r, ctrl)
|
||||
|
@ -230,7 +214,7 @@ func (r *Row) SetHollowServerList(list cchat.ServerList, ctrl Controller) {
|
|||
}
|
||||
|
||||
// Reset clears off all children servers. It's a no-op if there are none.
|
||||
func (r *Row) Reset() {
|
||||
func (r *ServerRow) Reset() {
|
||||
if r.children != nil {
|
||||
// Remove everything from the children container.
|
||||
r.children.Reset()
|
||||
|
@ -244,7 +228,7 @@ func (r *Row) Reset() {
|
|||
r.children = nil
|
||||
}
|
||||
|
||||
func (r *Row) childrenSetErr(err error) {
|
||||
func (r *ServerRow) childrenSetErr(err error) {
|
||||
// Update the state and only use this state field.
|
||||
r.childrenErr = err
|
||||
|
||||
|
@ -273,21 +257,29 @@ func (r *ServerRow) HasIcon() bool {
|
|||
return !r.IsHollow() && r.Button.Image.GetRevealChild()
|
||||
}
|
||||
|
||||
func (r *Row) Breadcrumb() traverse.Breadcrumb {
|
||||
if r.IsHollow() {
|
||||
return nil
|
||||
}
|
||||
return traverse.TryBreadcrumb(r.parentcrumb, r.Button.GetText())
|
||||
func (r *ServerRow) ParentBreadcrumb() traverse.Breadcrumber {
|
||||
return r.parentcrumb
|
||||
}
|
||||
|
||||
func (r *Row) SetLabelUnsafe(name text.Rich) {
|
||||
func (r *ServerRow) Breadcrumb() string {
|
||||
if r.IsHollow() {
|
||||
return ""
|
||||
}
|
||||
return r.Button.GetText()
|
||||
}
|
||||
|
||||
func (r *ServerRow) ID() cchat.ID {
|
||||
return r.Server.ID()
|
||||
}
|
||||
|
||||
func (r *ServerRow) SetLabelUnsafe(name text.Rich) {
|
||||
AssertUnhollow(r)
|
||||
|
||||
r.Button.SetLabelUnsafe(name)
|
||||
r.Avatar.SetText(name.Content)
|
||||
}
|
||||
|
||||
func (r *Row) SetIconer(v interface{}) {
|
||||
func (r *ServerRow) SetIconer(v interface{}) {
|
||||
AssertUnhollow(r)
|
||||
|
||||
if iconer, ok := v.(cchat.Icon); ok {
|
||||
|
@ -297,7 +289,7 @@ func (r *Row) SetIconer(v interface{}) {
|
|||
}
|
||||
|
||||
// SetLoading is called by the parent struct.
|
||||
func (r *Row) SetLoading() {
|
||||
func (r *ServerRow) SetLoading() {
|
||||
AssertUnhollow(r)
|
||||
|
||||
r.SetSensitive(false)
|
||||
|
@ -307,7 +299,7 @@ func (r *Row) SetLoading() {
|
|||
// SetFailed is shared between the parent struct and the children list. This is
|
||||
// because both of those errors share the same appearance, just different
|
||||
// callbacks.
|
||||
func (r *Row) SetFailed(err error, retry func()) {
|
||||
func (r *ServerRow) SetFailed(err error, retry func()) {
|
||||
AssertUnhollow(r)
|
||||
|
||||
r.SetSensitive(true)
|
||||
|
@ -318,7 +310,7 @@ func (r *Row) SetFailed(err error, retry func()) {
|
|||
|
||||
// SetDone is shared between the parent struct and the children list. This is
|
||||
// because both will use the same SetFailed.
|
||||
func (r *Row) SetDone() {
|
||||
func (r *ServerRow) SetDone() {
|
||||
AssertUnhollow(r)
|
||||
|
||||
r.Button.SetNormal()
|
||||
|
@ -326,7 +318,7 @@ func (r *Row) SetDone() {
|
|||
r.SetTooltipText("")
|
||||
}
|
||||
|
||||
func (r *Row) SetNormalExtraMenu(items []menu.Item) {
|
||||
func (r *ServerRow) SetNormalExtraMenu(items []menu.Item) {
|
||||
AssertUnhollow(r)
|
||||
|
||||
r.Button.SetNormalExtraMenu(items)
|
||||
|
@ -335,13 +327,13 @@ func (r *Row) SetNormalExtraMenu(items []menu.Item) {
|
|||
}
|
||||
|
||||
// SetSelected is used for highlighting the current message server.
|
||||
func (r *Row) SetSelected(selected bool) {
|
||||
func (r *ServerRow) SetSelected(selected bool) {
|
||||
AssertUnhollow(r)
|
||||
|
||||
r.Button.SetSelected(selected)
|
||||
}
|
||||
|
||||
func (r *Row) GetActive() bool {
|
||||
func (r *ServerRow) GetActive() bool {
|
||||
if !r.IsHollow() {
|
||||
return r.Button.GetActive()
|
||||
}
|
||||
|
@ -351,7 +343,7 @@ func (r *Row) GetActive() bool {
|
|||
|
||||
// SetRevealChild reveals the list of servers. It does nothing if there are no
|
||||
// servers, meaning if Row does not represent a ServerList.
|
||||
func (r *Row) SetRevealChild(reveal bool) {
|
||||
func (r *ServerRow) SetRevealChild(reveal bool) {
|
||||
AssertUnhollow(r)
|
||||
|
||||
// Do the above noop check.
|
||||
|
@ -371,11 +363,14 @@ func (r *Row) SetRevealChild(reveal bool) {
|
|||
// to call Servers on this. Now, we already know that there are hollow
|
||||
// servers in the children container.
|
||||
r.children.LoadAll()
|
||||
|
||||
// Save the path.
|
||||
savepath.Update(r, reveal)
|
||||
}
|
||||
|
||||
// GetRevealChild returns whether or not the server list is expanded, or always
|
||||
// false if there is no server list.
|
||||
func (r *Row) GetRevealChild() bool {
|
||||
func (r *ServerRow) GetRevealChild() bool {
|
||||
AssertUnhollow(r)
|
||||
|
||||
if r.childrev != nil {
|
||||
|
|
|
@ -7,31 +7,76 @@
|
|||
package traverse
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"github.com/diamondburned/cchat"
|
||||
)
|
||||
|
||||
type Breadcrumb []string
|
||||
|
||||
func (b Breadcrumb) String() string {
|
||||
return strings.Join([]string(b), "/")
|
||||
}
|
||||
|
||||
// Breadcrumber is the base interface that other interfaces extend on. A child
|
||||
// must at minimum implement this interface to use any other.
|
||||
type Breadcrumber interface {
|
||||
// Breadcrumb returns the parent's path before the children's breadcrumb.
|
||||
// This method recursively joins the parent's crumb with the children's,
|
||||
// then eventually make its way up to the root node.
|
||||
Breadcrumb() Breadcrumb
|
||||
ParentBreadcrumb() Breadcrumber
|
||||
}
|
||||
|
||||
type BreadcrumbNamer interface {
|
||||
// Breadcrumb returns the breadcrumb name.
|
||||
Breadcrumb() string
|
||||
}
|
||||
|
||||
// Traverse traverses the given breadcrumber recursively. If traverser returns
|
||||
// true, then the function halts. Traversal is done from parent down to
|
||||
// children.
|
||||
func Traverse(bc Breadcrumber, traverser func(b Breadcrumber) bool) {
|
||||
if bc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var stack []Breadcrumber
|
||||
for current := bc; current != nil; current = current.ParentBreadcrumb() {
|
||||
stack = append(stack, current)
|
||||
}
|
||||
|
||||
for _, bc := range stack {
|
||||
if traverser(bc) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TryBreadcrumb accepts a nilable breadcrumber and handles it appropriately.
|
||||
func TryBreadcrumb(i Breadcrumber, appended ...string) []string {
|
||||
if i == nil {
|
||||
return appended
|
||||
func TryBreadcrumb(i Breadcrumber) (breadcrumbs []string) {
|
||||
Traverse(i, func(b Breadcrumber) bool {
|
||||
if namer, ok := b.(BreadcrumbNamer); ok {
|
||||
breadcrumbs = append(breadcrumbs, namer.Breadcrumb())
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
for l, r := 0, len(breadcrumbs)-1; l < r; l, r = l+1, r-1 {
|
||||
breadcrumbs[l], breadcrumbs[r] = breadcrumbs[r], breadcrumbs[l]
|
||||
}
|
||||
|
||||
return append(i.Breadcrumb(), appended...)
|
||||
return
|
||||
}
|
||||
|
||||
func TryID(i Breadcrumber, appended ...cchat.ID) (ids []cchat.ID) {
|
||||
Traverse(i, func(b Breadcrumber) bool {
|
||||
switch b := b.(type) {
|
||||
case cchat.Identifier:
|
||||
ids = append(ids, b.ID())
|
||||
case BreadcrumbNamer:
|
||||
ids = append(ids, b.Breadcrumb())
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
for l, r := 0, len(ids)-1; l < r; l, r = l+1, r-1 {
|
||||
ids[l], ids[r] = ids[r], ids[l]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Unreadabler extends Breadcrumber to add unread states to the parent node.
|
||||
|
|
|
@ -217,8 +217,12 @@ func (r *Row) Reset() {
|
|||
r.cmder = nil
|
||||
}
|
||||
|
||||
func (r *Row) Breadcrumb() traverse.Breadcrumb {
|
||||
return traverse.TryBreadcrumb(r.parentcrumb, r.Session.Name().Content)
|
||||
func (r *Row) ParentBreadcrumb() traverse.Breadcrumber {
|
||||
return r.parentcrumb
|
||||
}
|
||||
|
||||
func (r *Row) Breadcrumb() string {
|
||||
return r.Session.Name().Content
|
||||
}
|
||||
|
||||
// Activate executes whatever needs to be done. If the row has failed, then this
|
||||
|
|
5
main.go
5
main.go
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/diamondburned/cchat-gtk/internal/ui"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/config"
|
||||
"github.com/diamondburned/cchat/services"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
_ "github.com/diamondburned/cchat-discord"
|
||||
_ "github.com/diamondburned/cchat-mock"
|
||||
|
@ -33,9 +32,7 @@ func main() {
|
|||
}
|
||||
|
||||
// Restore the configs.
|
||||
if err := config.Restore(); err != nil {
|
||||
log.Error(errors.Wrap(err, "Failed to restore config"))
|
||||
}
|
||||
config.Restore()
|
||||
|
||||
return app
|
||||
})
|
||||
|
|
16
shell.nix
16
shell.nix
|
@ -1,16 +1,6 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
# let hunspell = pkgs.hunspellWithDicts(with pkgs.hunspellDicts; [
|
||||
# en-us
|
||||
# en-us-large
|
||||
# ]);
|
||||
|
||||
let hunspellWrapper = pkgs.hunspellWithDicts(with pkgs.hunspellDicts; [
|
||||
en-us
|
||||
en-us-large
|
||||
]);
|
||||
|
||||
libhandy = pkgs.libhandy.overrideAttrs(old: {
|
||||
let libhandy = pkgs.libhandy.overrideAttrs(old: {
|
||||
name = "libhandy-0.90.0";
|
||||
src = builtins.fetchGit {
|
||||
url = "https://gitlab.gnome.org/GNOME/libhandy.git";
|
||||
|
@ -29,8 +19,8 @@ in pkgs.stdenv.mkDerivation rec {
|
|||
version = "0.0.2";
|
||||
|
||||
buildInputs =
|
||||
[ libhandy hunspellWrapper ]
|
||||
++ (with pkgs; [ enchant2 gnome3.gspell gnome3.glib gnome3.gtk ]);
|
||||
[ libhandy ]
|
||||
++ (with pkgs; [ gnome3.gspell gnome3.glib gnome3.gtk ]);
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkgconfig go
|
||||
|
|
Loading…
Reference in a new issue