Implement replying in DMs

This commit is contained in:
Alessio 2024-07-28 17:18:30 -07:00
parent b4384f9cc4
commit c1c781af61
3 changed files with 121 additions and 47 deletions

View File

@ -56,11 +56,17 @@ func (app *Application) message_send(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
panic_if(err)
var message_data struct {
Text string `json:"text"`
Text string `json:"text"`
InReplyToID string `json:"in_reply_to_id"`
}
panic_if(json.Unmarshal(body, &message_data))
trove := scraper.SendDMMessage(room_id, message_data.Text, 0)
in_reply_to_id, err := strconv.Atoi(message_data.InReplyToID)
if err != nil {
in_reply_to_id = 0
}
trove := scraper.SendDMMessage(room_id, message_data.Text, scraper.DMMessageID(in_reply_to_id))
app.Profile.SaveTweetTrove(trove, false)
go app.Profile.SaveTweetTrove(trove, true)
}

View File

@ -1332,7 +1332,7 @@ main {
flex-direction: row-reverse;
}
.dm-message__emoji-button-container {
.dm-message__button-container {
display: flex;
flex-direction: column;
justify-content: center;
@ -1356,30 +1356,7 @@ main {
align-items: flex-end;
}
}
.dm-message__replying-to {
background-color: #f0f0f0f0;
border-radius: 1em 1em 1em 0em;
padding: 0.5em 1.2em 2em 1.2em;
margin: 0 0 -2em 0.2em;
font-size: 0.9em;
.our-message & {
border-radius: 1em 1em 0em 1em;
margin: 0 0.2em -2em;
}
.replying-to-message {
cursor: pointer;
}
}
.dm-message__replying-to-label {
font-size: 0.8em;
color: var(--color-twitter-text-gray);
& img.svg-icon {
width: 1em;
}
}
.dm-message__tweet-preview {
border-radius: 1em;
overflow: hidden; /* TODO: redundancy check-- why is this necessary? check .rounded-gray-outline */
@ -1446,18 +1423,43 @@ main {
}
}
.dm-replying-to {
background-color: #f0f0f0f0;
border-radius: 1em 1em 1em 0em;
padding: 0.5em 1.2em 2em 1.2em;
margin: 0 0 -2em 0.2em;
font-size: 0.9em;
.our-message & {
border-radius: 1em 1em 0em 1em;
margin: 0 0.2em -2em;
}
.dm-replying-to__label {
font-size: 0.8em;
color: var(--color-twitter-text-gray);
& img.svg-icon {
width: 1em;
}
}
.dm-replying-to__preview-text {
cursor: pointer;
}
}
/**
* Messages page DM composer module
*/
.dm-composer {
padding-top: 0.5em;
border-top: 1px solid var(--color-outline-gray);
& form {
display: flex;
gap: 1em;
padding: 0.5em 1em;
padding: 0.8em 1em;
align-items: center;
border-top: 1px solid var(--color-outline-gray);
background-color: white;
}
[role="textbox"] {
flex-grow: 1;
@ -1478,4 +1480,22 @@ main {
height: 3em;
width: 6em;
}
.dm-composer__replying-to-container {
display: none;
&.unhidden {
display: revert;
}
/* This is gross; it's to counter the left margin on `dm-replying-to`, which is meant for the one on DMs specifically */
margin-left: -0.2em;
.dm-composer__replying-to {
position: absolute;
top: 0;
transform: translate(0, -100%);
width: 100%;
}
}
}

View File

@ -8,14 +8,14 @@
</div>
<div class="dm-message__contents">
{{if (ne .InReplyToID 0)}}
<div class="dm-message__replying-to">
<div class="dm-message__replying-to-label labelled-icon">
<div class="dm-replying-to">
<div class="dm-replying-to__label labelled-icon">
<img class="svg-icon" src="/static/icons/replying_to.svg" width="24" height="24" />
<label>Replying to</label>
</div>
<div class="replying-to-message"
<div class="dm-replying-to__preview-text"
data-replying-to-message-id="{{ .InReplyToID }}"
onclick="doReplyTo(this)"
onclick="handleReplyingToClicked(this)"
>
{{(dm_message .InReplyToID).Text}}
</div>
@ -65,16 +65,21 @@
</div>
{{end}}
</div>
<div class="dm-message__emoji-button-container">
<div class="dm-message__emoji-button button" onclick="
show_emoji_picker(function(emoji_info) {
htmx.ajax('POST', '/messages/{{$.DMChatRoomID}}/reacc', {values: {
message_id: '{{$.ID}}',
reacc: emoji_info.unicode,
}, source: '[data-message-id=\'{{$.ID}}\']'});
});
">
<img class="svg-icon" src="/static/icons/emoji-react.svg" width="24" height="24"/>
<div class="dm-message__button-container">
<div class="row">
<div class="dm-message__emoji-button button" onclick="
show_emoji_picker(function(emoji_info) {
htmx.ajax('POST', '/messages/{{$.DMChatRoomID}}/reacc', {values: {
message_id: '{{$.ID}}',
reacc: emoji_info.unicode,
}, source: '[data-message-id=\'{{$.ID}}\']'});
});
">
<img class="svg-icon" src="/static/icons/emoji-react.svg" width="24" height="24"/>
</div>
<div class="dm-message__emoji-button button" onclick="handle_reply_clicked(this.closest('.dm-message').getAttribute('data-message-id'))">
<img class="svg-icon" src="/static/icons/replying_to.svg" width="24" height="24"/>
</div>
</div>
</div>
</div>
@ -166,12 +171,31 @@
</div>
{{if .ActiveRoomID}}
<div class="dm-composer">
{{/* TODO: replying-to CSS re-use is a mess */}}
<div id="composerReplyingTo" class="dm-composer__replying-to-container">
<div class="dm-replying-to row row--spread">
<div>
<div class="dm-replying-to__label labelled-icon">
<img class="svg-icon" src="/static/icons/replying_to.svg" width="24" height="24" />
<label>Replying to</label>
</div>
<div class="dm-replying-to__preview-text"
data-replying-to-message-id=""
onclick="handleReplyingToClicked(this)"
>
Lorem ipsum dolor sit amet
</div>
</div>
<img class="svg-icon button" src="/static/icons/close.svg" width="24" height="24" onclick="cancel_reply()">
</div>
</div>
<form
hx-post="/messages/{{.ActiveRoomID}}/send?latest_timestamp={{.LatestPollingTimestamp}}"
hx-target="#new-messages-poller"
hx-swap="outerHTML scroll:.chat-messages:bottom"
hx-ext="json-enc"
hx-on:htmx:after-request="composer.innerText = ''; realInput.value = ''; "
hx-on:htmx:after-request="composer.innerText = ''; realInput.value = ''; cancel_reply();"
>
<img class="svg-icon button" src="/static/icons/emoji-insert.svg" width="24" height="24" onclick="
var carat = composer.innerText.length;
@ -192,11 +216,14 @@
"/>
<span id="composer" role="textbox" contenteditable oninput="realInput.value = this.innerText"></span>
<input id="realInput" type="hidden" name="text" value="" />
<input id="inReplyToInput" type="hidden" name="in_reply_to_id" value="" />
<input type="submit" />
</form>
</div>
<script>
// Make pasting text work for HTML as well as plain text
/**
* Make pasting text work for HTML as well as plain text
*/
composer.addEventListener("paste", function(e) {
// cancel paste
e.preventDefault();
@ -205,6 +232,27 @@
// insert text manually
document.execCommand("insertHTML", false, text);
});
/**
* Handle "Reply" button
*/
function handle_reply_clicked(reply_to_message_id) {
composerReplyingTo.classList.add("unhidden");
inReplyToInput.value = reply_to_message_id;
console.log(reply_to_message_id);
const message_text_element = document.querySelector('[data-message-id="' + reply_to_message_id + '"] .dm-message__text-content');
const preview_text = message_text_element.innerText;
const preview_element = composerReplyingTo.querySelector('.dm-replying-to__preview-text');
preview_element.innerText = preview_text;
preview_element.setAttribute("data-replying-to-message-id", reply_to_message_id);
}
/**
* Handle cancel-reply button
*/
function cancel_reply() {
composerReplyingTo.classList.remove("unhidden");
inReplyToInput.value = "";
}
</script>
{{ $room := (index $.Rooms $.ActiveRoomID) }}
@ -265,7 +313,7 @@
* Define callback on-click handler for 'replying-to' previews; they should scroll the replied-to
* message into view, if possible.
*/
function doReplyTo(replying_to_box) {
function handleReplyingToClicked(replying_to_box) {
const replied_to_id = replying_to_box.getAttribute("data-replying-to-message-id");
const replied_to_message = document.querySelector('[data-message-id="' + replied_to_id + '"]');