Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
diamondburned | b5bb0c9bb9 | |
diamondburned | 8bfabf58ec | |
diamondburned | 410ac73469 | |
diamondburned | 4e11444f6c | |
diamondburned | 86956a65ec | |
diamondburned | f2de1cb84d | |
diamondburned | 0cb14b9819 | |
diamondburned | f24feb2002 | |
diamondburned | f8c644fa7e | |
diamondburned | c7d4473c23 | |
diamondburned | 174496bdf9 | |
diamondburned | da5c38eb2f |
115
cchat.go
115
cchat.go
|
@ -176,7 +176,7 @@ type Actioner interface {
|
||||||
// Do executes a message action on the given messageID, which would be taken
|
// Do executes a message action on the given messageID, which would be taken
|
||||||
// from MessageHeader.ID(). This method is allowed to do IO; the frontend should
|
// from MessageHeader.ID(). This method is allowed to do IO; the frontend should
|
||||||
// take care of running it asynchronously.
|
// take care of running it asynchronously.
|
||||||
Do(action string, id ID) error // Blocking
|
Do(ctx context.Context, action string, id ID) error // Blocking
|
||||||
// MessageActions returns a list of possible actions to a message in pretty
|
// MessageActions returns a list of possible actions to a message in pretty
|
||||||
// strings that the frontend will use to directly display. This method must not
|
// strings that the frontend will use to directly display. This method must not
|
||||||
// do IO.
|
// do IO.
|
||||||
|
@ -217,7 +217,7 @@ type AuthenticateError interface {
|
||||||
type Authenticator interface {
|
type Authenticator interface {
|
||||||
// Authenticate will be called with a list of values with indices correspond to
|
// Authenticate will be called with a list of values with indices correspond to
|
||||||
// the returned slice of AuthenticateEntry.
|
// the returned slice of AuthenticateEntry.
|
||||||
Authenticate([]string) (Session, AuthenticateError) // Blocking
|
Authenticate(context.Context, []string) (Session, AuthenticateError) // Blocking
|
||||||
// AuthenticateForm should return a list of authentication entries for the
|
// AuthenticateForm should return a list of authentication entries for the
|
||||||
// frontend to render.
|
// frontend to render.
|
||||||
AuthenticateForm() []AuthenticateEntry
|
AuthenticateForm() []AuthenticateEntry
|
||||||
|
@ -295,7 +295,7 @@ type Commander interface {
|
||||||
// A helper function for this kind of behavior is available in package split,
|
// A helper function for this kind of behavior is available in package split,
|
||||||
// under the ArgsIndexed function. This implementation also provides the rough
|
// under the ArgsIndexed function. This implementation also provides the rough
|
||||||
// specifications.
|
// specifications.
|
||||||
Run(words []string) ([]byte, error) // Blocking
|
Run(ctx context.Context, words []string) ([]byte, error) // Blocking
|
||||||
|
|
||||||
// Asserters.
|
// Asserters.
|
||||||
|
|
||||||
|
@ -318,19 +318,17 @@ type Completer interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configurator is an interface which the backend can implement for a primitive
|
// Configurator is an interface which the backend can implement for a primitive
|
||||||
// configuration API. Since these methods do return an error, they are allowed
|
// configuration API.
|
||||||
// to do IO. The frontend should handle this appropriately, including running
|
|
||||||
// them asynchronously.
|
|
||||||
type Configurator interface {
|
type Configurator interface {
|
||||||
SetConfiguration(map[string]string) error // Blocking
|
SetConfiguration(map[string]string) error
|
||||||
Configuration() (map[string]string, error) // Blocking
|
Configuration() map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editor adds message editing to the messenger. Only EditMessage can do IO.
|
// Editor adds message editing to the messenger. Only EditMessage can do IO.
|
||||||
type Editor interface {
|
type Editor interface {
|
||||||
// Edit edits the message with the given ID to the given content, which is the
|
// Edit edits the message with the given ID to the given content, which is the
|
||||||
// edited string from RawMessageContent. This method can do IO.
|
// edited string from RawMessageContent. This method can do IO.
|
||||||
Edit(id ID, content string) error // Blocking
|
Edit(ctx context.Context, id ID, content string) error // Blocking
|
||||||
// RawContent gets the original message text for editing. This method must not
|
// RawContent gets the original message text for editing. This method must not
|
||||||
// do IO.
|
// do IO.
|
||||||
RawContent(id ID) (string, error)
|
RawContent(id ID) (string, error)
|
||||||
|
@ -357,17 +355,17 @@ type Identifier interface {
|
||||||
// Labels given to the frontend may contain images or avatars, and the frontend
|
// Labels given to the frontend may contain images or avatars, and the frontend
|
||||||
// has the choice to display them or not.
|
// has the choice to display them or not.
|
||||||
type LabelContainer interface {
|
type LabelContainer interface {
|
||||||
SetLabel(text.Rich)
|
SetLabel(context.Context, text.Rich)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListMember represents a single member in the member list. This is a base
|
// ListMember represents a single member in the member list. Note that this
|
||||||
// interface that may implement more interfaces, such as Iconer for the user's
|
// interface should be treated as a static container: updating a member will
|
||||||
// avatar.
|
// involve a completely new ListMember instance with the same ID.
|
||||||
//
|
//
|
||||||
// Note that the frontend may give everyone an avatar regardless, or it may not
|
// Note that the frontend may give everyone an avatar regardless, or it may not
|
||||||
// show any avatars at all.
|
// show any avatars at all.
|
||||||
type ListMember interface {
|
type ListMember interface {
|
||||||
User
|
Identifier
|
||||||
|
|
||||||
// Secondary returns the subtext of this member. This could be anything, such as
|
// Secondary returns the subtext of this member. This could be anything, such as
|
||||||
// a user's custom status or away reason.
|
// a user's custom status or away reason.
|
||||||
|
@ -376,6 +374,9 @@ type ListMember interface {
|
||||||
// offline members with the offline status if it doesn't want to show offline
|
// offline members with the offline status if it doesn't want to show offline
|
||||||
// menbers at all.
|
// menbers at all.
|
||||||
Status() Status
|
Status() Status
|
||||||
|
// Name returns the username or the nickname of the member, whichever the
|
||||||
|
// backend should prefer.
|
||||||
|
Name() text.Rich
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lister is for servers that contain children servers. This is similar to
|
// Lister is for servers that contain children servers. This is similar to
|
||||||
|
@ -392,16 +393,17 @@ type Lister interface {
|
||||||
// servers. This function can do IO, and the frontend should run this in a
|
// servers. This function can do IO, and the frontend should run this in a
|
||||||
// goroutine.
|
// goroutine.
|
||||||
Servers(ServersContainer) (stop func(), err error)
|
Servers(ServersContainer) (stop func(), err error)
|
||||||
// Columnate is optionally used by servers to give different nested servers its
|
// Columnate is optionally used by servers to tell the frontend whether or not
|
||||||
// own nesting values. Top-level servers must start at 1. The zero-value (0)
|
// its children should be put onto a new column instead of underneath it within
|
||||||
// indicates that the server that implements this interface is inherently the
|
// the same tree. If the method returns false, then the frontend can treat its
|
||||||
// children of its parent server.
|
// children as normal and show it as children within the same tree.
|
||||||
//
|
//
|
||||||
// For example, in Discord, guilds can be placed in guild folders, but guilds
|
// For example, in Discord, guilds can be placed in guild folders, but guilds
|
||||||
// and guild folders are put in the same column while guilds are actually
|
// and guild folders are put in the same column while guilds are actually
|
||||||
// children of the folders. To replicate this behavior, both guild and guild
|
// children of the folders. To replicate this behavior, guild folders should
|
||||||
// folders can return 1.
|
// return false, and guilds should return true. Both channels and categories can
|
||||||
Columnate() int
|
// return false.
|
||||||
|
Columnate() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemberDynamicSection represents a dynamically loaded member list section. The
|
// MemberDynamicSection represents a dynamically loaded member list section. The
|
||||||
|
@ -417,14 +419,14 @@ type MemberDynamicSection interface {
|
||||||
// The client can call this method exactly as many times as it has called
|
// The client can call this method exactly as many times as it has called
|
||||||
// LoadMore. However, false should be returned if the client should stop, and
|
// LoadMore. However, false should be returned if the client should stop, and
|
||||||
// future calls without LoadMore should still return false.
|
// future calls without LoadMore should still return false.
|
||||||
LoadLess() bool // Blocking
|
LoadLess(context.Context) bool // Blocking
|
||||||
// LoadMore is a method which the client can call to ask for more members. This
|
// LoadMore is a method which the client can call to ask for more members. This
|
||||||
// method can do IO.
|
// method can do IO.
|
||||||
//
|
//
|
||||||
// Clients may call this method on the last section in the section slice;
|
// Clients may call this method on the last section in the section slice;
|
||||||
// however, calling this method on any section is allowed. Clients may not call
|
// however, calling this method on any section is allowed. Clients may not call
|
||||||
// this method if the number of members in this section is equal to Total.
|
// this method if the number of members in this section is equal to Total.
|
||||||
LoadMore() bool // Blocking
|
LoadMore(context.Context) bool // Blocking
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemberListContainer is a generic interface for any container that can display
|
// MemberListContainer is a generic interface for any container that can display
|
||||||
|
@ -450,7 +452,7 @@ type MemberDynamicSection interface {
|
||||||
type MemberListContainer interface {
|
type MemberListContainer interface {
|
||||||
// RemoveMember removes a member from a section. If neither the member nor the
|
// RemoveMember removes a member from a section. If neither the member nor the
|
||||||
// section exists, then the client should ignore it.
|
// section exists, then the client should ignore it.
|
||||||
RemoveMember(sectionID ID, memberID ID)
|
RemoveMember(ctx context.Context, sectionID ID, memberID ID)
|
||||||
// SetMember adds or updates (or upsert) a member into a section. This operation
|
// 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
|
// 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
|
// separately in SetSection. If the section does not exist, then the client
|
||||||
|
@ -460,12 +462,12 @@ type MemberListContainer interface {
|
||||||
// Typically, the backend should try and avoid calling this method and instead
|
// Typically, the backend should try and avoid calling this method and instead
|
||||||
// update the labeler in the name. This method should only be used for adding
|
// update the labeler in the name. This method should only be used for adding
|
||||||
// members.
|
// members.
|
||||||
SetMember(sectionID ID, member ListMember)
|
SetMember(ctx context.Context, sectionID ID, member ListMember)
|
||||||
// SetSections (re)sets the list of sections to be the given slice. Members from
|
// 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 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
|
// the section name's content is the same. Old sections that don't appear in the
|
||||||
// new slice should be removed.
|
// new slice should be removed.
|
||||||
SetSections(sections []MemberSection)
|
SetSections(ctx context.Context, sections []MemberSection)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemberLister adds a member list into a message server.
|
// MemberLister adds a member list into a message server.
|
||||||
|
@ -518,11 +520,13 @@ type MessageHeader interface {
|
||||||
Time() time.Time
|
Time() time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageUpdate is the interface for a message update (or edit) event. It
|
// MessageUpdate is the interface for a message update (or edit) event. It is
|
||||||
// behaves similarly to MessageCreate, except all fields are optional. The
|
// only responsible for updating a message's content. The author's name should
|
||||||
// frontend is responsible for checking which field is not empty and check it.
|
// be updated using MessageCreate's Author.
|
||||||
type MessageUpdate interface {
|
type MessageUpdate interface {
|
||||||
MessageCreate
|
MessageHeader
|
||||||
|
|
||||||
|
Content() text.Rich
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessagesContainer is a view implementation that displays a list of messages
|
// MessagesContainer is a view implementation that displays a list of messages
|
||||||
|
@ -533,12 +537,12 @@ type MessageUpdate interface {
|
||||||
// allowed to have multiple views. This is usually done with tabs or splits, but
|
// allowed to have multiple views. This is usually done with tabs or splits, but
|
||||||
// the backend should update them all nonetheless.
|
// the backend should update them all nonetheless.
|
||||||
type MessagesContainer interface {
|
type MessagesContainer interface {
|
||||||
DeleteMessage(MessageDelete)
|
DeleteMessage(context.Context, MessageDelete)
|
||||||
UpdateMessage(MessageUpdate)
|
UpdateMessage(context.Context, MessageUpdate)
|
||||||
// CreateMessage inserts a message into the container. The frontend must
|
// CreateMessage inserts a message into the container. The frontend must
|
||||||
// guarantee that the messages are in order based on what's returned from
|
// guarantee that the messages are in order based on what's returned from
|
||||||
// Time().
|
// Time().
|
||||||
CreateMessage(MessageCreate)
|
CreateMessage(context.Context, MessageCreate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messenger is for servers that contain messages. This is similar to Discord or
|
// Messenger is for servers that contain messages. This is similar to Discord or
|
||||||
|
@ -587,7 +591,7 @@ type Namer interface {
|
||||||
// implement ServerMessage also don't need to implement ServerNickname. By
|
// implement ServerMessage also don't need to implement ServerNickname. By
|
||||||
// default, the session name should be used.
|
// default, the session name should be used.
|
||||||
type Nicknamer interface {
|
type Nicknamer interface {
|
||||||
Nickname(context.Context, LabelContainer) (stop func(), err error)
|
Namer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Noncer adds nonce support. A nonce is defined in this context as a unique
|
// Noncer adds nonce support. A nonce is defined in this context as a unique
|
||||||
|
@ -614,10 +618,10 @@ type ReadContainer interface {
|
||||||
// their read indicators. The backend can use this to free up users/authors that
|
// their read indicators. The backend can use this to free up users/authors that
|
||||||
// are no longer in the server, for example when they are offline or have left
|
// are no longer in the server, for example when they are offline or have left
|
||||||
// the server.
|
// the server.
|
||||||
DeleteIndications(authorIDs []ID)
|
DeleteIndications(ctx context.Context, authorIDs []ID)
|
||||||
// AddIndications adds a map of users/authors to the respective message ID of
|
// AddIndications adds a map of users/authors to the respective message ID of
|
||||||
// the server that implements ReadIndicator.
|
// the server that implements ReadIndicator.
|
||||||
AddIndications([]ReadIndication)
|
AddIndications(context.Context, []ReadIndication)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadIndicator adds a read indicator API for frontends to show. An example of
|
// ReadIndicator adds a read indicator API for frontends to show. An example of
|
||||||
|
@ -628,7 +632,7 @@ type ReadIndicator interface {
|
||||||
// must keep track of which read states to send over to not overwhelm the
|
// must keep track of which read states to send over to not overwhelm the
|
||||||
// frontend, and the frontend must either keep track of them, or it should not
|
// frontend, and the frontend must either keep track of them, or it should not
|
||||||
// display it at all.
|
// display it at all.
|
||||||
ReadIndicate(ReadContainer) (stop func(), err error)
|
ReadIndicate(context.Context, ReadContainer) (stop func(), err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replier indicates that the message being sent is a reply to something.
|
// Replier indicates that the message being sent is a reply to something.
|
||||||
|
@ -662,7 +666,7 @@ type Sender interface {
|
||||||
// CanAttach returns whether or not the client is allowed to upload files.
|
// CanAttach returns whether or not the client is allowed to upload files.
|
||||||
CanAttach() bool
|
CanAttach() bool
|
||||||
// Send is called by the frontend to send a message to this channel.
|
// Send is called by the frontend to send a message to this channel.
|
||||||
Send(SendableMessage) error // Blocking
|
Send(context.Context, SendableMessage) error // Blocking
|
||||||
|
|
||||||
// Asserters.
|
// Asserters.
|
||||||
|
|
||||||
|
@ -717,7 +721,7 @@ type ServerUpdate interface {
|
||||||
// as servers can be infinitely nested. Frontends should also reset the entire
|
// as servers can be infinitely nested. Frontends should also reset the entire
|
||||||
// node and its children when SetServers is called again.
|
// node and its children when SetServers is called again.
|
||||||
type ServersContainer interface {
|
type ServersContainer interface {
|
||||||
UpdateServer(ServerUpdate)
|
UpdateServer(context.Context, ServerUpdate)
|
||||||
// SetServer is called by the backend service to request a reset of the server
|
// 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,
|
// 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.
|
// or it can call that later. The backend should handle both cases.
|
||||||
|
@ -727,12 +731,12 @@ type ServersContainer interface {
|
||||||
// should only be considered empty if it's an empty non-nil slice. An
|
// should only be considered empty if it's an empty non-nil slice. An
|
||||||
// unavailable list, on the other hand, can be treated as backend issues, e.g. a
|
// unavailable list, on the other hand, can be treated as backend issues, e.g. a
|
||||||
// connection issue.
|
// connection issue.
|
||||||
SetServers([]Server)
|
SetServers(context.Context, []Server)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A service is a complete service that's capable of multiple sessions. It has
|
// Service is a complete service that's capable of multiple sessions. It has to
|
||||||
// to implement the Authenticate() method, which returns multiple
|
// implement the Authenticate() method, which returns multiple implementations
|
||||||
// implementations of Authenticator.
|
// of Authenticator.
|
||||||
//
|
//
|
||||||
// A service can implement SessionRestorer, which would indicate the frontend
|
// A service can implement SessionRestorer, which would indicate the frontend
|
||||||
// that it can restore past sessions. Sessions are saved using the SessionSaver
|
// that it can restore past sessions. Sessions are saved using the SessionSaver
|
||||||
|
@ -744,6 +748,13 @@ type ServersContainer interface {
|
||||||
// configurations must be optional, as frontends may not implement a
|
// configurations must be optional, as frontends may not implement a
|
||||||
// configurator UI.
|
// configurator UI.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
|
// Identifier returns the unique identifier for the service. There is no
|
||||||
|
// enforced representation, but services are recommended to follow the Reverse
|
||||||
|
// Domain Name Notation for consistency. An example of that would be:
|
||||||
|
//
|
||||||
|
// com.github.diamondburned.cchat-discord
|
||||||
|
// com.github.username.service
|
||||||
|
Identifier
|
||||||
// Namer returns the name of the service.
|
// Namer returns the name of the service.
|
||||||
Namer
|
Namer
|
||||||
|
|
||||||
|
@ -783,7 +794,7 @@ type Session interface {
|
||||||
// When this function fails, the frontend may display the error upfront.
|
// When this function fails, the frontend may display the error upfront.
|
||||||
// However, it will treat the session as actually disconnected. If needed, the
|
// However, it will treat the session as actually disconnected. If needed, the
|
||||||
// backend must implement reconnection by itself.
|
// backend must implement reconnection by itself.
|
||||||
Disconnect() error // Blocking, Disposer
|
Disconnect(context.Context) error // Blocking, Disposer
|
||||||
|
|
||||||
// Asserters.
|
// Asserters.
|
||||||
|
|
||||||
|
@ -797,7 +808,7 @@ type Session interface {
|
||||||
//
|
//
|
||||||
// To save a session, refer to SessionSaver.
|
// To save a session, refer to SessionSaver.
|
||||||
type SessionRestorer interface {
|
type SessionRestorer interface {
|
||||||
RestoreSession(map[string]string) (Session, error) // Blocking
|
RestoreSession(context.Context, map[string]string) (Session, error) // Blocking
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionSaver extends Session and is called by the frontend to save the
|
// SessionSaver extends Session and is called by the frontend to save the
|
||||||
|
@ -824,11 +835,11 @@ type TypingContainer interface {
|
||||||
// of typers. This function is usually not needed, as the client will take care
|
// 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
|
// of removing them after TypingTimeout has been reached or other conditions
|
||||||
// listed in ServerMessageTypingIndicator are met.
|
// listed in ServerMessageTypingIndicator are met.
|
||||||
RemoveTyper(authorID ID)
|
RemoveTyper(ctx context.Context, authorID ID)
|
||||||
// AddTyper appends the typer (author) into the frontend's list of typers, or it
|
// AddTyper appends the typer (author) into the frontend's list of typers, or it
|
||||||
// pushes this typer on top of others. The frontend should assume current time
|
// pushes this typer on top of others. The frontend should assume current time
|
||||||
// every time AddTyper is called.
|
// every time AddTyper is called.
|
||||||
AddTyper(User)
|
AddTyper(context.Context, User)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypingIndicator optionally extends ServerMessage to provide bidirectional
|
// TypingIndicator optionally extends ServerMessage to provide bidirectional
|
||||||
|
@ -846,7 +857,7 @@ type TypingIndicator interface {
|
||||||
// This method does not take in a context, as it's supposed to only use event
|
// This method does not take in a context, as it's supposed to only use event
|
||||||
// handlers and not do any IO calls. Nonetheless, the client must treat it like
|
// handlers and not do any IO calls. Nonetheless, the client must treat it like
|
||||||
// it does and call it asynchronously.
|
// it does and call it asynchronously.
|
||||||
TypingSubscribe(TypingContainer) (stop func(), err error)
|
TypingSubscribe(context.Context, TypingContainer) (stop func(), err error)
|
||||||
// TypingTimeout returns the interval between typing events sent by the client
|
// TypingTimeout returns the interval between typing events sent by the client
|
||||||
// as well as the timeout before the client should remove the typer. Typically,
|
// as well as the timeout before the client should remove the typer. Typically,
|
||||||
// a constant should be returned.
|
// a constant should be returned.
|
||||||
|
@ -855,7 +866,7 @@ type TypingIndicator interface {
|
||||||
// function can do IO calls, and the client must take care of calling it in a
|
// function can do IO calls, and the client must take care of calling it in a
|
||||||
// goroutine (or an asynchronous queue) as well as throttling it to
|
// goroutine (or an asynchronous queue) as well as throttling it to
|
||||||
// TypingTimeout.
|
// TypingTimeout.
|
||||||
Typing() error // Blocking
|
Typing(context.Context) error // Blocking
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnreadContainer is an interface that a single server container (such as a
|
// UnreadContainer is an interface that a single server container (such as a
|
||||||
|
@ -874,7 +885,7 @@ type TypingIndicator interface {
|
||||||
type UnreadContainer interface {
|
type UnreadContainer interface {
|
||||||
// SetUnread sets the container's unread state to the given boolean. The
|
// SetUnread sets the container's unread state to the given boolean. The
|
||||||
// frontend may choose how to represent this.
|
// frontend may choose how to represent this.
|
||||||
SetUnread(unread bool, mentioned bool)
|
SetUnread(ctx context.Context, unread bool, mentioned bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnreadIndicator adds an unread state API for frontends to use. The unread
|
// UnreadIndicator adds an unread state API for frontends to use. The unread
|
||||||
|
@ -888,7 +899,7 @@ type UnreadIndicator interface {
|
||||||
//
|
//
|
||||||
// This function must provide a way to remove callbacks, as clients must call
|
// This function must provide a way to remove callbacks, as clients must call
|
||||||
// this when the old server is destroyed, such as when Servers is called.
|
// this when the old server is destroyed, such as when Servers is called.
|
||||||
UnreadIndicate(UnreadContainer) (stop func(), err error)
|
UnreadIndicate(context.Context, UnreadContainer) (stop func(), err error)
|
||||||
// MarkRead marks a message in the server messenger as read. Backends that
|
// MarkRead marks a message in the server messenger as read. Backends that
|
||||||
// implement the UnreadIndicator interface must give control of marking messages
|
// implement the UnreadIndicator interface must give control of marking messages
|
||||||
// as read to the frontend if possible.
|
// as read to the frontend if possible.
|
||||||
|
@ -897,7 +908,7 @@ type UnreadIndicator interface {
|
||||||
// the frontend has no use in knowing the error. As such, marking messages as
|
// the frontend has no use in knowing the error. As such, marking messages as
|
||||||
// read is best-effort. The backend is in charge of synchronizing the read state
|
// read is best-effort. The backend is in charge of synchronizing the read state
|
||||||
// with the server and coordinating it with reasonable rate limits, if needed.
|
// with the server and coordinating it with reasonable rate limits, if needed.
|
||||||
MarkRead(messageID ID)
|
MarkRead(ctx context.Context, messageID ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// User is the interface for an identifiable author. The interface defines that
|
// User is the interface for an identifiable author. The interface defines that
|
||||||
|
|
|
@ -66,9 +66,13 @@ func generateInterfaces(ifaces []repository.Interface) jen.Code {
|
||||||
stmt.Params(generateFuncParams(method.Returns, method.ErrorType)...)
|
stmt.Params(generateFuncParams(method.Returns, method.ErrorType)...)
|
||||||
case repository.SetterMethod:
|
case repository.SetterMethod:
|
||||||
stmt.Params(generateFuncParams(method.Parameters, "")...)
|
stmt.Params(generateFuncParams(method.Parameters, "")...)
|
||||||
|
stmt.Params(generateFuncParamsErr(repository.NamedType{}, method.ErrorType)...)
|
||||||
|
case repository.ContainerUpdaterMethod:
|
||||||
|
stmt.Params(generateFuncParamsCtx(method.Parameters, "")...)
|
||||||
|
stmt.Params(generateFuncParamsErr(repository.NamedType{}, method.ErrorType)...)
|
||||||
case repository.IOMethod:
|
case repository.IOMethod:
|
||||||
stmt.Params(generateFuncParams(method.Parameters, "")...)
|
stmt.Params(generateFuncParamsCtx(method.Parameters, "")...)
|
||||||
stmt.Params(generateFuncParamErr(method.ReturnValue, method.ErrorType)...)
|
stmt.Params(generateFuncParamsErr(method.ReturnValue, method.ErrorType)...)
|
||||||
var comment = "Blocking"
|
var comment = "Blocking"
|
||||||
if method.Disposer {
|
if method.Disposer {
|
||||||
comment += ", Disposer"
|
comment += ", Disposer"
|
||||||
|
@ -96,7 +100,7 @@ func generateInterfaces(ifaces []repository.Interface) jen.Code {
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFuncParamErr(param repository.NamedType, errorType string) []jen.Code {
|
func generateFuncParamsErr(param repository.NamedType, errorType string) []jen.Code {
|
||||||
stmt := make([]jen.Code, 0, 2)
|
stmt := make([]jen.Code, 0, 2)
|
||||||
|
|
||||||
if !param.IsZero() {
|
if !param.IsZero() {
|
||||||
|
@ -121,6 +125,18 @@ func generateFuncParam(param repository.NamedType) jen.Code {
|
||||||
return jen.Id(param.Name).Add(genutils.GenerateType(param))
|
return jen.Id(param.Name).Add(genutils.GenerateType(param))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateFuncParamsCtx(params []repository.NamedType, errorType string) []jen.Code {
|
||||||
|
var name string
|
||||||
|
if len(params) > 0 && params[0].Name != "" {
|
||||||
|
name = "ctx"
|
||||||
|
}
|
||||||
|
|
||||||
|
p := []repository.NamedType{{Name: name, Type: "context.Context"}}
|
||||||
|
p = append(p, params...)
|
||||||
|
|
||||||
|
return generateFuncParams(p, errorType)
|
||||||
|
}
|
||||||
|
|
||||||
func generateFuncParams(params []repository.NamedType, errorType string) []jen.Code {
|
func generateFuncParams(params []repository.NamedType, errorType string) []jen.Code {
|
||||||
if len(params) == 0 {
|
if len(params) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
Binary file not shown.
|
@ -10,6 +10,7 @@ func init() {
|
||||||
gob.Register(AsserterMethod{})
|
gob.Register(AsserterMethod{})
|
||||||
gob.Register(GetterMethod{})
|
gob.Register(GetterMethod{})
|
||||||
gob.Register(SetterMethod{})
|
gob.Register(SetterMethod{})
|
||||||
|
gob.Register(ContainerUpdaterMethod{})
|
||||||
gob.Register(IOMethod{})
|
gob.Register(IOMethod{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,21 +68,39 @@ func (m GetterMethod) ReturnError() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetterMethod is a method that sets values. These methods must not do IO, and
|
// SetterMethod is a method that sets values. These methods must not do IO, and
|
||||||
// they have to be non-blocking. They're used only for containers. Actual setter
|
// they have to be non-blocking.
|
||||||
// methods implemented by the backend belongs to IOMethods.
|
|
||||||
//
|
|
||||||
// Clients should always keep track of the values given to setters and free them
|
|
||||||
// once the setters are called again with new values.
|
|
||||||
type SetterMethod struct {
|
type SetterMethod struct {
|
||||||
method
|
method
|
||||||
|
|
||||||
// Parameters is the list of parameters in the function. These parameters
|
// Parameters is the list of parameters in the function. These parameters
|
||||||
// should be the parameters to set.
|
// should be the parameters to set.
|
||||||
Parameters []NamedType
|
Parameters []NamedType
|
||||||
|
// ErrorType is non-empty if the function returns an error at the end of
|
||||||
|
// returns. An error may be returned from the backend if the input is
|
||||||
|
// invalid, but it must not do IO. Frontend setters must never error.
|
||||||
|
ErrorType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerUpdaterMethod is a SetterMethod that passes to the container the
|
||||||
|
// current context to prevent race conditions when synchronizing.
|
||||||
|
// The rule of thumb is that any setter method done inside a method with a
|
||||||
|
// context is usually this type of method.
|
||||||
|
type ContainerUpdaterMethod struct {
|
||||||
|
method
|
||||||
|
|
||||||
|
// Parameters is the list of parameters in the function. These parameters
|
||||||
|
// should be the parameters to set.
|
||||||
|
Parameters []NamedType
|
||||||
|
// ErrorType is non-empty if the function returns an error at the end of
|
||||||
|
// returns. An error may be returned from the backend if the input is
|
||||||
|
// invalid, but it must not do IO. Frontend setters must never error.
|
||||||
|
ErrorType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOMethod is a regular method that can do IO and thus is blocking. These
|
// IOMethod is a regular method that can do IO and thus is blocking. These
|
||||||
// methods usually always return errors.
|
// methods usually always return errors. IOMethods must always have means of
|
||||||
|
// cancelling them in the API, but implementations don't have to use it; as
|
||||||
|
// such, the user should always have a timeout to gracefully wait.
|
||||||
type IOMethod struct {
|
type IOMethod struct {
|
||||||
method
|
method
|
||||||
|
|
||||||
|
|
|
@ -651,7 +651,7 @@ var Main = Packages{
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
A service is a complete service that's capable of multiple
|
Service is a complete service that's capable of multiple
|
||||||
sessions. It has to implement the Authenticate() method, which
|
sessions. It has to implement the Authenticate() method, which
|
||||||
returns multiple implementations of Authenticator.
|
returns multiple implementations of Authenticator.
|
||||||
|
|
||||||
|
@ -668,6 +668,17 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "Service",
|
Name: "Service",
|
||||||
Embeds: []EmbeddedInterface{{
|
Embeds: []EmbeddedInterface{{
|
||||||
|
Comment: Comment{`
|
||||||
|
Identifier returns the unique identifier for the service. There
|
||||||
|
is no enforced representation, but services are recommended to
|
||||||
|
follow the Reverse Domain Name Notation for consistency. An
|
||||||
|
example of that would be:
|
||||||
|
|
||||||
|
com.github.diamondburned.cchat-discord
|
||||||
|
com.github.username.service
|
||||||
|
`},
|
||||||
|
InterfaceName: "Identifier",
|
||||||
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
Namer returns the name of the service.
|
Namer returns the name of the service.
|
||||||
`},
|
`},
|
||||||
|
@ -806,18 +817,15 @@ var Main = Packages{
|
||||||
}, {
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
Configurator is an interface which the backend can implement for a
|
Configurator is an interface which the backend can implement for a
|
||||||
primitive configuration API. Since these methods do return an error,
|
primitive configuration API.
|
||||||
they are allowed to do IO. The frontend should handle this
|
|
||||||
appropriately, including running them asynchronously.
|
|
||||||
`},
|
`},
|
||||||
Name: "Configurator",
|
Name: "Configurator",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
IOMethod{
|
GetterMethod{
|
||||||
method: method{Name: "Configuration"},
|
method: method{Name: "Configuration"},
|
||||||
ReturnValue: NamedType{Type: "map[string]string"},
|
Returns: []NamedType{{Type: "map[string]string"}},
|
||||||
ErrorType: "error",
|
|
||||||
},
|
},
|
||||||
IOMethod{
|
SetterMethod{
|
||||||
method: method{Name: "SetConfiguration"},
|
method: method{Name: "SetConfiguration"},
|
||||||
Parameters: []NamedType{{Type: "map[string]string"}},
|
Parameters: []NamedType{{Type: "map[string]string"}},
|
||||||
ErrorType: "error",
|
ErrorType: "error",
|
||||||
|
@ -1004,22 +1012,24 @@ var Main = Packages{
|
||||||
GetterMethod{
|
GetterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
Columnate is optionally used by servers to give
|
Columnate is optionally used by servers to tell the
|
||||||
different nested servers its own nesting values.
|
frontend whether or not its children should be put
|
||||||
Top-level servers must start at 1. The zero-value
|
onto a new column instead of underneath it within
|
||||||
(0) indicates that the server that implements this
|
the same tree. If the method returns false, then the
|
||||||
interface is inherently the children of its parent
|
frontend can treat its children as normal and show
|
||||||
server.
|
it as children within the same tree.
|
||||||
|
|
||||||
For example, in Discord, guilds can be placed in
|
For example, in Discord, guilds can be placed in
|
||||||
guild folders, but guilds and guild folders are put
|
guild folders, but guilds and guild folders are put
|
||||||
in the same column while guilds are actually
|
in the same column while guilds are actually
|
||||||
children of the folders. To replicate this behavior,
|
children of the folders. To replicate this behavior,
|
||||||
both guild and guild folders can return 1.
|
guild folders should return false, and guilds should
|
||||||
|
return true. Both channels and categories can return
|
||||||
|
false.
|
||||||
`},
|
`},
|
||||||
Name: "Columnate",
|
Name: "Columnate",
|
||||||
},
|
},
|
||||||
Returns: []NamedType{{"", "int"}},
|
Returns: []NamedType{{"", "bool"}},
|
||||||
},
|
},
|
||||||
ContainerMethod{
|
ContainerMethod{
|
||||||
method: method{
|
method: method{
|
||||||
|
@ -1198,14 +1208,8 @@ var Main = Packages{
|
||||||
implement ServerNickname. By default, the session name should be
|
implement ServerNickname. By default, the session name should be
|
||||||
used.
|
used.
|
||||||
`},
|
`},
|
||||||
Name: "Nicknamer",
|
Name: "Nicknamer",
|
||||||
Methods: []Method{
|
Embeds: []EmbeddedInterface{{InterfaceName: "Namer"}},
|
||||||
ContainerMethod{
|
|
||||||
method: method{Name: "Nickname"},
|
|
||||||
HasContext: true,
|
|
||||||
ContainerType: "LabelContainer",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
Backlogger adds message history capabilities into a message
|
Backlogger adds message history capabilities into a message
|
||||||
|
@ -1244,7 +1248,6 @@ var Main = Packages{
|
||||||
Name: "Backlog",
|
Name: "Backlog",
|
||||||
},
|
},
|
||||||
Parameters: []NamedType{
|
Parameters: []NamedType{
|
||||||
{"ctx", "context.Context"},
|
|
||||||
{"before", "ID"},
|
{"before", "ID"},
|
||||||
{"msgc", "MessagesContainer"},
|
{"msgc", "MessagesContainer"},
|
||||||
},
|
},
|
||||||
|
@ -1295,6 +1298,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "ReadIndicate",
|
Name: "ReadIndicate",
|
||||||
},
|
},
|
||||||
|
HasContext: true,
|
||||||
ContainerType: "ReadContainer",
|
ContainerType: "ReadContainer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1307,7 +1311,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "UnreadIndicator",
|
Name: "UnreadIndicator",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
MarkRead marks a message in the server messenger as
|
MarkRead marks a message in the server messenger as
|
||||||
|
@ -1341,6 +1345,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "UnreadIndicate",
|
Name: "UnreadIndicate",
|
||||||
},
|
},
|
||||||
|
HasContext: true,
|
||||||
ContainerType: "UnreadContainer",
|
ContainerType: "UnreadContainer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1396,6 +1401,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "TypingSubscribe",
|
Name: "TypingSubscribe",
|
||||||
},
|
},
|
||||||
|
HasContext: true,
|
||||||
ContainerType: "TypingContainer",
|
ContainerType: "TypingContainer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1446,7 +1452,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "ServersContainer",
|
Name: "ServersContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
SetServer is called by the backend service to
|
SetServer is called by the backend service to
|
||||||
|
@ -1467,7 +1473,7 @@ var Main = Packages{
|
||||||
},
|
},
|
||||||
Parameters: []NamedType{{Type: "[]Server"}},
|
Parameters: []NamedType{{Type: "[]Server"}},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{Name: "UpdateServer"},
|
method: method{Name: "UpdateServer"},
|
||||||
Parameters: []NamedType{{Type: "ServerUpdate"}},
|
Parameters: []NamedType{{Type: "ServerUpdate"}},
|
||||||
},
|
},
|
||||||
|
@ -1527,7 +1533,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "MessagesContainer",
|
Name: "MessagesContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
CreateMessage inserts a message into the container.
|
CreateMessage inserts a message into the container.
|
||||||
|
@ -1538,11 +1544,11 @@ var Main = Packages{
|
||||||
},
|
},
|
||||||
Parameters: []NamedType{{Type: "MessageCreate"}},
|
Parameters: []NamedType{{Type: "MessageCreate"}},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{Name: "UpdateMessage"},
|
method: method{Name: "UpdateMessage"},
|
||||||
Parameters: []NamedType{{Type: "MessageUpdate"}},
|
Parameters: []NamedType{{Type: "MessageUpdate"}},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{Name: "DeleteMessage"},
|
method: method{Name: "DeleteMessage"},
|
||||||
Parameters: []NamedType{{Type: "MessageDelete"}},
|
Parameters: []NamedType{{Type: "MessageDelete"}},
|
||||||
},
|
},
|
||||||
|
@ -1595,12 +1601,20 @@ var Main = Packages{
|
||||||
}, {
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
MessageUpdate is the interface for a message update (or edit)
|
MessageUpdate is the interface for a message update (or edit)
|
||||||
event. It behaves similarly to MessageCreate, except all fields
|
event. It is only responsible for updating a message's content.
|
||||||
are optional. The frontend is responsible for checking which
|
The author's name should be updated using MessageCreate's
|
||||||
field is not empty and check it.
|
Author.
|
||||||
`},
|
`},
|
||||||
Name: "MessageUpdate",
|
Name: "MessageUpdate",
|
||||||
Embeds: []EmbeddedInterface{{InterfaceName: "MessageCreate"}},
|
Embeds: []EmbeddedInterface{{InterfaceName: "MessageHeader"}},
|
||||||
|
Methods: []Method{
|
||||||
|
GetterMethod{
|
||||||
|
method: method{Name: "Content"},
|
||||||
|
Returns: []NamedType{{
|
||||||
|
Type: MakeQual("text", "Rich"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
MessageDelete is the interface for a message delete event.
|
MessageDelete is the interface for a message delete event.
|
||||||
|
@ -1623,7 +1637,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "LabelContainer",
|
Name: "LabelContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{Name: "SetLabel"},
|
method: method{Name: "SetLabel"},
|
||||||
Parameters: []NamedType{{
|
Parameters: []NamedType{{
|
||||||
Type: MakeQual("text", "Rich"),
|
Type: MakeQual("text", "Rich"),
|
||||||
|
@ -1639,7 +1653,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "ReadContainer",
|
Name: "ReadContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
AddIndications adds a map of users/authors to the
|
AddIndications adds a map of users/authors to the
|
||||||
|
@ -1650,7 +1664,7 @@ var Main = Packages{
|
||||||
},
|
},
|
||||||
Parameters: []NamedType{{"", "[]ReadIndication"}},
|
Parameters: []NamedType{{"", "[]ReadIndication"}},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
DeleteIndications deletes a list of unused
|
DeleteIndications deletes a list of unused
|
||||||
|
@ -1684,7 +1698,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "UnreadContainer",
|
Name: "UnreadContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
SetUnread sets the container's unread state to the
|
SetUnread sets the container's unread state to the
|
||||||
|
@ -1710,7 +1724,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "TypingContainer",
|
Name: "TypingContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
AddTyper appends the typer (author) into the
|
AddTyper appends the typer (author) into the
|
||||||
|
@ -1722,7 +1736,7 @@ var Main = Packages{
|
||||||
},
|
},
|
||||||
Parameters: []NamedType{{"", "User"}},
|
Parameters: []NamedType{{"", "User"}},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
RemoveTyper explicitly removes the typer with the
|
RemoveTyper explicitly removes the typer with the
|
||||||
|
@ -1762,7 +1776,7 @@ var Main = Packages{
|
||||||
`},
|
`},
|
||||||
Name: "MemberListContainer",
|
Name: "MemberListContainer",
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
SetSections (re)sets the list of sections to be the
|
SetSections (re)sets the list of sections to be the
|
||||||
|
@ -1778,7 +1792,7 @@ var Main = Packages{
|
||||||
{Name: "sections", Type: "[]MemberSection"},
|
{Name: "sections", Type: "[]MemberSection"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
SetMember adds or updates (or upsert) a member into
|
SetMember adds or updates (or upsert) a member into
|
||||||
|
@ -1801,7 +1815,7 @@ var Main = Packages{
|
||||||
{"member", "ListMember"},
|
{"member", "ListMember"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SetterMethod{
|
ContainerUpdaterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
RemoveMember removes a member from a section. If
|
RemoveMember removes a member from a section. If
|
||||||
|
@ -1818,18 +1832,29 @@ var Main = Packages{
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
ListMember represents a single member in the member list. This
|
ListMember represents a single member in the member list. Note
|
||||||
is a base interface that may implement more interfaces, such as
|
that this interface should be treated as a static container:
|
||||||
Iconer for the user's avatar.
|
updating a member will involve a completely new ListMember
|
||||||
|
instance with the same ID.
|
||||||
|
|
||||||
Note that the frontend may give everyone an avatar regardless,
|
Note that the frontend may give everyone an avatar regardless,
|
||||||
or it may not show any avatars at all.
|
or it may not show any avatars at all.
|
||||||
`},
|
`},
|
||||||
Name: "ListMember",
|
Name: "ListMember",
|
||||||
Embeds: []EmbeddedInterface{
|
Embeds: []EmbeddedInterface{
|
||||||
{InterfaceName: "User"},
|
{InterfaceName: "Identifier"},
|
||||||
},
|
},
|
||||||
Methods: []Method{
|
Methods: []Method{
|
||||||
|
GetterMethod{
|
||||||
|
method: method{
|
||||||
|
Name: "Name",
|
||||||
|
Comment: Comment{`
|
||||||
|
Name returns the username or the nickname of the
|
||||||
|
member, whichever the backend should prefer.
|
||||||
|
`},
|
||||||
|
},
|
||||||
|
Returns: []NamedType{{Type: MakeQual("text", "Rich")}},
|
||||||
|
},
|
||||||
GetterMethod{
|
GetterMethod{
|
||||||
method: method{
|
method: method{
|
||||||
Comment: Comment{`
|
Comment: Comment{`
|
||||||
|
|
|
@ -11,7 +11,9 @@ func SolidColor(rgb uint32) uint32 {
|
||||||
return (rgb << 8) | 0xFF
|
return (rgb << 8) | 0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty returns true if the given rich segment's content is empty.
|
// IsEmpty returns true if the given rich segment's content is empty. Note that
|
||||||
|
// a rich text is not necessarily empty if the content is empty, because there
|
||||||
|
// may be images within the segments.
|
||||||
func (r Rich) IsEmpty() bool {
|
func (r Rich) IsEmpty() bool {
|
||||||
return r.Content == ""
|
return r.Content == "" && len(r.Segments) == 0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue