Added type broadcasting; WIP unread indicator; minor fixes

This commit is contained in:
diamondburned 2020-07-17 00:26:55 -07:00
parent 4bb3726101
commit 4c173773bf
9 changed files with 113 additions and 31 deletions

4
go.mod
View File

@ -7,8 +7,8 @@ replace github.com/gotk3/gotk3 => github.com/diamondburned/gotk3 v0.0.0-20200630
require (
github.com/Xuanwo/go-locale v0.2.0
github.com/alecthomas/chroma v0.7.3
github.com/diamondburned/cchat v0.0.43
github.com/diamondburned/cchat-discord v0.0.0-20200717002543-508f355b9657
github.com/diamondburned/cchat v0.0.45
github.com/diamondburned/cchat-discord v0.0.0-20200717063909-2f4cb5f246c4
github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b
github.com/diamondburned/imgutil v0.0.0-20200710174014-8a3be144a972
github.com/disintegration/imaging v1.6.2

14
go.sum
View File

@ -44,20 +44,22 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/diamondburned/aqs v0.0.0-20200704043812-99b676ee44eb h1:Ja/niwykeFoSkYxdRRzM8QUAuCswfLmaiBTd2UIU+54=
github.com/diamondburned/aqs v0.0.0-20200704043812-99b676ee44eb/go.mod h1:q1MbMBfZrv7xqV8n7LgMwhHs3oBbNwWJes8exs2AmDs=
github.com/diamondburned/arikawa v0.10.5 h1:o5lBopooA+8cXlKZdct5qF0xztuZZ35phvQrwGS5vYM=
github.com/diamondburned/arikawa v0.10.5/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
github.com/diamondburned/arikawa v0.12.4 h1:lhWJqcGkIIMiOYWdsoEuGlri2UbMkzMeh+VfuJPkXt4=
github.com/diamondburned/arikawa v0.12.4/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
github.com/diamondburned/cchat v0.0.43 h1:HetAujSaUSdnQgAUZgprNLARjf/MSWXpCfWdvX2wOCU=
github.com/diamondburned/cchat v0.0.43/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/cchat-discord v0.0.0-20200717002543-508f355b9657 h1:/ZwVENnNKBioK34qA4sr/2B0pcJbDpPyw6DpiI3Cjr0=
github.com/diamondburned/cchat-discord v0.0.0-20200717002543-508f355b9657/go.mod h1:cX6rGfvIv2rfNPrhfcRx88bfNxyL7eFmiYZLCWGfchw=
github.com/diamondburned/cchat v0.0.45 h1:HMVSKx1h6lh2OenWaBTvMSK531hWaXAW7I0tKZepYug=
github.com/diamondburned/cchat v0.0.45/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/cchat-discord v0.0.0-20200717063909-2f4cb5f246c4 h1:otasqokx6kIGo9KnJt1F22MdCyUIvZmxZkLv+kcdKiI=
github.com/diamondburned/cchat-discord v0.0.0-20200717063909-2f4cb5f246c4/go.mod h1:Z0uWBUaheEtozKj4NMgsSK4X5a3Du5tYakDb5plEluY=
github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b h1:sq0MXjJc3yAOZvuolRxOpKQNvpMLyTmsECxQqdYgF5E=
github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b/go.mod h1:+bAf0m2o5qH54DmYJ/lR1HeITV53ol0JaoKyFFx3m3E=
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d h1:Ha/I6PMKi+B4hpWclwlXj0tUMehR7Q0TNxPczzBwzPI=
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/diamondburned/imgutil v0.0.0-20200710174014-8a3be144a972 h1:OWxllHbUptXzDias6YI4MM0R3o50q8MfhkkwVIlfiNo=
github.com/diamondburned/imgutil v0.0.0-20200710174014-8a3be144a972/go.mod h1:kBQKaukR/LyCfhED99/T4/XxUMDNEEzf1Fx6vreD3RQ=
github.com/diamondburned/ningen v0.1.1-0.20200715040340-2395a0dbd0fa h1:ntHcz6GNzxn3TovtYZVwOBvL3xn7Iq1luaV/KEIEXrk=
github.com/diamondburned/ningen v0.1.1-0.20200715040340-2395a0dbd0fa/go.mod h1:SKPY3387RHCbMrnefex9D+zlrA2yB+LCtaaQAgatAuc=
github.com/diamondburned/ningen v0.1.1-0.20200717013108-297a3bdf84dc h1:YZ84Kdlv91tdcyLfGfQ+LG9kWZN8dTKIic0KlEtGV0U=
github.com/diamondburned/ningen v0.1.1-0.20200717013108-297a3bdf84dc/go.mod h1:Sunqp1b9Tc0+DtWKslhf83Zepgj/TELB6h8J9HZCPqQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=

View File

@ -1,6 +1,8 @@
package input
import (
"time"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-gtk/internal/gts"
"github.com/diamondburned/cchat-gtk/internal/log"
@ -96,12 +98,14 @@ type Field struct {
UserID string
Sender cchat.ServerMessageSender
editor cchat.ServerMessageEditor
typer cchat.ServerMessageTypingIndicator
ctrl Controller
// states
editingID string // never empty
sendings []PresendMessage
lastTyped time.Time
typerDura time.Duration
}
var inputFieldCSS = primitives.PrepareCSS(`
@ -178,6 +182,10 @@ func (f *Field) Reset() {
f.UserID = ""
f.Sender = nil
f.editor = nil
f.typer = nil
f.lastTyped = time.Time{}
f.typerDura = 0
f.Username.Reset()
// reset the input
@ -197,11 +205,14 @@ func (f *Field) SetSender(session cchat.Session, sender cchat.ServerMessageSende
f.text.SetSensitive(true)
// Allow editor to be nil.
ed, ok := sender.(cchat.ServerMessageEditor)
if !ok {
log.Printlnf("Editor is not implemented for %T", sender)
f.editor, _ = sender.(cchat.ServerMessageEditor)
// Allow typer to be nil.
f.typer, _ = sender.(cchat.ServerMessageTypingIndicator)
// Populate the duration state if typer is not nil.
if f.typer != nil {
f.typerDura = f.typer.TypingTimeout()
}
f.editor = ed
}
}

View File

@ -1,6 +1,8 @@
package input
import (
"time"
"github.com/diamondburned/cchat-gtk/internal/gts"
"github.com/diamondburned/cchat-gtk/internal/log"
"github.com/gotk3/gotk3/gdk"
@ -95,6 +97,23 @@ func (f *Field) keyDown(tv *gtk.TextView, ev *gdk.Event) bool {
}
}
// If the server supports typing indication, then announce that we are
// typing with a proper rate limit.
if f.typer != nil {
// Get the current time; if the next timestamp is before now, then that
// means it's time for us to update it and send a typing indication.
if now := time.Now(); f.lastTyped.Add(f.typerDura).Before(now) {
// Update.
f.lastTyped = now
// Send asynchronously.
go func() {
if err := f.typer.Typing(); err != nil {
log.Error(errors.Wrap(err, "Failed to announce typing"))
}
}()
}
}
// Passthrough.
return false
}

View File

@ -64,6 +64,19 @@ func RemoveClass(styleCtx StyleContexter, classes ...string) {
}
}
type ClassEnum struct{ class string }
func (c *ClassEnum) SetClass(ctx StyleContexter, class string) {
var style, _ = ctx.GetStyleContext()
if c.class != "" {
style.RemoveClass(c.class)
}
if c.class = class; class != "" {
style.AddClass(class)
}
}
type StyleContextFocuser interface {
StyleContexter
GrabFocus()

View File

@ -39,7 +39,7 @@ func (a *AppendMap) Anchor(start, end int, href string) {
a.Close(end, "</a>")
}
// AnchorNU makes a new <a> tag without underlines.
// AnchorNU makes a new <a> tag without underlines and colors.
func (a *AppendMap) AnchorNU(start, end int, href string) {
a.Anchor(start, end, href)
a.Span(start, end, `underline="none"`)

View File

@ -3,8 +3,9 @@ package button
import (
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-gtk/internal/gts"
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
"github.com/diamondburned/cchat/text"
)
@ -15,6 +16,7 @@ type ToggleButtonImage struct {
menu *menu.LazyMenu
clicked func(bool)
readcss primitives.ClassEnum
err error
icon string // whether or not the button has an icon
@ -23,6 +25,12 @@ type ToggleButtonImage struct {
var _ cchat.IconContainer = (*ToggleButtonImage)(nil)
var serverButtonCSS = primitives.PrepareCSS(`
.read { color: alpha(@theme_fg_color, 0.5) }
.unread { color: @theme_fg_color }
.mentioned { color: red }
`)
func NewToggleButtonImage(content text.Rich) *ToggleButtonImage {
b := rich.NewToggleButtonImage(content)
b.Show()
@ -33,10 +41,8 @@ func NewToggleButtonImage(content text.Rich) *ToggleButtonImage {
clicked: func(bool) {},
menu: menu.NewLazyMenu(b.ToggleButton),
}
tb.Connect("clicked", func() {
tb.clicked(tb.GetActive())
})
tb.Connect("clicked", func() { tb.clicked(tb.GetActive()) })
primitives.AttachCSS(tb, serverButtonCSS)
return tb
}
@ -92,6 +98,17 @@ func (b *ToggleButtonImage) SetFailed(err error, retry func()) {
}
}
func (b *ToggleButtonImage) SetUnreadUnsafe(unread, mentioned bool) {
switch {
case unread:
b.readcss.SetClass(b, "unread")
case mentioned:
b.readcss.SetClass(b, "mentioned")
default:
b.readcss.SetClass(b, "read")
}
}
func (b *ToggleButtonImage) SetPlaceholderIcon(iconName string, iconSzPx int) {
b.icon = iconName
b.Image.SetPlaceholderIcon(iconName, iconSzPx)

View File

@ -7,7 +7,7 @@ import (
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
"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/button"
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/button"
"github.com/diamondburned/cchat/text"
"github.com/gotk3/gotk3/gtk"
"github.com/pkg/errors"
@ -45,6 +45,21 @@ func NewServerRow(p breadcrumb.Breadcrumber, server cchat.Server, ctrl Controlle
case cchat.ServerMessage:
row.Button.SetClickedIfTrue(func() { ctrl.RowSelected(serverRow, server) })
primitives.AddClass(row, "server-message")
// Check if the server is capable of indicating unread state.
if unreader, ok := server.(cchat.ServerMessageUnreadIndicator); ok {
// Set as read by default.
row.Button.SetUnreadUnsafe(false, false)
gts.Async(func() (func(), error) {
c, err := unreader.UnreadIndicate(row)
if err != nil {
return nil, errors.Wrap(err, "Failed to use unread indicator")
}
return func() { row.Connect("destroy", c) }, nil
})
}
}
return serverRow
@ -208,6 +223,15 @@ func (r *Row) SetRevealChild(reveal bool) {
}
}
// GetRevealChild returns whether or not the server list is expanded, or always
// false if there is no server list.
func (r *Row) GetRevealChild() bool {
if r.childrev != nil {
return r.childrev.GetRevealChild()
}
return false
}
// Load loads the row without uncollapsing it.
func (r *Row) Load() {
// Safeguard.
@ -239,11 +263,11 @@ func (r *Row) Load() {
}()
}
// GetRevealChild returns whether or not the server list is expanded, or always
// false if there is no server list.
func (r *Row) GetRevealChild() bool {
if r.childrev != nil {
return r.childrev.GetRevealChild()
}
return false
// SetUnread is thread-safe.
func (r *Row) SetUnread(unread, mentioned bool) {
gts.ExecAsync(func() { r.SetUnreadUnsafe(unread, mentioned) })
}
func (r *Row) SetUnreadUnsafe(unread, mentioned bool) {
r.Button.SetUnreadUnsafe(unread, mentioned)
}

View File

@ -62,12 +62,8 @@ type Row struct {
ActionsMenu *actions.Menu // session.*
// TODO: enum class? having the button be red on fail would be good
// put commander in either a hover menu or a right click menu. maybe in the
// headerbar as well.
// TODO headerbar how? custom interface to get menu items and callbacks in
// controller?
cmder *commander.Buffer
}