mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-01-23 17:16:26 +01:00
Experimental reply parent hint
This commit is contained in:
parent
14f5c37721
commit
f3d77dd04e
7 changed files with 847 additions and 700 deletions
|
@ -330,6 +330,62 @@
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status.compact-reply {
|
||||||
|
--avatar-size: 20px;
|
||||||
|
--line-start: 40px;
|
||||||
|
--line-width: 3px;
|
||||||
|
--line-end: calc(var(--line-start) + var(--line-width));
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
--top-padding: 8px;
|
||||||
|
padding-top: var(--top-padding);
|
||||||
|
padding-bottom: 0;
|
||||||
|
background-image: linear-gradient(
|
||||||
|
160deg,
|
||||||
|
var(--reply-to-faded-color),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
|
||||||
|
> * {
|
||||||
|
opacity: 0.65;
|
||||||
|
transition: opacity 1s ease-out;
|
||||||
|
}
|
||||||
|
.status-link:hover & > * {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--top-padding) + var(--avatar-size));
|
||||||
|
left: var(--line-start);
|
||||||
|
width: var(--line-width);
|
||||||
|
height: calc(100% - var(--top-padding) - var(--avatar-size) + 16px);
|
||||||
|
background-color: var(--comment-line-color);
|
||||||
|
z-index: 0;
|
||||||
|
mask-image: linear-gradient(to bottom, #000 8px, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-left: calc((50px - var(--avatar-size)) / 2);
|
||||||
|
justify-self: center;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-compact {
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
font-size: 90%;
|
||||||
|
line-height: var(--avatar-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.status .container {
|
.status .container {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
|
@ -126,6 +126,7 @@ function Status({
|
||||||
showFollowedTags,
|
showFollowedTags,
|
||||||
allowContextMenu,
|
allowContextMenu,
|
||||||
showActionsBar,
|
showActionsBar,
|
||||||
|
showReplyParent,
|
||||||
}) {
|
}) {
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
return (
|
return (
|
||||||
|
@ -1271,6 +1272,10 @@ function Status({
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{showReplyParent && !!(inReplyToId && inReplyToAccount) && (
|
||||||
|
<StatusCompact sKey={sKey} />
|
||||||
|
)}
|
||||||
<article
|
<article
|
||||||
data-state-post-id={sKey}
|
data-state-post-id={sKey}
|
||||||
ref={(node) => {
|
ref={(node) => {
|
||||||
|
@ -1670,7 +1675,11 @@ function Status({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!!content && (
|
{!!content && (
|
||||||
<div class="content" ref={contentRef} data-read-more={readMoreText}>
|
<div
|
||||||
|
class="content"
|
||||||
|
ref={contentRef}
|
||||||
|
data-read-more={readMoreText}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
lang={language}
|
lang={language}
|
||||||
dir="auto"
|
dir="auto"
|
||||||
|
@ -1814,7 +1823,9 @@ function Status({
|
||||||
showCaption={mediaAttachments.length === 1}
|
showCaption={mediaAttachments.length === 1}
|
||||||
lang={language}
|
lang={language}
|
||||||
altIndex={
|
altIndex={
|
||||||
showMultipleMediaCaptions && !!media.description && i + 1
|
showMultipleMediaCaptions &&
|
||||||
|
!!media.description &&
|
||||||
|
i + 1
|
||||||
}
|
}
|
||||||
to={`/${instance}/s/${id}?${
|
to={`/${instance}/s/${id}?${
|
||||||
withinContext ? 'media' : 'media-only'
|
withinContext ? 'media' : 'media-only'
|
||||||
|
@ -1920,7 +1931,9 @@ function Status({
|
||||||
confirmLabel={
|
confirmLabel={
|
||||||
<>
|
<>
|
||||||
<Icon icon="rocket" />
|
<Icon icon="rocket" />
|
||||||
<span>{reblogged ? 'Unboost?' : 'Boost to everyone?'}</span>
|
<span>
|
||||||
|
{reblogged ? 'Unboost?' : 'Boost to everyone?'}
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
menuFooter={
|
menuFooter={
|
||||||
|
@ -2018,6 +2031,7 @@ function Status({
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
</article>
|
</article>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2412,6 +2426,41 @@ function nicePostURL(url) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function StatusCompact({ sKey }) {
|
||||||
|
const snapStates = useSnapshot(states);
|
||||||
|
const statusReply = snapStates.statusReply[sKey];
|
||||||
|
if (!statusReply) return null;
|
||||||
|
|
||||||
|
const { id, instance } = statusReply;
|
||||||
|
const status = getStatus(id, instance);
|
||||||
|
if (!status) return null;
|
||||||
|
|
||||||
|
const {
|
||||||
|
sensitive,
|
||||||
|
spoilerText,
|
||||||
|
account: { avatar, avatarStatic, bot },
|
||||||
|
visibility,
|
||||||
|
content,
|
||||||
|
} = status;
|
||||||
|
if (sensitive || spoilerText) return null;
|
||||||
|
if (!content) return null;
|
||||||
|
|
||||||
|
const statusPeekText = statusPeek(status);
|
||||||
|
return (
|
||||||
|
<article
|
||||||
|
class={`status compact-reply ${
|
||||||
|
visibility === 'direct' ? 'visibility-direct' : ''
|
||||||
|
}`}
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<Avatar url={avatarStatic || avatar} squircle={bot} />
|
||||||
|
<div class="content-compact" title={statusPeekText}>
|
||||||
|
{statusPeekText}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function FilteredStatus({
|
function FilteredStatus({
|
||||||
status,
|
status,
|
||||||
filterInfo,
|
filterInfo,
|
||||||
|
|
|
@ -46,6 +46,7 @@ function Timeline({
|
||||||
view,
|
view,
|
||||||
filterContext,
|
filterContext,
|
||||||
showFollowedTags,
|
showFollowedTags,
|
||||||
|
showReplyParent,
|
||||||
}) {
|
}) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState([]);
|
||||||
|
@ -84,7 +85,7 @@ function Timeline({
|
||||||
if (boostsCarousel) {
|
if (boostsCarousel) {
|
||||||
value = groupBoosts(value);
|
value = groupBoosts(value);
|
||||||
}
|
}
|
||||||
value = groupContext(value);
|
value = groupContext(value, instance);
|
||||||
}
|
}
|
||||||
if (pinnedPosts.length) {
|
if (pinnedPosts.length) {
|
||||||
value = pinnedPosts.concat(value);
|
value = pinnedPosts.concat(value);
|
||||||
|
@ -522,6 +523,7 @@ function TimelineItem({
|
||||||
filterContext,
|
filterContext,
|
||||||
view,
|
view,
|
||||||
showFollowedTags,
|
showFollowedTags,
|
||||||
|
showReplyParent,
|
||||||
}) {
|
}) {
|
||||||
const { id: statusID, reblog, items, type, _pinned } = status;
|
const { id: statusID, reblog, items, type, _pinned } = status;
|
||||||
if (_pinned) useItemID = false;
|
if (_pinned) useItemID = false;
|
||||||
|
@ -680,6 +682,7 @@ function TimelineItem({
|
||||||
instance={instance}
|
instance={instance}
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
|
showReplyParent
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -688,6 +691,7 @@ function TimelineItem({
|
||||||
instance={instance}
|
instance={instance}
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
|
showReplyParent
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -129,6 +129,7 @@ function Following({ title, path, id, ...props }) {
|
||||||
// allowFilters
|
// allowFilters
|
||||||
filterContext="home"
|
filterContext="home"
|
||||||
showFollowedTags
|
showFollowedTags
|
||||||
|
showReplyParent
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ function List(props) {
|
||||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||||
// allowFilters
|
// allowFilters
|
||||||
filterContext="home"
|
filterContext="home"
|
||||||
|
showReplyParent
|
||||||
// refresh={reloadCount}
|
// refresh={reloadCount}
|
||||||
headerStart={
|
headerStart={
|
||||||
<Link to="/l" class="button plain">
|
<Link to="/l" class="button plain">
|
||||||
|
|
|
@ -37,6 +37,7 @@ const states = proxy({
|
||||||
unfurledLinks: {},
|
unfurledLinks: {},
|
||||||
statusQuotes: {},
|
statusQuotes: {},
|
||||||
statusFollowedTags: {},
|
statusFollowedTags: {},
|
||||||
|
statusReply: {},
|
||||||
accounts: {},
|
accounts: {},
|
||||||
routeNotification: null,
|
routeNotification: null,
|
||||||
// Modals
|
// Modals
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { api } from './api';
|
||||||
import { extractTagsFromStatus, getFollowedTags } from './followed-tags';
|
import { extractTagsFromStatus, getFollowedTags } from './followed-tags';
|
||||||
|
import pmem from './pmem';
|
||||||
import { fetchRelationships } from './relationships';
|
import { fetchRelationships } from './relationships';
|
||||||
import states, { statusKey } from './states';
|
import states, { saveStatus, statusKey } from './states';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
|
||||||
export function groupBoosts(values) {
|
export function groupBoosts(values) {
|
||||||
|
@ -81,7 +83,7 @@ export function dedupeBoosts(items, instance) {
|
||||||
return filteredItems;
|
return filteredItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function groupContext(items) {
|
export function groupContext(items, instance) {
|
||||||
const contexts = [];
|
const contexts = [];
|
||||||
let contextIndex = 0;
|
let contextIndex = 0;
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
|
@ -173,12 +175,45 @@ export function groupContext(items) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.inReplyToId && item.inReplyToAccountId !== item.account.id) {
|
||||||
|
const sKey = statusKey(item.id, instance);
|
||||||
|
if (states.statusReply[sKey]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If it's a reply and not a thread
|
||||||
|
queueMicrotask(async () => {
|
||||||
|
try {
|
||||||
|
const { masto } = api({ instance });
|
||||||
|
// const replyToStatus = await masto.v1.statuses
|
||||||
|
// .$select(item.inReplyToId)
|
||||||
|
// .fetch();
|
||||||
|
const replyToStatus = await fetchStatus(item.inReplyToId, masto);
|
||||||
|
saveStatus(replyToStatus, instance, {
|
||||||
|
skipThreading: true,
|
||||||
|
skipUnfurling: true,
|
||||||
|
});
|
||||||
|
states.statusReply[sKey] = {
|
||||||
|
id: replyToStatus.id,
|
||||||
|
instance,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
// Silently fail
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
newItems.push(item);
|
newItems.push(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
return newItems;
|
return newItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchStatus = pmem((statusID, masto) => {
|
||||||
|
return masto.v1.statuses.$select(statusID).fetch();
|
||||||
|
});
|
||||||
|
|
||||||
export async function assignFollowedTags(items, instance) {
|
export async function assignFollowedTags(items, instance) {
|
||||||
const followedTags = await getFollowedTags(); // [{name: 'tag'}, {...}]
|
const followedTags = await getFollowedTags(); // [{name: 'tag'}, {...}]
|
||||||
if (!followedTags.length) return;
|
if (!followedTags.length) return;
|
||||||
|
|
Loading…
Reference in a new issue