From d3f89d768bcddab4d99b722dbf75b5df6dd2650d Mon Sep 17 00:00:00 2001 From: Steve Phillips Date: Thu, 17 Dec 2020 23:25:42 -0800 Subject: [PATCH] Added "Delete All Messages Forever Now" button! :tada: Fixes #10 Fixes #203 --- messages.go | 21 +++++++++- pg_types.go | 11 ++++++ room.go | 61 ++++++++++++++++++++++++++++++ src/components/chat/MessageForm.js | 19 +++++++++- src/epics/chatEpics.js | 2 +- src/epics/helpers/ChatHandler.js | 14 +++++++ src/static/sass/_layout.scss | 30 +++++++++++---- 7 files changed, 147 insertions(+), 11 deletions(-) diff --git a/messages.go b/messages.go index aeede8d..2000860 100644 --- a/messages.go +++ b/messages.go @@ -12,11 +12,17 @@ import ( type Message []byte type OutgoingPayload struct { - Ephemeral []Message `json:"ephemeral"` + Ephemeral []Message `json:"ephemeral"` + FromServer FromServer `json:"from_server,omitempty"` +} + +type FromServer struct { + AllMessagesDeleted bool `json:"all_messages_deleted,omitempty"` } type ToServer struct { - TTL *int `json:"ttl_secs"` + TTL *int `json:"ttl_secs"` + DeleteAllMessages bool `json:"delete_all_messages"` } type IncomingPayload struct { @@ -75,6 +81,17 @@ func messageReader(room *Room, client *Client) { continue } + if payload.ToServer.DeleteAllMessages { + err = room.DeleteAllMessages() + if err != nil { + room.BroadcastMessages(client, Message(err.Error())) + log.Errorf("Error deleting all messages from room %s -- %s", room.ID, err) + continue + } + room.BroadcastDeleteSignal() + continue + } + err = room.AddMessages(payload.Ephemeral, payload.ToServer.TTL) if err != nil { log.Debugf("Error from AddMessages: %v", err) diff --git a/pg_types.go b/pg_types.go index 5e42019..83b9bd7 100644 --- a/pg_types.go +++ b/pg_types.go @@ -77,6 +77,17 @@ func (cl *PGClient) Get(urlSuffix string) (*http.Response, error) { return http.DefaultClient.Do(req) } +func (cl *PGClient) Delete(urlSuffix string) (*http.Response, error) { + log.Debugf("DELETE'ing from %s", urlSuffix) + + req, _ := http.NewRequest("DELETE", cl.BaseURL+urlSuffix, nil) + // req.Header.Add("Prefer", "return=representation") + req.Header.Add("Prefer", "return=none") + // req.Header.Add("Content-Type", "application/json") + + return http.DefaultClient.Do(req) +} + func (cl *PGClient) GetInto(urlSuffix string, respobj interface{}) error { resp, err := cl.Get(urlSuffix) if err != nil { diff --git a/room.go b/room.go index ba1bfb1..5186388 100644 --- a/room.go +++ b/room.go @@ -4,6 +4,8 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" + "fmt" + "io/ioutil" "net/http" "sync" "time" @@ -180,6 +182,39 @@ func (r *Room) BroadcastMessages(sender *Client, msgs ...Message) { } } +func (r *Room) DeleteAllMessages() error { + resp, err := r.pgClient.Delete("/messages?room_id=eq." + r.ID) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + if len(body) != 0 { + return fmt.Errorf("Error deleting messages: `%s`", body) + } + + return nil +} + +func (r *Room) BroadcastDeleteSignal() { + r.clientLock.RLock() + defer r.clientLock.RUnlock() + + for _, client := range r.Clients { + go func(client *Client) { + err := client.SendDeleteSignal() + if err != nil { + log.Debugf("Error sending message. Err: %s", err) + } + }(client) + } +} + type Client struct { wsConn *websocket.Conn writeLock sync.Mutex @@ -208,6 +243,32 @@ func (c *Client) SendMessages(msgs ...Message) error { return nil } +func (c *Client) SendDeleteSignal() error { + c.writeLock.Lock() + defer c.writeLock.Unlock() + + outgoing := OutgoingPayload{ + Ephemeral: []Message{}, + FromServer: FromServer{ + AllMessagesDeleted: true, + }, + } + + body, err := json.Marshal(outgoing) + if err != nil { + return err + } + + err = c.wsConn.WriteMessage(websocket.TextMessage, body) + if err != nil { + log.Debugf("Error sending message to client. Removing client from room. Err: %s", err) + c.room.RemoveClient(c) + return err + } + + return nil +} + func (c *Client) SendError(errStr string, secretErr error) error { c.writeLock.Lock() defer c.writeLock.Unlock() diff --git a/src/components/chat/MessageForm.js b/src/components/chat/MessageForm.js index 3a8a29c..2d0e623 100644 --- a/src/components/chat/MessageForm.js +++ b/src/components/chat/MessageForm.js @@ -20,6 +20,8 @@ import { addSuggestion } from '../../actions/chatActions'; +import { chatHandler } from '../../epics/chatEpics'; + class MessageForm extends Component { constructor(props) { super(props); @@ -130,6 +132,10 @@ class MessageForm extends Component { this.messageInput = input; } + onDeleteAllMsgs = (e) => { + chatHandler.sendDeleteAllMessagesSignalToServer(); + } + render() { const { message, showEmojiPicker } = this.props.chat; const { messageUpdate, togglePicker } = this.props; @@ -154,7 +160,18 @@ class MessageForm extends Component {
+ onClick={togglePicker} + /> + +
+ +
diff --git a/src/epics/chatEpics.js b/src/epics/chatEpics.js index 5736915..33b427c 100644 --- a/src/epics/chatEpics.js +++ b/src/epics/chatEpics.js @@ -35,7 +35,7 @@ import createDetectVisibilityObservable from './helpers/createDetectPageVisibili const authUrl = `${window.location.origin}/api/login`; const wsUrl = `${window.location.origin.replace('http', 'ws')}/api/ws/messages/all`; -const chatHandler = new ChatHandler(wsUrl); +export const chatHandler = new ChatHandler(wsUrl); import { Observable } from 'rxjs/Observable'; import { ajax } from 'rxjs/observable/dom/ajax'; diff --git a/src/epics/helpers/ChatHandler.js b/src/epics/helpers/ChatHandler.js index 9ad49d3..5f7ef9a 100644 --- a/src/epics/helpers/ChatHandler.js +++ b/src/epics/helpers/ChatHandler.js @@ -44,6 +44,11 @@ class ChatHandler { .catch(error => console.error('An error occurred in ChatHandler', error)) .subscribe(); } + if (data && data.from_server) { + if (data.from_server.all_messages_deleted) { + alert("All messages deleted from server! (Refresh this page to remove them from this browser tab.)"); + } + } } resolveIncomingMessageTypeObservable = (message) => { @@ -144,6 +149,15 @@ class ChatHandler { this.send({ tags, ttl }); } + sendDeleteAllMessagesSignalToServer = () => { + const msgForServer = { + to_server: { + delete_all_messages: true + } + } + this.ws.send(JSON.stringify(msgForServer)); + } + sendMessageToServer = (ttl_secs, fileBlob, saveName, senderMinilockID) => { const reader = new FileReader(); reader.addEventListener("loadend", () => { diff --git a/src/static/sass/_layout.scss b/src/static/sass/_layout.scss index 4803642..09cac68 100644 --- a/src/static/sass/_layout.scss +++ b/src/static/sass/_layout.scss @@ -153,14 +153,7 @@ header { flex-grow: 2; border: none; } - .chat-icons { - display: flex; - flex-direction: row; - margin-bottom: 5px; - } .emoji-picker-icon { - float: left; - margin-left: 8px; cursor: pointer; } } @@ -341,3 +334,26 @@ textarea { max-height: 40vh !important; width: 100% !important; } + +// All widths +.chat-icons { + display: flex; + flex-direction: row; + margin-bottom: 5px; + width: 100%; +} +.chat-icons .right-chat-icons { + display: flex; + flex-direction: row; + height: 100%; + margin-left: auto; +} +.right-chat-icons .delete-all-msgs { + display: flex; + flex-direction: row; + background-color: #c00; + color: white; +} +.delete-all-msgs:hover { + background-color: #e00; +}