Added spoilers in chat

If a user clicks the spoiler, it will change color and the background will revert.
closes #62
This commit is contained in:
joeyak 2019-03-24 23:43:30 -04:00
parent d419f64379
commit 86207ecd48
9 changed files with 112 additions and 36 deletions

View File

@ -1,8 +1,8 @@
TAGS= TAGS=
.PHONY: fmt vet get clean dev setdev .PHONY: fmt vet get clean dev setdev test
all: fmt vet MovieNight MovieNight.exe static/main.wasm all: fmt vet test MovieNight MovieNight.exe static/main.wasm
setdev: setdev:
$(eval export TAGS=-tags "dev") $(eval export TAGS=-tags "dev")
@ -32,3 +32,6 @@ get:
vet: vet:
go vet $(TAGS) ./... go vet $(TAGS) ./...
GOOS=js GOARCH=wasm go vet $(TAGS) ./... GOOS=js GOARCH=wasm go vet $(TAGS) ./...
test:
go test $(TAGS) ./...

View File

@ -10,6 +10,12 @@ import (
"github.com/zorchenhimer/MovieNight/common" "github.com/zorchenhimer/MovieNight/common"
) )
var (
regexSpoiler = regexp.MustCompile(`\|\|(.*?)\|\|`)
spoilerStart = `<span class="spoiler" onclick='$(this).removeClass("spoiler").addClass("spoiler-active")'>`
spoilerEnd = `</span>`
)
type Client struct { type Client struct {
name string // Display name name string // Display name
conn *chatConnection conn *chatConnection
@ -18,6 +24,7 @@ type Client struct {
CmdLevel common.CommandLevel CmdLevel common.CommandLevel
IsColorForced bool IsColorForced bool
IsNameForced bool IsNameForced bool
regexName *regexp.Regexp
} }
//Client has a new message to broadcast //Client has a new message to broadcast
@ -49,6 +56,10 @@ func (cl *Client) NewMsg(data common.ClientData) {
msg = removeDumbSpaces(msg) msg = removeDumbSpaces(msg)
msg = strings.Trim(msg, " ") msg = strings.Trim(msg, " ")
// Add the spoiler tag outside of the command vs message statement
// because the /me command outputs to the messages
msg = addSpoilerTags(msg)
// Don't send zero-length messages // Don't send zero-length messages
if len(msg) == 0 { if len(msg) == 0 {
return return
@ -94,7 +105,11 @@ func (cl *Client) NewMsg(data common.ClientData) {
func (cl *Client) SendChatData(data common.ChatData) error { func (cl *Client) SendChatData(data common.ChatData) error {
// Colorize name on chat messages // Colorize name on chat messages
if data.Type == common.DTChat { if data.Type == common.DTChat {
data = replaceColorizedName(data, cl) var err error
data = cl.replaceColorizedName(data)
if err != nil {
return fmt.Errorf("could not colorize name: %v", err)
}
} }
cd, err := data.ToJSON() cd, err := data.ToJSON()
@ -164,6 +179,24 @@ func (cl *Client) Host() string {
return cl.conn.Host() return cl.conn.Host()
} }
func (cl *Client) setName(s string) error {
regex, err := regexp.Compile(fmt.Sprintf("(%s|@%s)", s, s))
if err != nil {
return fmt.Errorf("could not compile regex: %v", err)
}
cl.name = s
cl.regexName = regex
return nil
}
func (cl *Client) replaceColorizedName(chatData common.ChatData) common.ChatData {
data := chatData.Data.(common.DataMessage)
data.Message = cl.regexName.ReplaceAllString(data.Message, `<span class="mention">$1</span>`)
chatData.Data = data
return chatData
}
var dumbSpaces = []string{ var dumbSpaces = []string{
"\n", "\n",
"\t", "\t",
@ -187,12 +220,6 @@ func removeDumbSpaces(msg string) string {
return newMsg return newMsg
} }
func replaceColorizedName(chatData common.ChatData, client *Client) common.ChatData { func addSpoilerTags(msg string) string {
data := chatData.Data.(common.DataMessage) return regexSpoiler.ReplaceAllString(msg, fmt.Sprintf(`%s$1%s`, spoilerStart, spoilerEnd))
data.Message = regexp.MustCompile(fmt.Sprintf(`(%s|@%s)`, client.name, client.name)).
ReplaceAllString(data.Message, `<span class="mention">$1</span>`)
chatData.Data = data
return chatData
} }

23
chatclient_test.go Normal file
View File

@ -0,0 +1,23 @@
package main
import "testing"
func TestClient_addSpoilerTag(t *testing.T) {
data := [][]string{
{"||||", spoilerStart + spoilerEnd},
{"|||||", spoilerStart + spoilerEnd + "|"},
{"||||||", spoilerStart + spoilerEnd + "||"},
{"|||||||", spoilerStart + spoilerEnd + "|||"},
{"||||||||", spoilerStart + spoilerEnd + spoilerStart + spoilerEnd},
{"||test||", spoilerStart + "test" + spoilerEnd},
{"|| ||", spoilerStart + " " + spoilerEnd},
{"|s|||", "|s|||"},
}
for i := range data {
s := addSpoilerTags(data[i][0])
if s != data[i][1] {
t.Errorf("expected %#v, got %#v with %#v", data[i][1], s, data[i][0])
}
}
}

View File

@ -98,12 +98,16 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) {
conn.clientName = name conn.clientName = name
client := &Client{ client := &Client{
name: name,
conn: conn, conn: conn,
belongsTo: cr, belongsTo: cr,
color: common.RandomColor(), color: common.RandomColor(),
} }
err := client.setName(name)
if err != nil {
return nil, fmt.Errorf("could not set client name to %#v: %v", name, err)
}
host := client.Host() host := client.Host()
if banned, names := settings.IsBanned(host); banned { if banned, names := settings.IsBanned(host); banned {
@ -483,7 +487,10 @@ func (cr *ChatRoom) changeName(oldName, newName string, forced bool) error {
} }
if currentClient != nil { if currentClient != nil {
currentClient.name = newName err := currentClient.setName(newName)
if err != nil {
return fmt.Errorf("could not set client name to %#v: %v", newName, err)
}
common.LogDebugf("%q -> %q\n", oldName, newName) common.LogDebugf("%q -> %q\n", oldName, newName)
if forced { if forced {

View File

@ -42,6 +42,10 @@ var colors = []string{
"whitesmoke", "yellow", "yellowgreen", "whitesmoke", "yellow", "yellowgreen",
} }
var (
regexColor = regexp.MustCompile(`^#([0-9A-Fa-f]{3}){1,2}$`)
)
// IsValidColor takes a string s and compares it against a list of css color names. // IsValidColor takes a string s and compares it against a list of css color names.
// It also accepts hex codes in the form of #000 (RGB), to #00000000 (RRGGBBAA), with A // It also accepts hex codes in the form of #000 (RGB), to #00000000 (RRGGBBAA), with A
// being the alpha value // being the alpha value
@ -53,7 +57,7 @@ func IsValidColor(s string) bool {
} }
} }
if regexp.MustCompile(`^#([0-9A-Fa-f]{3}){1,2}$`).MatchString(s) { if regexColor.MatchString(s) {
c, err := colorful.Hex(s) c, err := colorful.Hex(s)
if err != nil { if err != nil {
return false return false

View File

@ -23,7 +23,6 @@ const (
logPrefixChat string = "[CHAT] " logPrefixChat string = "[CHAT] "
logPrefixInfo string = "[INFO] " logPrefixInfo string = "[INFO] "
logPrefixDebug string = "[DEBUG] " logPrefixDebug string = "[DEBUG] "
logPrefixDev string = "[DEV] "
) )
var ( var (
@ -31,18 +30,8 @@ var (
logChat *log.Logger logChat *log.Logger
logInfo *log.Logger logInfo *log.Logger
logDebug *log.Logger logDebug *log.Logger
logDev *log.Logger
) )
//func ParseLogLevel(input string) LogLevel {
// switch LogLevel(input) {
// case LLError, LLChat, LLInfo, LLDebug:
// return LogLevel(input)
// default:
// return LLError
// }
//}
func SetupLogging(level LogLevel, file string) error { func SetupLogging(level LogLevel, file string) error {
switch level { switch level {
case LLDebug: case LLDebug:

View File

@ -2,18 +2,21 @@
package common package common
func LogDevf(format string, v ...interface{}) { import (
if logError == nil { "log"
panic("Logging not setup!") "os"
} )
logError.Printf(format, v...) var logDev *log.Logger
func init() {
logDev = log.New(os.Stdout, "[DEV]", log.LstdFlags)
}
func LogDevf(format string, v ...interface{}) {
logDev.Printf(format, v...)
} }
func LogDevln(v ...interface{}) { func LogDevln(v ...interface{}) {
if logError == nil { logDev.Println(v...)
panic("Logging not setup!")
}
logError.Println(v...)
} }

View File

@ -49,7 +49,7 @@ func init() {
panic("Missing stream key is settings.json") panic("Missing stream key is settings.json")
} }
if err = common.SetupLogging(settings.LogLevel, settings.LogFile); err != nil { if err = settings.SetupLogging(); err != nil {
panic("Unable to setup logger: " + err.Error()) panic("Unable to setup logger: " + err.Error())
} }
@ -184,3 +184,7 @@ func (s *Settings) GetStreamKey() string {
} }
return s.StreamKey return s.StreamKey
} }
func (s *Settings) SetupLogging() error {
return common.SetupLogging(s.LogLevel, s.LogFile)
}

View File

@ -116,6 +116,22 @@ span.svmsg {
color: var(--var-contrast-color); color: var(--var-contrast-color);
} }
.spoiler {
border-radius: 3px;
padding: 0px 3px;
}
.spoiler *,
.spoiler {
background: var(--var-popout-color);
color: var(--var-popout-color);
}
.spoiler-active {
background: var(--var-background-color);
color: aqua;
}
.range-div { .range-div {
margin-bottom: 5px; margin-bottom: 5px;
display: flex; display: flex;