Rework how hiding messages works. (#1509)
* Rework how hiding messages work. Fixes #1350 * Remove unused function * Revert to old event name to support previously saved webhooks
This commit is contained in:
parent
2278fec70a
commit
b43c5e674e
@ -10,8 +10,8 @@ const (
|
||||
UserJoined EventType = "USER_JOINED"
|
||||
// UserNameChanged is the event sent when a chat username change takes place.
|
||||
UserNameChanged EventType = "NAME_CHANGE"
|
||||
// VisibiltyToggled is the event sent when a chat message's visibility changes.
|
||||
VisibiltyToggled EventType = "VISIBILITY-UPDATE"
|
||||
// VisibiltyUpdate is the event sent when a chat message's visibility changes.
|
||||
VisibiltyUpdate EventType = "VISIBILITY-UPDATE"
|
||||
// PING is a ping message.
|
||||
PING EventType = "PING"
|
||||
// PONG is a pong message.
|
||||
|
21
core/chat/events/setMessageVisibilityEvent.go
Normal file
21
core/chat/events/setMessageVisibilityEvent.go
Normal file
@ -0,0 +1,21 @@
|
||||
package events
|
||||
|
||||
// SetMessageVisibilityEvent is the event fired when one or more message
|
||||
// visibilities are changed.
|
||||
type SetMessageVisibilityEvent struct {
|
||||
Event
|
||||
UserMessageEvent
|
||||
MessageIDs []string
|
||||
Visible bool
|
||||
}
|
||||
|
||||
// GetBroadcastPayload will return the object to send to all chat users.
|
||||
func (e *SetMessageVisibilityEvent) GetBroadcastPayload() EventPayload {
|
||||
return EventPayload{
|
||||
"type": VisibiltyUpdate,
|
||||
"id": e.ID,
|
||||
"timestamp": e.Timestamp,
|
||||
"ids": e.MessageIDs,
|
||||
"visible": e.Visible,
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/owncast/owncast/core/chat/events"
|
||||
"github.com/owncast/owncast/core/webhooks"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -14,23 +16,26 @@ func SetMessagesVisibility(messageIDs []string, visibility bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send an update event to all clients for each message.
|
||||
// Note: Our client expects a single message at a time, so we can't just
|
||||
// send an array of messages in a single update.
|
||||
for _, id := range messageIDs {
|
||||
message, err := getMessageByID(id)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
continue
|
||||
}
|
||||
payload := message.GetBroadcastPayload()
|
||||
payload["type"] = events.VisibiltyToggled
|
||||
if err := _server.Broadcast(payload); err != nil {
|
||||
log.Debugln(err)
|
||||
}
|
||||
|
||||
go webhooks.SendChatEvent(message)
|
||||
// Send an event letting the chat clients know to hide or show
|
||||
// the messages.
|
||||
event := events.SetMessageVisibilityEvent{
|
||||
MessageIDs: messageIDs,
|
||||
Visible: visibility,
|
||||
}
|
||||
event.Event.SetDefaults()
|
||||
|
||||
payload := event.GetBroadcastPayload()
|
||||
if err := _server.Broadcast(payload); err != nil {
|
||||
return errors.New("error broadcasting message visibility payload " + err.Error())
|
||||
}
|
||||
|
||||
// Send webhook
|
||||
wh := webhooks.WebhookEvent{
|
||||
EventData: event,
|
||||
Type: event.GetMessageType(),
|
||||
}
|
||||
|
||||
webhooks.SendEventToWebhooks(wh)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func GetChatModerationHistory() []events.UserMessageEvent {
|
||||
}
|
||||
|
||||
// Get all messages regardless of visibility
|
||||
var query = "SELECT messages.id, user_id, body, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at FROM messages INNER JOIN users ON messages.user_id = users.id ORDER BY timestamp DESC"
|
||||
query := "SELECT messages.id, user_id, body, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at FROM messages INNER JOIN users ON messages.user_id = users.id ORDER BY timestamp DESC"
|
||||
result := getChat(query)
|
||||
|
||||
_historyCache = &result
|
||||
@ -172,7 +172,7 @@ func GetChatModerationHistory() []events.UserMessageEvent {
|
||||
// GetChatHistory will return all the chat messages suitable for returning as user-facing chat history.
|
||||
func GetChatHistory() []events.UserMessageEvent {
|
||||
// Get all visible messages
|
||||
var query = fmt.Sprintf("SELECT messages.id, user_id, body, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at FROM messages, users WHERE messages.user_id = users.id AND hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT %d", maxBacklogNumber)
|
||||
query := fmt.Sprintf("SELECT messages.id, user_id, body, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at FROM messages, users WHERE messages.user_id = users.id AND hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT %d", maxBacklogNumber)
|
||||
m := getChat(query)
|
||||
|
||||
// Invert order of messages
|
||||
@ -221,7 +221,6 @@ func saveMessageVisibility(messageIDs []string, visible bool) error {
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare("UPDATE messages SET hidden_at=? WHERE id IN (?" + strings.Repeat(",?", len(messageIDs)-1) + ")")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -252,41 +251,6 @@ func saveMessageVisibility(messageIDs []string, visible bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMessageByID(messageID string) (*events.UserMessageEvent, error) {
|
||||
var query = "SELECT * FROM messages WHERE id = ?"
|
||||
row := _datastore.DB.QueryRow(query, messageID)
|
||||
|
||||
var id string
|
||||
var userID string
|
||||
var body string
|
||||
var eventType models.EventType
|
||||
var hiddenAt *time.Time
|
||||
var timestamp time.Time
|
||||
|
||||
err := row.Scan(&id, &userID, &body, &eventType, &hiddenAt, ×tamp)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := user.GetUserByID(userID)
|
||||
|
||||
return &events.UserMessageEvent{
|
||||
events.Event{
|
||||
Type: eventType,
|
||||
ID: id,
|
||||
Timestamp: timestamp,
|
||||
},
|
||||
events.UserEvent{
|
||||
User: user,
|
||||
HiddenAt: hiddenAt,
|
||||
},
|
||||
events.MessageEvent{
|
||||
Body: body,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Only keep recent messages so we don't keep more chat data than needed
|
||||
// for privacy and efficiency reasons.
|
||||
func runPruner() {
|
||||
|
@ -29,6 +29,7 @@ export default class Chat extends Component {
|
||||
this.receivedFirstMessages = false;
|
||||
this.receivedMessageUpdate = false;
|
||||
this.hasFetchedHistory = false;
|
||||
this.forceRender = false;
|
||||
|
||||
this.windowBlurred = false;
|
||||
this.numMessagesSinceBlur = 0;
|
||||
@ -69,6 +70,11 @@ export default class Chat extends Component {
|
||||
|
||||
const { webSocketConnected, messages, chatUserNames, newMessagesReceived } =
|
||||
this.state;
|
||||
|
||||
if (this.forceRender) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const {
|
||||
webSocketConnected: nextSocket,
|
||||
messages: nextMessages,
|
||||
@ -185,42 +191,51 @@ export default class Chat extends Component {
|
||||
(item) => item.id === messageId
|
||||
);
|
||||
|
||||
// If the message already exists and this is an update event
|
||||
// then update it.
|
||||
const updatedMessageList = [...curMessages];
|
||||
|
||||
// Change the visibility of messages by ID.
|
||||
if (messageType === 'VISIBILITY-UPDATE') {
|
||||
const updatedMessageList = [...curMessages];
|
||||
const idsToUpdate = message.ids;
|
||||
const visible = message.visible;
|
||||
|
||||
updatedMessageList.forEach((item) => {
|
||||
if (idsToUpdate.includes(item.id)) {
|
||||
item.visible = visible;
|
||||
}
|
||||
|
||||
this.forceRender = true;
|
||||
this.setState({
|
||||
messages: updatedMessageList,
|
||||
});
|
||||
});
|
||||
return;
|
||||
} else if (existingIndex === -1 && messageVisible) {
|
||||
const convertedMessage = {
|
||||
...message,
|
||||
type: 'CHAT',
|
||||
};
|
||||
// if message exists and should now hide, take it out.
|
||||
if (existingIndex >= 0 && !messageVisible) {
|
||||
this.setState({
|
||||
messages: curMessages.filter((item) => item.id !== messageId),
|
||||
});
|
||||
} else if (existingIndex === -1 && messageVisible) {
|
||||
// insert message at timestamp
|
||||
const insertAtIndex = curMessages.findIndex((item, index) => {
|
||||
const time = item.timestamp || messageTimestamp;
|
||||
const nextMessage =
|
||||
index < curMessages.length - 1 && curMessages[index + 1];
|
||||
const nextTime = nextMessage.timestamp || messageTimestamp;
|
||||
const messageTimestampDate = new Date(messageTimestamp);
|
||||
return (
|
||||
messageTimestampDate > new Date(time) &&
|
||||
messageTimestampDate <= new Date(nextTime)
|
||||
);
|
||||
});
|
||||
updatedMessageList.splice(insertAtIndex + 1, 0, convertedMessage);
|
||||
if (updatedMessageList.length > 300) {
|
||||
updatedMessageList = updatedMessageList.slice(
|
||||
Math.max(updatedMessageList.length - 300, 0)
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
messages: updatedMessageList,
|
||||
});
|
||||
|
||||
// insert message at timestamp
|
||||
const insertAtIndex = curMessages.findIndex((item, index) => {
|
||||
const time = item.timestamp || messageTimestamp;
|
||||
const nextMessage =
|
||||
index < curMessages.length - 1 && curMessages[index + 1];
|
||||
const nextTime = nextMessage.timestamp || messageTimestamp;
|
||||
const messageTimestampDate = new Date(messageTimestamp);
|
||||
return (
|
||||
messageTimestampDate > new Date(time) &&
|
||||
messageTimestampDate <= new Date(nextTime)
|
||||
);
|
||||
});
|
||||
updatedMessageList.splice(insertAtIndex + 1, 0, convertedMessage);
|
||||
if (updatedMessageList.length > 300) {
|
||||
updatedMessageList = updatedMessageList.slice(
|
||||
Math.max(updatedMessageList.length - 300, 0)
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
messages: updatedMessageList,
|
||||
});
|
||||
} else if (existingIndex === -1) {
|
||||
// else if message doesn't exist, add it and extra username
|
||||
const newState = {
|
||||
@ -354,6 +369,8 @@ export default class Chat extends Component {
|
||||
const { username, readonly, chatInputEnabled, inputMaxBytes } = props;
|
||||
const { messages, chatUserNames, webSocketConnected } = state;
|
||||
|
||||
this.forceRender = false;
|
||||
|
||||
const messageList = messages
|
||||
.filter((message) => message.visible !== false)
|
||||
.map(
|
||||
|
Loading…
x
Reference in New Issue
Block a user