Create chat/ package with Message type, Room (NATS pub/sub + buffer), DB persistence helpers, and a unified templ component parameterized by Config (CSS prefix, post URL, color function, key propagation). Both c4game and snakegame now use chat.Room for message management and chatcomponents.Chat for rendering, eliminating the duplicated ChatMessage types, chat templ components, chatAutoScroll scripts, color functions, and inline buffer management.
75 lines
2.1 KiB
Plaintext
75 lines
2.1 KiB
Plaintext
package components
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/ryanhamamura/c4/chat"
|
|
"github.com/starfederation/datastar-go/datastar"
|
|
)
|
|
|
|
// ColorFunc resolves a player slot to a CSS color string.
|
|
type ColorFunc func(slot int) string
|
|
|
|
// Config holds the game-specific settings for rendering a chat component.
|
|
type Config struct {
|
|
// CSSPrefix is used for element IDs and CSS classes (e.g. "c4" or "snake").
|
|
CSSPrefix string
|
|
// PostURL is the URL to POST chat messages to (e.g. "/games/{id}/chat").
|
|
PostURL string
|
|
// Color resolves a player slot to a CSS color string.
|
|
Color ColorFunc
|
|
// StopKeyPropagation adds data-on:keydown.stop="" to the input to prevent
|
|
// key events from propagating (needed for snake to avoid steering while typing).
|
|
StopKeyPropagation bool
|
|
}
|
|
|
|
templ Chat(messages []chat.Message, cfg Config) {
|
|
<div id={ cfg.CSSPrefix + "-chat" } class={ cfg.CSSPrefix + "-chat" }>
|
|
<div class={ cfg.CSSPrefix + "-chat-history" }>
|
|
for _, m := range messages {
|
|
<div class={ cfg.CSSPrefix + "-chat-msg" }>
|
|
<span style={ fmt.Sprintf("color:%s;font-weight:bold;", cfg.Color(m.Slot)) }>
|
|
{ m.Nickname + ": " }
|
|
</span>
|
|
<span>{ m.Message }</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
<div class={ cfg.CSSPrefix + "-chat-input" } data-morph-ignore>
|
|
if cfg.StopKeyPropagation {
|
|
<input
|
|
type="text"
|
|
placeholder="Chat..."
|
|
autocomplete="off"
|
|
data-bind="chatMsg"
|
|
data-on:keydown.stop=""
|
|
data-on:keydown.key_enter={ datastar.PostSSE(cfg.PostURL) }
|
|
/>
|
|
} else {
|
|
<input
|
|
type="text"
|
|
placeholder="Chat..."
|
|
autocomplete="off"
|
|
data-bind="chatMsg"
|
|
data-on:keydown.enter={ datastar.PostSSE(cfg.PostURL) }
|
|
/>
|
|
}
|
|
<button
|
|
type="button"
|
|
data-on:click={ datastar.PostSSE(cfg.PostURL) }
|
|
>
|
|
Send
|
|
</button>
|
|
</div>
|
|
@chatAutoScroll(cfg.CSSPrefix)
|
|
</div>
|
|
}
|
|
|
|
script chatAutoScroll(cssPrefix string) {
|
|
var el = document.querySelector('.' + cssPrefix + '-chat-history');
|
|
if (!el) return;
|
|
el.scrollTop = el.scrollHeight;
|
|
new MutationObserver(function(){ el.scrollTop = el.scrollHeight; })
|
|
.observe(el, {childList:true, subtree:true});
|
|
}
|