cchat/cchat_frontend.go

182 lines
7.4 KiB
Go

package cchat
import (
"io"
"github.com/diamondburned/cchat/text"
)
// ServersContainer is any type of view that displays the list of servers. It
// should implement a SetServers([]Server) that the backend could use to call
// anytime the server list changes (at all).
//
// Typically, most frontends should implement this interface onto a tree node,
// as servers can be infinitely nested. Frontends should also reset the entire
// node and its children when SetServers is called again.
type ServersContainer interface {
// SetServer is called by the backend service to request a reset of the
// server list. The frontend can choose to call Servers() on each of the
// given servers, or it can call that later. The backend should handle both
// cases.
SetServers([]Server)
}
// MessagesContainer is a view implementation that displays a list of messages
// live. This implements the 3 most common message events: CreateMessage,
// UpdateMessage and DeleteMessage. The frontend must handle all 3.
//
// Since this container interface extends a single Server, the frontend is
// allowed to have multiple views. This is usually done with tabs or splits, but
// the backend should update them all nonetheless.
type MessagesContainer interface {
CreateMessage(MessageCreate)
UpdateMessage(MessageUpdate)
DeleteMessage(MessageDelete)
}
// LabelContainer is a generic interface for any container that can hold texts.
// It's typically used for rich text labelling for usernames and server names.
//
// Methods that takes in a LabelContainer typically holds it in the state and
// may call SetLabel any time it wants. Thus, the frontend should synchronize
// calls with the main thread if needed.
type LabelContainer interface {
SetLabel(text.Rich)
}
// IconContainer is a generic interface for any container that can hold an
// image. It's typically used for icons that can update itself. Frontends should
// round these icons. For images that shouldn't be rounded, use ImageContainer.
//
// Methods may call SetIcon at any time in its main thread, so the frontend must
// do any I/O (including downloading the image) in another goroutine to avoid
// blocking the backend.
type IconContainer interface {
SetIcon(url string)
}
// ImageContainer does nothing; it's reserved for future API usages. Typically,
// images don't have round corners while icons do.
type ImageContainer interface {
SetImage(url string)
}
// UnreadIndicator is an interface that a single server container (such as a
// button or a tree node) can implement if it's capable of indicating the read
// and mentioned status for that channel.
//
// Server containers that implement this has to implement both SetUnread and
// SetMentioned, and they should also represent those statuses differently. For
// example, a mentioned channel could have a red outline, while an unread
// channel could appear brighter.
//
// Server containers are expected to represent this information in their parent
// nodes as well. For example, if a server is unread, then its parent servers as
// well as the session node should indicate the same status. Highlighting the
// session and service nodes are, however, implementation details, meaning that
// this decision is up to the frontend to decide.
type UnreadIndicator interface {
// Unread sets the container's unread state to the given boolean. The
// frontend may choose how to represent this.
SetUnread(unread, mentioned bool)
}
// TypingIndicator is a generic interface for any container that can display
// users typing in the current chatbox. The typing indicator must adhere to the
// TypingTimeout returned from ServerMessageTypingIndicator. The backend should
// assume that to be the case and send events appropriately.
//
// For more documentation, refer to ServerMessageTypingIndicator.
type TypingIndicator interface {
// AddTyper appends the typer into the frontend's list of typers, or it
// pushes this typer on top of others.
AddTyper(Typer)
// RemoveTyper explicitly removes the typer with the given user ID from the
// list of typers. This function is usually not needed, as the client will
// take care of removing them after TypingTimeout has been reached or other
// conditions listed in ServerMessageTypingIndicator are met.
RemoveTyper(id string)
}
// MemberListContainer is a generic interface for any container that can display
// a member list. This is similar to Discord's right-side member list or IRC's
// users list. Below is a visual representation of a typical member list
// container:
//
// +-MemberList-----------\
// | +-Section------------|
// | | |
// | | Header - Total |
// | | |
// | | +-Member-----------|
// | | | Name |
// | | | Secondary |
// | | \__________________|
// | | |
// | | +-Member-----------|
// | | | Name |
// | | | Secondary |
// | | \__________________|
// \_\____________________/
//
type MemberListContainer interface {
// SetSections (re)sets the list of sections to be the given slice. Members
// from the old section list should be transferred over to the new section
// entry if the section name's content is the same. Old sections that don't
// appear in the new slice should be removed.
SetSections(sections []MemberListSection)
// SetMember adds or updates (or upsert) a member into a section. This
// operation must not change the section's member count. As such, changes
// should be done separately in SetSection. If the section does not exist,
// then the client should ignore this member. As such, backends must call
// SetSections first before SetMember on a new section.
SetMember(sectionContent string, member ListMember)
// RemoveMember removes a member from a section. If neither the member nor
// the section exists, then the client should ignore it.
RemoveMember(sectionContent string, id string)
}
// MemberListSection represents a member list section. The section name's
// content must be unique among other sections from the same list regardless of
// the rich segments.
type MemberListSection interface {
// Name returns the section name.
Name() text.Rich
// Total returns the total member count.
Total() int
}
// SendableMessage is the bare minimum interface of a sendable message, that is,
// a message that can be sent with SendMessage(). This allows the frontend to
// implement its own message data implementation.
//
// An example of extending this interface is MessageNonce, which is similar to
// IRCv3's labeled response extension or Discord's nonces. The frontend could
// implement this interface and check if incoming MessageCreate events implement
// the same interface.
//
// SendableMessage can implement the following interfaces:
//
// - MessageNonce (optional)
// - SendableMessageAttachments (optional): refer to ServerMessageAttachmentSender
type SendableMessage interface {
Content() string
}
// SendableMessageAttachments extends SendableMessage which adds attachments
// into the message. Backends that can use this interface should implement
// ServerMessageAttachmentSender.
type SendableMessageAttachments interface {
Attachments() []MessageAttachment
}
// MessageAttachment represents a single file attachment.
//
// If needed, the frontend will close the reader after the message is sent, that
// is when the SendMessage function returns. The backend must not use the reader
// after that.
type MessageAttachment struct {
io.Reader
Name string
}