}
// Render message based on a theme.
-func (m *Msg) Render(t *Theme) string {
+func (m Msg) Render(t *Theme) string {
// TODO: Render based on theme
// TODO: Cache based on theme
return m.String()
}
-func (m *Msg) String() string {
+func (m Msg) String() string {
return m.body
}
-func (m *Msg) Command() string {
+func (m Msg) Command() string {
return ""
}
-func (m *Msg) Timestamp() time.Time {
+func (m Msg) Timestamp() time.Time {
return m.timestamp
}
from *User
}
-func NewPublicMsg(body string, from *User) *PublicMsg {
- return &PublicMsg{
+func NewPublicMsg(body string, from *User) PublicMsg {
+ return PublicMsg{
Msg: Msg{
body: body,
timestamp: time.Now(),
}
}
-func (m *PublicMsg) From() *User {
+func (m PublicMsg) From() *User {
return m.from
}
-func (m *PublicMsg) ParseCommand() (*CommandMsg, bool) {
+func (m PublicMsg) ParseCommand() (*CommandMsg, bool) {
// Check if the message is a command
if !strings.HasPrefix(m.body, "/") {
return nil, false
return &msg, true
}
-func (m *PublicMsg) Render(t *Theme) string {
+func (m PublicMsg) Render(t *Theme) string {
if t == nil {
return m.String()
}
return fmt.Sprintf("%s: %s", t.ColorName(m.from), m.body)
}
-func (m *PublicMsg) RenderFor(cfg UserConfig) string {
+func (m PublicMsg) RenderFor(cfg UserConfig) string {
if cfg.Highlight == nil || cfg.Theme == nil {
return m.Render(cfg.Theme)
}
return fmt.Sprintf("%s: %s", cfg.Theme.ColorName(m.from), body)
}
-func (m *PublicMsg) String() string {
+func (m PublicMsg) String() string {
return fmt.Sprintf("%s: %s", m.from.Name(), m.body)
}
to *User
}
-func NewPrivateMsg(body string, from *User, to *User) *PrivateMsg {
- return &PrivateMsg{
- PublicMsg: *NewPublicMsg(body, from),
+func NewPrivateMsg(body string, from *User, to *User) PrivateMsg {
+ return PrivateMsg{
+ PublicMsg: NewPublicMsg(body, from),
to: to,
}
}
}
type CommandMsg struct {
- *PublicMsg
+ PublicMsg
command string
args []string
}
-func (m *CommandMsg) Command() string {
+func (m CommandMsg) Command() string {
return m.command
}
-func (m *CommandMsg) Args() []string {
+func (m CommandMsg) Args() []string {
return m.args
}
-func (m *CommandMsg) Body() string {
+func (m CommandMsg) Body() string {
return m.body
}
// User definition, implemented set Item interface and io.Writer
type User struct {
Identifier
- Config UserConfig
- colorIdx int
- joined time.Time
- msg chan Message
- done chan struct{}
- replyTo *User // Set when user gets a /msg, for replying.
- closed bool
- closeOnce sync.Once
+ Config UserConfig
+ colorIdx int
+ joined time.Time
+ msg chan Message
+ done chan struct{}
+
+ mu sync.Mutex
+ replyTo *User // Set when user gets a /msg, for replying.
+ screen io.Closer
+ closed bool
}
func NewUser(identity Identifier) *User {
return &u
}
-func NewUserScreen(identity Identifier, screen io.Writer) *User {
+func NewUserScreen(identity Identifier, screen io.WriteCloser) *User {
u := NewUser(identity)
+ u.screen = screen
go u.Consume(screen)
return u
// Disconnect user, stop accepting messages
func (u *User) Close() {
- u.closeOnce.Do(func() {
- u.closed = true
- close(u.done)
- close(u.msg)
- })
+ u.mu.Lock()
+ defer u.mu.Unlock()
+
+ if u.closed {
+ return
+ }
+
+ u.closed = true
+ close(u.done)
+ close(u.msg)
+
+ if u.screen != nil {
+ u.screen.Close()
+ }
}
// Consume message buffer into an io.Writer. Will block, should be called in a
// Add message to consume by user
func (u *User) Send(m Message) error {
+ u.mu.Lock()
+ defer u.mu.Unlock()
+
if u.closed {
return ErrUserClosed
}
id := NewIdentity(term.Conn)
user := message.NewUserScreen(id, term)
user.Config.Theme = &h.theme
- go func() {
- // Close term once user is closed.
- user.Wait()
- term.Close()
- }()
+
+ // Close term once user is closed.
defer user.Close()
+ defer term.Close()
h.mu.Lock()
motd := h.motd
}
m := message.NewPrivateMsg(strings.Join(args[1:], " "), msg.From(), target)
- room.Send(m)
+ room.Send(&m)
return nil
},
})
}
m := message.NewPrivateMsg(strings.Join(args, " "), msg.From(), target)
- room.Send(m)
+ room.Send(&m)
return nil
},
})