feature: reactions

This commit is contained in:
Andy Burke 2025-10-15 17:50:48 -07:00
parent b8467ec870
commit 7046bb0389
11 changed files with 371 additions and 133 deletions

View file

@ -106,6 +106,13 @@
margin: 1rem 0;
}
.blurb-container button[commandfor="eventactionspopover"] {
position: absolute;
bottom: 0.25rem;
right: 0.25rem;
border: none;
}
.blurb-container .new-blurb-container {
grid-area: newblurb;
display: none;
@ -142,7 +149,7 @@
id="blurbs-list"
data-feed
data-precheck="!!document.body.dataset.user && document.body.dataset.user.indexOf( 'topics.blurbs.read' ) !== -1"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=blurb&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'blurb:able-able-able-able-able-able-able-able-able-able' }"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=blurb,reaction&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'able-able-able-able-able-able-able-able-able-able' }"
data-longpolling="true"
data-reverse="true"
data-insert="prepend"
@ -162,11 +169,31 @@
) ?? feed
);
};
feed.__target_element = (item) => {
let target = feed;
switch (item.type) {
case "reaction":
target = document.querySelector(
`.blurb-container[data-event_id='${item.parent_id}'] > .reactions-container`,
);
break;
case "blurb":
default:
target =
document.querySelector(
`.blurb-container[data-event_id='${item.parent_id}'] > .replies-container`,
) ?? feed;
break;
}
return target;
};
feed.__context = async (item) => {
const blurb_datetime = datetime_to_local(item.timestamps.created);
return {
event: item,
blurb: item,
creator: await USERS.get(item.creator_id),
blurb_datetime
@ -199,10 +226,23 @@
</div>
</div>
<div class="content-container">${htmlify(md_to_html(context.blurb.data.blurb))}</div>
<div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button>
<!-- #include file="./new_blurb.html" -->
<div class="replies-container"></div>
</div>
</template>
<template data-for_type="reaction">
<div
id="${context.event.id}"
class="reaction-container"
data-event_id="${context.event.id}"
data-creator_id="${context.creator.id}"
data-temp_id="${context.event.meta?.temp_id ?? ''}"
>
<span class="reaction">${ context.event.data.reaction }</span>
</div>
</template>
</div>
</div>
</div>

View file

@ -30,14 +30,8 @@
}
#chat #chat-entry-container form {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: row;
padding: 0.75rem;
}
#chat #chat-entry-container form input[type="file"] {
@ -144,16 +138,12 @@
opacity: 0.75;
}
#chat .message-container .message-actions-container {
#chat .message-container button[commandfor] {
position: absolute;
top: 0.1rem;
right: 0.1rem;
display: flex;
flex-direction: row-reverse;
border: 1px solid;
border-color: rgba(0, 0, 0, 0);
border-radius: var(--border-radius);
color: rgb(from var(--text) r g b / 0.7);
border: none;
z-index: 10;
}
@ -237,6 +227,7 @@
#chat .message-container .message-media-container,
#chat .message-container .reactions-container {
padding-left: 8rem;
overflow-x: auto;
}
#chat .message-container .reactions-container:has(> .reaction-container) {
@ -244,15 +235,6 @@
margin-bottom: 0.25rem;
}
#chat .message-container .reactions-container .reaction-container {
display: inline-block;
border: 1px solid var(--border-subtle);
border-radius: var(--border-radius);
margin-right: 0.5rem;
padding: 0.25rem;
font-size: large;
}
#chat .embed-container {
position: relative;
width: 100%;

View file

@ -22,7 +22,7 @@
id="chat-content"
data-feed
data-precheck="!!document.body.dataset.user && document.body.dataset.user.indexOf( 'topics.chat.read' ) !== -1"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=chat,reaction&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'chat:able-able-able-able-able-able-able-able-able-able' }"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=chat,reaction&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'able-able-able-able-able-able-able-able-able-able' }"
data-longpolling="true"
data-reverse="true"
data-insert="append"
@ -107,42 +107,7 @@
data-creator_id="${context.creator.id}"
data-temp_id="${context.event.meta?.temp_id ?? ''}"
>
<div class="message-actions-container">
<input
type="checkbox"
id="show_message_actions-${context.event.id}"
class="message-actions-display-toggle"
/>
<label
class="message-actions-display-toggle-label"
for="show_message_actions-${context.event.id}"
>
<div class="icon more-borderless"></div>
</label>
<button
class="message-action"
data-action="react"
data-reactions
data-smart
>
<i class="icon circle"></i><span class="action-name">React</span>
</button>
<button
class="message-action"
data-action="reply"
onclick="document.getElementById( 'parent-id' ).value = '${context.event.id}';"
>
<i class="icon reply"></i><span class="action-name">Reply</span>
</button>
<button class="message-action mockup" data-action="forward_copy">
<i class="icon forward-copy"></i
><span class="action-name">Copy Link</span>
</button>
<button class="message-action mockup" data-action="delete">
<i class="icon trash"></i><span class="action-name">Delete</span>
</button>
</div>
<button class="icon more" commandfor="eventactionspopover"></button>
<div class="info-container">
<div class="avatar-container inline">
<img

View file

@ -78,6 +78,13 @@
font-size: large;
}
.essay-container button[commandfor="eventactionspopover"] {
position: absolute;
bottom: 0.25rem;
right: 0.25rem;
border: none;
}
.essay-container .new-essay-container {
grid-area: newessay;
display: none;
@ -109,7 +116,7 @@
id="essays-list"
data-feed
data-precheck="!!document.body.dataset.user && document.body.dataset.user.indexOf( 'topics.essays.read' ) !== -1"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=essay&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'essay:able-able-able-able-able-able-able-able-able-able' }"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=essay,reaction&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'able-able-able-able-able-able-able-able-able-able' }"
data-longpolling="true"
data-reverse="true"
data-insert="prepend"
@ -127,17 +134,27 @@
});
feed.__target_element = (item) => {
return (
document.querySelector(
`.essay-container[data-essay_id='${item.parent_id}'] > .replies-container`,
) ?? feed
);
let target = feed;
switch (item.type) {
case "reaction":
target = document.querySelector(
`.essay-container[data-event_id='${item.parent_id}'] > .reactions-container`,
);
break;
case "essay":
default:
target = feed;
break;
}
return target;
};
feed.__context = async (item) => {
const essay_datetime = datetime_to_local(item.timestamps.created);
return {
event: item,
essay: item,
creator: await USERS.get(item.creator_id),
essay_datetime,
@ -180,6 +197,19 @@
<div class="content-container">
${htmlify(md_to_html(context.essay.data.essay))}
</div>
<div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button>
</div>
</template>
<template data-for_type="reaction">
<div
id="${context.event.id}"
class="reaction-container"
data-event_id="${context.event.id}"
data-creator_id="${context.creator.id}"
data-temp_id="${context.event.meta?.temp_id ?? ''}"
>
<span class="reaction">${ context.event.data.reaction }</span>
</div>
</template>
</div>

View file

@ -14,7 +14,7 @@
". . content"
". . newpost"
". . replies";
max-height: 6rem;
max-height: 8rem;
padding: 1rem;
border: 1px solid var(--border-subtle);
overflow-y: hidden;
@ -111,6 +111,13 @@
margin-top: 2rem;
}
.post-container button[commandfor="eventactionspopover"] {
position: absolute;
bottom: 0.25rem;
right: 0.25rem;
border: none;
}
.post-container .new-post-container {
grid-area: newpost;
}
@ -139,7 +146,7 @@
id="posts-list"
data-feed
data-precheck="!!document.body.dataset.user && document.body.dataset.user.indexOf( 'topics.essays.read' ) !== -1"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=post&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'post:able-able-able-able-able-able-able-able-able-able' }"
data-source="/api/topics/${ document.body.dataset.topic }/events?type=post,reaction&limit=100&sort=newest&wait=true&after_id=${ feed.__newest_id ?? 'able-able-able-able-able-able-able-able-able-able' }"
data-longpolling="true"
data-reverse="true"
data-insert="prepend"
@ -157,17 +164,30 @@
});
feed.__target_element = (item) => {
return (
document.querySelector(
`.post-container[data-post_id='${item.parent_id}'] > .replies-container`,
) ?? feed
);
let target = feed;
switch (item.type) {
case "reaction":
target = document.querySelector(
`.post-container[data-event_id='${item.parent_id}'] > .reactions-container`,
);
break;
case "chat":
default:
target =
document.querySelector(
`.post-container[data-event_id='${item.parent_id}'] > .replies-container`,
) ?? feed;
break;
}
return target;
};
feed.__context = async (item) => {
const post_datetime = datetime_to_local(item.timestamps.created);
return {
event: item,
post: item,
creator: await USERS.get(item.creator_id),
post_datetime,
@ -215,10 +235,23 @@
<div class="content-container">
${htmlify(md_to_html(context.post.data.content))}
</div>
<div class="reactions-container"></div>
<button class="icon more" commandfor="eventactionspopover"></button>
<!-- #include file="./new_post.html" -->
<div class="replies-container"></div>
</div>
</template>
<template data-for_type="reaction">
<div
id="${context.event.id}"
class="reaction-container"
data-event_id="${context.event.id}"
data-creator_id="${context.creator.id}"
data-temp_id="${context.event.meta?.temp_id ?? ''}"
>
<span class="reaction">${ context.event.data.reaction }</span>
</div>
</template>
</div>
<!-- #include file="./new_post.html" -->