Implement replying in DMs
This commit is contained in:
parent
b4384f9cc4
commit
c1c781af61
@ -56,11 +56,17 @@ func (app *Application) message_send(w http.ResponseWriter, r *http.Request) {
|
|||||||
body, err := io.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
panic_if(err)
|
panic_if(err)
|
||||||
var message_data struct {
|
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))
|
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)
|
app.Profile.SaveTweetTrove(trove, false)
|
||||||
go app.Profile.SaveTweetTrove(trove, true)
|
go app.Profile.SaveTweetTrove(trove, true)
|
||||||
}
|
}
|
||||||
|
@ -1332,7 +1332,7 @@ main {
|
|||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dm-message__emoji-button-container {
|
.dm-message__button-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -1356,30 +1356,7 @@ main {
|
|||||||
align-items: flex-end;
|
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 {
|
.dm-message__tweet-preview {
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
overflow: hidden; /* TODO: redundancy check-- why is this necessary? check .rounded-gray-outline */
|
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
|
* Messages page DM composer module
|
||||||
*/
|
*/
|
||||||
.dm-composer {
|
.dm-composer {
|
||||||
padding-top: 0.5em;
|
|
||||||
border-top: 1px solid var(--color-outline-gray);
|
|
||||||
|
|
||||||
& form {
|
& form {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
padding: 0.5em 1em;
|
padding: 0.8em 1em;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border-top: 1px solid var(--color-outline-gray);
|
||||||
|
background-color: white;
|
||||||
}
|
}
|
||||||
[role="textbox"] {
|
[role="textbox"] {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -1478,4 +1480,22 @@ main {
|
|||||||
height: 3em;
|
height: 3em;
|
||||||
width: 6em;
|
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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dm-message__contents">
|
<div class="dm-message__contents">
|
||||||
{{if (ne .InReplyToID 0)}}
|
{{if (ne .InReplyToID 0)}}
|
||||||
<div class="dm-message__replying-to">
|
<div class="dm-replying-to">
|
||||||
<div class="dm-message__replying-to-label labelled-icon">
|
<div class="dm-replying-to__label labelled-icon">
|
||||||
<img class="svg-icon" src="/static/icons/replying_to.svg" width="24" height="24" />
|
<img class="svg-icon" src="/static/icons/replying_to.svg" width="24" height="24" />
|
||||||
<label>Replying to</label>
|
<label>Replying to</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="replying-to-message"
|
<div class="dm-replying-to__preview-text"
|
||||||
data-replying-to-message-id="{{ .InReplyToID }}"
|
data-replying-to-message-id="{{ .InReplyToID }}"
|
||||||
onclick="doReplyTo(this)"
|
onclick="handleReplyingToClicked(this)"
|
||||||
>
|
>
|
||||||
{{(dm_message .InReplyToID).Text}}
|
{{(dm_message .InReplyToID).Text}}
|
||||||
</div>
|
</div>
|
||||||
@ -65,16 +65,21 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="dm-message__emoji-button-container">
|
<div class="dm-message__button-container">
|
||||||
<div class="dm-message__emoji-button button" onclick="
|
<div class="row">
|
||||||
show_emoji_picker(function(emoji_info) {
|
<div class="dm-message__emoji-button button" onclick="
|
||||||
htmx.ajax('POST', '/messages/{{$.DMChatRoomID}}/reacc', {values: {
|
show_emoji_picker(function(emoji_info) {
|
||||||
message_id: '{{$.ID}}',
|
htmx.ajax('POST', '/messages/{{$.DMChatRoomID}}/reacc', {values: {
|
||||||
reacc: emoji_info.unicode,
|
message_id: '{{$.ID}}',
|
||||||
}, source: '[data-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"/>
|
">
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -166,12 +171,31 @@
|
|||||||
</div>
|
</div>
|
||||||
{{if .ActiveRoomID}}
|
{{if .ActiveRoomID}}
|
||||||
<div class="dm-composer">
|
<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
|
<form
|
||||||
hx-post="/messages/{{.ActiveRoomID}}/send?latest_timestamp={{.LatestPollingTimestamp}}"
|
hx-post="/messages/{{.ActiveRoomID}}/send?latest_timestamp={{.LatestPollingTimestamp}}"
|
||||||
hx-target="#new-messages-poller"
|
hx-target="#new-messages-poller"
|
||||||
hx-swap="outerHTML scroll:.chat-messages:bottom"
|
hx-swap="outerHTML scroll:.chat-messages:bottom"
|
||||||
hx-ext="json-enc"
|
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="
|
<img class="svg-icon button" src="/static/icons/emoji-insert.svg" width="24" height="24" onclick="
|
||||||
var carat = composer.innerText.length;
|
var carat = composer.innerText.length;
|
||||||
@ -192,11 +216,14 @@
|
|||||||
"/>
|
"/>
|
||||||
<span id="composer" role="textbox" contenteditable oninput="realInput.value = this.innerText"></span>
|
<span id="composer" role="textbox" contenteditable oninput="realInput.value = this.innerText"></span>
|
||||||
<input id="realInput" type="hidden" name="text" value="" />
|
<input id="realInput" type="hidden" name="text" value="" />
|
||||||
|
<input id="inReplyToInput" type="hidden" name="in_reply_to_id" value="" />
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<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) {
|
composer.addEventListener("paste", function(e) {
|
||||||
// cancel paste
|
// cancel paste
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -205,6 +232,27 @@
|
|||||||
// insert text manually
|
// insert text manually
|
||||||
document.execCommand("insertHTML", false, text);
|
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>
|
</script>
|
||||||
|
|
||||||
{{ $room := (index $.Rooms $.ActiveRoomID) }}
|
{{ $room := (index $.Rooms $.ActiveRoomID) }}
|
||||||
@ -265,7 +313,7 @@
|
|||||||
* Define callback on-click handler for 'replying-to' previews; they should scroll the replied-to
|
* Define callback on-click handler for 'replying-to' previews; they should scroll the replied-to
|
||||||
* message into view, if possible.
|
* 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_id = replying_to_box.getAttribute("data-replying-to-message-id");
|
||||||
const replied_to_message = document.querySelector('[data-message-id="' + replied_to_id + '"]');
|
const replied_to_message = document.querySelector('[data-message-id="' + replied_to_id + '"]');
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user