2020-06-07 04:27:28 +00:00
|
|
|
package cozy
|
|
|
|
|
|
|
|
import (
|
2020-06-13 07:29:32 +00:00
|
|
|
"time"
|
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
|
2020-06-13 07:29:32 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/humanize"
|
2020-06-07 04:27:28 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/message"
|
2020-06-13 07:29:32 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
2020-07-16 05:41:21 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
|
2020-07-17 00:21:14 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/roundimage"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/rich/labeluri"
|
2020-12-25 08:32:08 +00:00
|
|
|
"github.com/gotk3/gotk3/cairo"
|
2020-06-07 04:27:28 +00:00
|
|
|
"github.com/gotk3/gotk3/gtk"
|
|
|
|
)
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// TopFullMargin is the margin on top of every full message.
|
2020-06-26 04:11:31 +00:00
|
|
|
const TopFullMargin = 4
|
2020-06-13 07:29:32 +00:00
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
type FullMessage struct {
|
|
|
|
*message.GenericContainer
|
|
|
|
|
|
|
|
// Grid widgets.
|
2020-06-13 07:29:32 +00:00
|
|
|
Avatar *Avatar
|
2020-06-07 04:27:28 +00:00
|
|
|
MainBox *gtk.Box // wraps header and content
|
|
|
|
|
|
|
|
// Header wraps author and timestamp.
|
|
|
|
HeaderBox *gtk.Box
|
|
|
|
}
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
type AvatarPixbufCopier interface {
|
2020-12-25 08:32:08 +00:00
|
|
|
CopyAvatarPixbuf(img httputil.SurfaceContainer)
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
var (
|
2021-01-02 09:24:14 +00:00
|
|
|
_ AvatarPixbufCopier = (*FullMessage)(nil)
|
|
|
|
_ message.Container = (*FullMessage)(nil)
|
|
|
|
_ container.MessageRow = (*FullMessage)(nil)
|
2020-06-07 04:27:28 +00:00
|
|
|
)
|
|
|
|
|
2020-07-06 00:19:48 +00:00
|
|
|
var boldCSS = primitives.PrepareCSS(`
|
|
|
|
* { font-weight: 600; }
|
|
|
|
`)
|
|
|
|
|
2020-07-17 00:21:14 +00:00
|
|
|
var avatarCSS = primitives.PrepareClassCSS("cozy-avatar", `
|
|
|
|
/* Slightly dip down on click */
|
|
|
|
.cozy-avatar:active {
|
|
|
|
margin-top: 1px;
|
|
|
|
}
|
|
|
|
`)
|
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
func NewFullMessage(msg cchat.MessageCreate) *FullMessage {
|
|
|
|
msgc := WrapFullMessage(message.NewContainer(msg))
|
2020-06-13 07:29:32 +00:00
|
|
|
// Don't update the avatar. NewMessage in controller will try and reuse the
|
|
|
|
// pixbuf if possible.
|
2020-06-07 04:27:28 +00:00
|
|
|
msgc.UpdateAuthorName(msg.Author().Name())
|
|
|
|
msgc.UpdateTimestamp(msg.Time())
|
2020-06-13 07:29:32 +00:00
|
|
|
msgc.UpdateContent(msg.Content(), false)
|
2020-06-07 04:27:28 +00:00
|
|
|
return msgc
|
|
|
|
}
|
|
|
|
|
|
|
|
func WrapFullMessage(gc *message.GenericContainer) *FullMessage {
|
2020-06-13 07:29:32 +00:00
|
|
|
avatar := NewAvatar()
|
2021-01-02 09:24:14 +00:00
|
|
|
avatar.SetMarginTop(TopFullMargin / 2)
|
2020-06-07 04:27:28 +00:00
|
|
|
avatar.SetMarginStart(container.ColumnSpacing * 2)
|
2020-12-30 06:30:41 +00:00
|
|
|
avatar.Connect("clicked", func(w gtk.IWidget) {
|
2020-07-17 00:21:14 +00:00
|
|
|
if output := gc.Username.Output(); len(output.Mentions) > 0 {
|
2020-12-30 06:30:41 +00:00
|
|
|
labeluri.PopoverMentioner(w, output.Input, output.Mentions[0])
|
2020-07-17 00:21:14 +00:00
|
|
|
}
|
|
|
|
})
|
2021-01-02 09:24:14 +00:00
|
|
|
avatar.Show()
|
2020-06-13 07:29:32 +00:00
|
|
|
|
|
|
|
// Style the timestamp accordingly.
|
|
|
|
gc.Timestamp.SetXAlign(0.0) // left-align
|
|
|
|
gc.Timestamp.SetVAlign(gtk.ALIGN_END) // bottom-align
|
|
|
|
gc.Timestamp.SetMarginStart(0) // clear margins
|
|
|
|
|
2020-12-31 03:00:00 +00:00
|
|
|
gc.Username.SetMaxWidthChars(75)
|
|
|
|
|
2020-07-17 00:21:14 +00:00
|
|
|
// Attach the class and CSS for the left avatar.
|
|
|
|
avatarCSS(avatar)
|
2020-06-07 04:27:28 +00:00
|
|
|
|
2020-07-06 00:19:48 +00:00
|
|
|
// Attach the username style provider.
|
|
|
|
primitives.AttachCSS(gc.Username, boldCSS)
|
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
header, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
|
|
|
header.PackStart(gc.Username, false, false, 0)
|
|
|
|
header.PackStart(gc.Timestamp, false, false, 7) // padding
|
|
|
|
header.Show()
|
|
|
|
|
|
|
|
main, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
|
|
|
main.PackStart(header, false, false, 0)
|
2020-06-26 04:11:31 +00:00
|
|
|
main.PackStart(gc.Content, false, false, 0)
|
2020-06-13 07:29:32 +00:00
|
|
|
main.SetMarginTop(TopFullMargin)
|
2020-06-07 04:27:28 +00:00
|
|
|
main.SetMarginEnd(container.ColumnSpacing * 2)
|
2021-01-02 09:24:14 +00:00
|
|
|
main.SetMarginStart(container.ColumnSpacing)
|
2020-06-07 04:27:28 +00:00
|
|
|
main.Show()
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// Also attach a class for the main box shown on the right.
|
|
|
|
primitives.AddClass(main, "cozy-main")
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
gc.PackStart(avatar, false, false, 0)
|
|
|
|
gc.PackStart(main, true, true, 0)
|
|
|
|
gc.SetClass("cozy-full")
|
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
return &FullMessage{
|
|
|
|
GenericContainer: gc,
|
|
|
|
Avatar: avatar,
|
|
|
|
MainBox: main,
|
|
|
|
HeaderBox: header,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 22:40:06 +00:00
|
|
|
func (m *FullMessage) Collapsed() bool { return false }
|
2020-06-13 21:51:03 +00:00
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
func (m *FullMessage) Unwrap() *message.GenericContainer {
|
2020-06-13 07:29:32 +00:00
|
|
|
// Remove GenericContainer's widgets from the containers.
|
|
|
|
m.HeaderBox.Remove(m.Username)
|
|
|
|
m.HeaderBox.Remove(m.Timestamp)
|
2020-10-25 01:13:33 +00:00
|
|
|
m.MainBox.Remove(m.HeaderBox)
|
2020-06-13 07:29:32 +00:00
|
|
|
m.MainBox.Remove(m.Content)
|
|
|
|
|
2020-08-20 23:53:13 +00:00
|
|
|
// Hide the avatar.
|
|
|
|
m.Avatar.Hide()
|
|
|
|
|
|
|
|
// Remove the message from the grid.
|
2021-01-02 09:24:14 +00:00
|
|
|
m.Remove(m.Avatar)
|
|
|
|
m.Remove(m.MainBox)
|
2020-08-20 23:53:13 +00:00
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// Return after removing.
|
|
|
|
return m.GenericContainer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *FullMessage) UpdateTimestamp(t time.Time) {
|
|
|
|
m.GenericContainer.UpdateTimestamp(t)
|
2020-07-06 00:19:48 +00:00
|
|
|
m.Timestamp.SetText(humanize.TimeAgoLong(t))
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
2020-10-15 06:32:11 +00:00
|
|
|
func (m *FullMessage) UpdateAuthor(author cchat.Author) {
|
2020-06-07 04:27:28 +00:00
|
|
|
// Call the parent's method to update the labels.
|
|
|
|
m.GenericContainer.UpdateAuthor(author)
|
2020-10-15 06:32:11 +00:00
|
|
|
m.Avatar.SetURL(author.Avatar())
|
2020-06-07 04:27:28 +00:00
|
|
|
}
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// CopyAvatarPixbuf sets the pixbuf into the given container. This shares the
|
|
|
|
// same pixbuf, but gtk.Image should take its own reference from the pixbuf.
|
2020-12-25 08:32:08 +00:00
|
|
|
func (m *FullMessage) CopyAvatarPixbuf(dst httputil.SurfaceContainer) {
|
|
|
|
switch img := m.Avatar.Image.GetImage(); img.GetStorageType() {
|
2020-06-07 04:27:28 +00:00
|
|
|
case gtk.IMAGE_PIXBUF:
|
2020-07-17 00:21:14 +00:00
|
|
|
dst.SetFromPixbuf(img.GetPixbuf())
|
2020-06-07 04:27:28 +00:00
|
|
|
case gtk.IMAGE_ANIMATION:
|
2020-07-17 00:21:14 +00:00
|
|
|
dst.SetFromAnimation(img.GetAnimation())
|
2020-12-25 08:32:08 +00:00
|
|
|
case gtk.IMAGE_SURFACE:
|
|
|
|
v, _ := img.GetProperty("surface")
|
|
|
|
dst.SetFromSurface(v.(*cairo.Surface))
|
2020-06-07 04:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-17 22:58:38 +00:00
|
|
|
func (m *FullMessage) AttachMenu(items []menu.Item) {
|
2020-06-13 07:29:32 +00:00
|
|
|
// Bind to parent's container as well.
|
|
|
|
m.GenericContainer.AttachMenu(items)
|
|
|
|
|
|
|
|
// Bind to the box.
|
|
|
|
// TODO lol
|
|
|
|
}
|
|
|
|
|
2020-06-07 04:27:28 +00:00
|
|
|
type FullSendingMessage struct {
|
|
|
|
message.PresendContainer
|
|
|
|
FullMessage
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2021-01-02 09:24:14 +00:00
|
|
|
_ message.Container = (*FullSendingMessage)(nil)
|
|
|
|
_ container.MessageRow = (*FullSendingMessage)(nil)
|
2020-06-07 04:27:28 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func NewFullSendingMessage(msg input.PresendMessage) *FullSendingMessage {
|
|
|
|
var msgc = message.NewPresendContainer(msg)
|
|
|
|
|
|
|
|
return &FullSendingMessage{
|
|
|
|
PresendContainer: msgc,
|
|
|
|
FullMessage: *WrapFullMessage(msgc.GenericContainer),
|
|
|
|
}
|
|
|
|
}
|
2020-06-07 07:06:13 +00:00
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
type Avatar struct {
|
2020-07-17 00:21:14 +00:00
|
|
|
roundimage.Button
|
2020-12-25 08:32:08 +00:00
|
|
|
Image *roundimage.StaticImage
|
2020-12-20 09:48:52 +00:00
|
|
|
url string
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewAvatar() *Avatar {
|
2020-08-20 23:53:13 +00:00
|
|
|
img, _ := roundimage.NewStaticImage(nil, 0)
|
2020-12-20 09:48:52 +00:00
|
|
|
img.SetSizeRequest(AvatarSize, AvatarSize)
|
2020-08-17 00:13:47 +00:00
|
|
|
img.Show()
|
|
|
|
|
2020-08-20 23:53:13 +00:00
|
|
|
avatar, _ := roundimage.NewCustomButton(img)
|
|
|
|
avatar.SetVAlign(gtk.ALIGN_START)
|
2020-08-17 00:13:47 +00:00
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// Default icon.
|
2020-08-17 00:13:47 +00:00
|
|
|
primitives.SetImageIcon(img, "user-available-symbolic", AvatarSize)
|
2020-06-13 07:29:32 +00:00
|
|
|
|
2020-12-20 09:48:52 +00:00
|
|
|
return &Avatar{*avatar, img, ""}
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetURL updates the Avatar to be that URL. It does nothing if URL is empty or
|
|
|
|
// matches the existing one.
|
|
|
|
func (a *Avatar) SetURL(url string) {
|
|
|
|
// Check if the URL is the same. This will save us quite a few requests, as
|
|
|
|
// some methods rely on the side-effects of other methods, and they may call
|
|
|
|
// UpdateAuthor multiple times.
|
|
|
|
if a.url == url || url == "" {
|
2020-06-07 07:06:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
a.url = url
|
2020-12-25 08:32:08 +00:00
|
|
|
a.Image.SetImageURL(url)
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ManuallySetURL sets the URL without downloading the image. It assumes the
|
|
|
|
// pixbuf is borrowed elsewhere.
|
|
|
|
func (a *Avatar) ManuallySetURL(url string) {
|
|
|
|
a.url = url
|
2020-06-07 07:06:13 +00:00
|
|
|
}
|