mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-25 01:08:50 +01:00
Early implementation of media-first UI experience
This commit is contained in:
parent
6e73728e2b
commit
a0d2037007
5 changed files with 682 additions and 230 deletions
28
src/app.css
28
src/app.css
|
@ -301,6 +301,34 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.deck-container-media-first {
|
||||||
|
.timeline {
|
||||||
|
> li:not(.timeline-item-carousel, .timeline-item-container) {
|
||||||
|
&:has(.status-media-first) {
|
||||||
|
width: fit-content;
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: 0 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
max-width: min(480px, 100%);
|
||||||
|
margin-inline: auto !important;
|
||||||
|
|
||||||
|
&:has(.skeleton) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(.media[data-orientation='landscape']) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-link:has(.status-media-first):hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.timeline.grow {
|
.timeline.grow {
|
||||||
/* min-height: 100vh;
|
/* min-height: 100vh;
|
||||||
min-height: 100dvh; */
|
min-height: 100dvh; */
|
||||||
|
|
|
@ -618,6 +618,7 @@
|
||||||
~ *:not(
|
~ *:not(
|
||||||
.content.truncated,
|
.content.truncated,
|
||||||
.media-container,
|
.media-container,
|
||||||
|
.media-first-container,
|
||||||
.card,
|
.card,
|
||||||
.media-figure-multiple,
|
.media-figure-multiple,
|
||||||
.spoiler-media-button
|
.spoiler-media-button
|
||||||
|
@ -638,6 +639,7 @@
|
||||||
|
|
||||||
~ *:not(
|
~ *:not(
|
||||||
.media-container,
|
.media-container,
|
||||||
|
.media-first-container,
|
||||||
.card,
|
.card,
|
||||||
.media-figure-multiple,
|
.media-figure-multiple,
|
||||||
.spoiler-media-button
|
.spoiler-media-button
|
||||||
|
@ -708,11 +710,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~ :is(.media-container, .media-figure-multiple) .media {
|
~ :is(.media-container, .media-first-container, .media-figure-multiple)
|
||||||
|
.media {
|
||||||
background-image: radial-gradient(
|
background-image: radial-gradient(
|
||||||
circle at 50% 50%,
|
circle at 50% 50%,
|
||||||
var(--average-color, var(--bg-faded-color)),
|
var(--average-color, var(--bg-faded-color)),
|
||||||
var(--bg-color) 20em
|
var(--bg-color) 25em
|
||||||
);
|
);
|
||||||
|
|
||||||
> *:not(.media-play, .alt-badge) {
|
> *:not(.media-play, .alt-badge) {
|
||||||
|
@ -1316,6 +1319,227 @@ body:has(#modal-container .carousel) .status .media img:hover {
|
||||||
background-blend-mode: multiply;
|
background-blend-mode: multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status.skeleton .media-first-container {
|
||||||
|
min-height: 3em;
|
||||||
|
background-color: var(--outline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-media-first {
|
||||||
|
.meta-name {
|
||||||
|
opacity: 0.65;
|
||||||
|
transition: opacity 0.5s ease-in-out;
|
||||||
|
|
||||||
|
b + i {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:is(:hover, :focus) > & .meta-name {
|
||||||
|
opacity: 1;
|
||||||
|
b + i {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-first-spoiler-content {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 100%;
|
||||||
|
transition: opacity 0.5s ease-in-out;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
&:hover .media-first-spoiler-content {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-first-spoiler-button {
|
||||||
|
display: inline-flex !important;
|
||||||
|
}
|
||||||
|
.media-first-container {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
user-select: none;
|
||||||
|
margin-inline: -16px;
|
||||||
|
position: relative;
|
||||||
|
scrollbar-width: none;
|
||||||
|
/* border: var(--hairline-width) solid var(--outline-color);
|
||||||
|
border-inline-width: 0;
|
||||||
|
background-color: var(--bg-faded-color); */
|
||||||
|
|
||||||
|
@media (min-width: 40em) {
|
||||||
|
margin-inline: 0;
|
||||||
|
/* border-radius: 4px; */
|
||||||
|
border-inline-width: var(--hairline-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .media-first-item {
|
||||||
|
scroll-snap-align: center;
|
||||||
|
scroll-snap-stop: always;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:not(:only-child) {
|
||||||
|
background-color: var(--bg-blur-color);
|
||||||
|
box-shadow: inset 0 0 0 var(--hairline-width) var(--outline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media {
|
||||||
|
/* background-color: var(--average-color, var(--bg-faded-color)); */
|
||||||
|
width: var(--width);
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
min-height: var(--min-dimension);
|
||||||
|
/* max-height: min(var(--height), 80vh); */
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
object-fit: scale-down;
|
||||||
|
animation: none;
|
||||||
|
|
||||||
|
&:not([data-loaded='true']) {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-carousel-controls {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100%;
|
||||||
|
position: sticky;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-indexer {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
color: var(--media-fg-color);
|
||||||
|
background-color: var(--media-bg-color);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 16px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 1.5s ease-in-out;
|
||||||
|
border: var(--hairline-width) solid var(--media-outline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-carousel-button {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding-inline: 8px;
|
||||||
|
margin-block: 3em;
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.carousel-button {
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .carousel-button {
|
||||||
|
left: auto;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) and (pointer: fine) {
|
||||||
|
.carousel-button {
|
||||||
|
filter: opacity(0);
|
||||||
|
}
|
||||||
|
&:hover .carousel-button {
|
||||||
|
filter: opacity(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:is(:hover, :focus) > & .carousel-indexer {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-carousel-dots {
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
.carousel-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--text-color);
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
opacity: 0.3;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: var(--text-color);
|
||||||
|
transform: scale(1.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-first-content {
|
||||||
|
margin-top: 8px;
|
||||||
|
height: 1.75em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0.9em;
|
||||||
|
mask-image: linear-gradient(to bottom, black 1.5em, transparent 1.75em);
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.5s ease-in-out;
|
||||||
|
|
||||||
|
@media (min-width: 40em) {
|
||||||
|
margin-inline: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
text-align: center;
|
||||||
|
/* Brute force ellipsis */
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
filter: grayscale(0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(:hover, :focus) > & .media-first-content {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.status:not(.large) .hashtag-stuffing {
|
.status:not(.large) .hashtag-stuffing {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
|
|
@ -169,15 +169,19 @@ function Status({
|
||||||
allowContextMenu,
|
allowContextMenu,
|
||||||
showActionsBar,
|
showActionsBar,
|
||||||
showReplyParent,
|
showReplyParent,
|
||||||
|
mediaFirst,
|
||||||
}) {
|
}) {
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
return (
|
return (
|
||||||
<div class="status skeleton">
|
<div class={`status skeleton ${mediaFirst ? 'status-media-first' : ''}`}>
|
||||||
<Avatar size="xxl" />
|
{!mediaFirst && <Avatar size="xxl" />}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="meta">███ ████████</div>
|
<div class="meta">
|
||||||
|
{(size === 's' || mediaFirst) && <Avatar size="m" />} ███ ████████
|
||||||
|
</div>
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div class="content">
|
{mediaFirst && <div class="media-first-container" />}
|
||||||
|
<div class={`content ${mediaFirst ? 'media-first-content' : ''}`}>
|
||||||
<p>████ ████████</p>
|
<p>████ ████████</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -247,6 +251,10 @@ function Status({
|
||||||
emojiReactions,
|
emojiReactions,
|
||||||
} = status;
|
} = status;
|
||||||
|
|
||||||
|
// if (!mediaAttachments?.length) mediaFirst = false;
|
||||||
|
const hasMediaAttachments = !!mediaAttachments?.length;
|
||||||
|
if (mediaFirst && hasMediaAttachments) size = 's';
|
||||||
|
|
||||||
const currentAccount = useMemo(() => {
|
const currentAccount = useMemo(() => {
|
||||||
return store.session.get('currentAccount');
|
return store.session.get('currentAccount');
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -354,6 +362,7 @@ function Status({
|
||||||
size={size}
|
size={size}
|
||||||
contentTextWeight={contentTextWeight}
|
contentTextWeight={contentTextWeight}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -378,6 +387,7 @@ function Status({
|
||||||
contentTextWeight={contentTextWeight}
|
contentTextWeight={contentTextWeight}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -411,6 +421,7 @@ function Status({
|
||||||
contentTextWeight={contentTextWeight}
|
contentTextWeight={contentTextWeight}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -848,56 +859,62 @@ function Status({
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{(enableTranslate || !language || differentLanguage) && <MenuDivider />}
|
{!mediaFirst && (
|
||||||
{enableTranslate ? (
|
<>
|
||||||
<div class={supportsTTS ? 'menu-horizontal' : ''}>
|
{(enableTranslate || !language || differentLanguage) && (
|
||||||
<MenuItem
|
<MenuDivider />
|
||||||
disabled={forceTranslate}
|
|
||||||
onClick={() => {
|
|
||||||
setForceTranslate(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon="translate" />
|
|
||||||
<span>Translate</span>
|
|
||||||
</MenuItem>
|
|
||||||
{supportsTTS && (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => {
|
|
||||||
const postText = getPostText(status);
|
|
||||||
if (postText) {
|
|
||||||
speak(postText, language);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon="speak" />
|
|
||||||
<span>Speak</span>
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
{enableTranslate ? (
|
||||||
) : (
|
<div class={supportsTTS ? 'menu-horizontal' : ''}>
|
||||||
(!language || differentLanguage) && (
|
|
||||||
<div class={supportsTTS ? 'menu-horizontal' : ''}>
|
|
||||||
<MenuLink
|
|
||||||
to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}
|
|
||||||
>
|
|
||||||
<Icon icon="translate" />
|
|
||||||
<span>Translate</span>
|
|
||||||
</MenuLink>
|
|
||||||
{supportsTTS && (
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
disabled={forceTranslate}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const postText = getPostText(status);
|
setForceTranslate(true);
|
||||||
if (postText) {
|
|
||||||
speak(postText, language);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon icon="speak" />
|
<Icon icon="translate" />
|
||||||
<span>Speak</span>
|
<span>Translate</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
{supportsTTS && (
|
||||||
</div>
|
<MenuItem
|
||||||
)
|
onClick={() => {
|
||||||
|
const postText = getPostText(status);
|
||||||
|
if (postText) {
|
||||||
|
speak(postText, language);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="speak" />
|
||||||
|
<span>Speak</span>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
(!language || differentLanguage) && (
|
||||||
|
<div class={supportsTTS ? 'menu-horizontal' : ''}>
|
||||||
|
<MenuLink
|
||||||
|
to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}
|
||||||
|
>
|
||||||
|
<Icon icon="translate" />
|
||||||
|
<span>Translate</span>
|
||||||
|
</MenuLink>
|
||||||
|
{supportsTTS && (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
const postText = getPostText(status);
|
||||||
|
if (postText) {
|
||||||
|
speak(postText, language);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="speak" />
|
||||||
|
<span>Speak</span>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{((!isSizeLarge && sameInstance) ||
|
{((!isSizeLarge && sameInstance) ||
|
||||||
enableTranslate ||
|
enableTranslate ||
|
||||||
|
@ -1384,7 +1401,7 @@ function Status({
|
||||||
}[size]
|
}[size]
|
||||||
} ${_deleted ? 'status-deleted' : ''} ${quoted ? 'status-card' : ''} ${
|
} ${_deleted ? 'status-deleted' : ''} ${quoted ? 'status-card' : ''} ${
|
||||||
isContextMenuOpen ? 'status-menu-open' : ''
|
isContextMenuOpen ? 'status-menu-open' : ''
|
||||||
}`}
|
} ${mediaFirst && hasMediaAttachments ? 'status-media-first' : ''}`}
|
||||||
onMouseEnter={debugHover}
|
onMouseEnter={debugHover}
|
||||||
onContextMenu={(e) => {
|
onContextMenu={(e) => {
|
||||||
if (!showContextMenu) return;
|
if (!showContextMenu) return;
|
||||||
|
@ -1712,188 +1729,253 @@ function Status({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{!!spoilerText && (
|
{mediaFirst && hasMediaAttachments ? (
|
||||||
<>
|
<>
|
||||||
<div
|
{(!!spoilerText || !!sensitive) && !readingExpandSpoilers && (
|
||||||
class="content spoiler-content"
|
<>
|
||||||
lang={language}
|
{!!spoilerText && (
|
||||||
dir="auto"
|
<span
|
||||||
ref={spoilerContentRef}
|
class="spoiler-content media-first-spoiler-content"
|
||||||
data-read-more={readMoreText}
|
lang={language}
|
||||||
>
|
dir="auto"
|
||||||
<p>
|
ref={spoilerContentRef}
|
||||||
<EmojiText text={spoilerText} emojis={emojis} />
|
data-read-more={readMoreText}
|
||||||
</p>
|
>
|
||||||
</div>
|
<EmojiText text={spoilerText} emojis={emojis} />{' '}
|
||||||
{readingExpandSpoilers || previewMode ? (
|
</span>
|
||||||
<div class="spoiler-divider">
|
)}
|
||||||
<Icon icon="eye-open" /> Content warning
|
<button
|
||||||
|
class={`light spoiler-button media-first-spoiler-button ${
|
||||||
|
showSpoiler ? 'spoiling' : ''
|
||||||
|
}`}
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (showSpoiler) {
|
||||||
|
delete states.spoilers[id];
|
||||||
|
if (!readingExpandSpoilers) {
|
||||||
|
delete states.spoilersMedia[id];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
states.spoilers[id] = true;
|
||||||
|
if (!readingExpandSpoilers) {
|
||||||
|
states.spoilersMedia[id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
|
||||||
|
{showSpoiler ? 'Show less' : 'Show content'}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<MediaFirstContainer
|
||||||
|
mediaAttachments={mediaAttachments}
|
||||||
|
language={language}
|
||||||
|
postID={id}
|
||||||
|
instance={instance}
|
||||||
|
/>
|
||||||
|
{!!content && (
|
||||||
|
<div class="media-first-content content" ref={contentRef}>
|
||||||
|
<PostContent
|
||||||
|
post={status}
|
||||||
|
instance={instance}
|
||||||
|
previewMode={previewMode}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
class={`light spoiler-button ${
|
|
||||||
showSpoiler ? 'spoiling' : ''
|
|
||||||
}`}
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (showSpoiler) {
|
|
||||||
delete states.spoilers[id];
|
|
||||||
if (!readingExpandSpoilers) {
|
|
||||||
delete states.spoilersMedia[id];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
states.spoilers[id] = true;
|
|
||||||
if (!readingExpandSpoilers) {
|
|
||||||
states.spoilersMedia[id] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
|
|
||||||
{showSpoiler ? 'Show less' : 'Show content'}
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
) : (
|
||||||
{!!content && (
|
<>
|
||||||
<div
|
{!!spoilerText && (
|
||||||
class="content"
|
<>
|
||||||
ref={contentRef}
|
<div
|
||||||
data-read-more={readMoreText}
|
class="content spoiler-content"
|
||||||
>
|
|
||||||
<PostContent
|
|
||||||
post={status}
|
|
||||||
instance={instance}
|
|
||||||
previewMode={previewMode}
|
|
||||||
/>
|
|
||||||
<QuoteStatuses id={id} instance={instance} level={quoted} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!!poll && (
|
|
||||||
<Poll
|
|
||||||
lang={language}
|
|
||||||
poll={poll}
|
|
||||||
readOnly={readOnly || !sameInstance || !authenticated}
|
|
||||||
onUpdate={(newPoll) => {
|
|
||||||
states.statuses[sKey].poll = newPoll;
|
|
||||||
}}
|
|
||||||
refresh={() => {
|
|
||||||
return masto.v1.polls
|
|
||||||
.$select(poll.id)
|
|
||||||
.fetch()
|
|
||||||
.then((pollResponse) => {
|
|
||||||
states.statuses[sKey].poll = pollResponse;
|
|
||||||
})
|
|
||||||
.catch((e) => {}); // Silently fail
|
|
||||||
}}
|
|
||||||
votePoll={(choices) => {
|
|
||||||
return masto.v1.polls
|
|
||||||
.$select(poll.id)
|
|
||||||
.votes.create({
|
|
||||||
choices,
|
|
||||||
})
|
|
||||||
.then((pollResponse) => {
|
|
||||||
states.statuses[sKey].poll = pollResponse;
|
|
||||||
})
|
|
||||||
.catch((e) => {}); // Silently fail
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(((enableTranslate || inlineTranslate) &&
|
|
||||||
!!content.trim() &&
|
|
||||||
!!getHTMLText(emojifyText(content, emojis)) &&
|
|
||||||
differentLanguage) ||
|
|
||||||
forceTranslate) && (
|
|
||||||
<TranslationBlock
|
|
||||||
forceTranslate={forceTranslate || inlineTranslate}
|
|
||||||
mini={!isSizeLarge && !withinContext}
|
|
||||||
sourceLanguage={language}
|
|
||||||
text={getPostText(status)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!previewMode &&
|
|
||||||
sensitive &&
|
|
||||||
!!mediaAttachments.length &&
|
|
||||||
readingExpandMedia !== 'show_all' && (
|
|
||||||
<button
|
|
||||||
class={`plain spoiler-media-button ${
|
|
||||||
showSpoilerMedia ? 'spoiling' : ''
|
|
||||||
}`}
|
|
||||||
type="button"
|
|
||||||
hidden={!readingExpandSpoilers && !!spoilerText}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (showSpoilerMedia) {
|
|
||||||
delete states.spoilersMedia[id];
|
|
||||||
} else {
|
|
||||||
states.spoilersMedia[id] = true;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon={showSpoilerMedia ? 'eye-open' : 'eye-close'} />{' '}
|
|
||||||
{showSpoilerMedia ? 'Show less' : 'Show media'}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{!!mediaAttachments.length && (
|
|
||||||
<MultipleMediaFigure
|
|
||||||
lang={language}
|
|
||||||
enabled={showMultipleMediaCaptions}
|
|
||||||
captionChildren={captionChildren}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={mediaContainerRef}
|
|
||||||
class={`media-container media-eq${mediaAttachments.length} ${
|
|
||||||
mediaAttachments.length > 2 ? 'media-gt2' : ''
|
|
||||||
} ${mediaAttachments.length > 4 ? 'media-gt4' : ''}`}
|
|
||||||
>
|
|
||||||
{displayedMediaAttachments.map((media, i) => (
|
|
||||||
<Media
|
|
||||||
key={media.id}
|
|
||||||
media={media}
|
|
||||||
autoAnimate={isSizeLarge}
|
|
||||||
showCaption={mediaAttachments.length === 1}
|
|
||||||
allowLongerCaption={
|
|
||||||
!content && mediaAttachments.length === 1
|
|
||||||
}
|
|
||||||
lang={language}
|
lang={language}
|
||||||
altIndex={
|
dir="auto"
|
||||||
showMultipleMediaCaptions &&
|
ref={spoilerContentRef}
|
||||||
!!media.description &&
|
data-read-more={readMoreText}
|
||||||
i + 1
|
>
|
||||||
}
|
<p>
|
||||||
to={`/${instance}/s/${id}?${
|
<EmojiText text={spoilerText} emojis={emojis} />
|
||||||
withinContext ? 'media' : 'media-only'
|
</p>
|
||||||
}=${i + 1}`}
|
</div>
|
||||||
onClick={
|
{readingExpandSpoilers || previewMode ? (
|
||||||
onMediaClick
|
<div class="spoiler-divider">
|
||||||
? (e) => {
|
<Icon icon="eye-open" /> Content warning
|
||||||
onMediaClick(e, i, media, status);
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
class={`light spoiler-button ${
|
||||||
|
showSpoiler ? 'spoiling' : ''
|
||||||
|
}`}
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (showSpoiler) {
|
||||||
|
delete states.spoilers[id];
|
||||||
|
if (!readingExpandSpoilers) {
|
||||||
|
delete states.spoilersMedia[id];
|
||||||
}
|
}
|
||||||
: undefined
|
} else {
|
||||||
}
|
states.spoilers[id] = true;
|
||||||
|
if (!readingExpandSpoilers) {
|
||||||
|
states.spoilersMedia[id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
|
||||||
|
{showSpoiler ? 'Show less' : 'Show content'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!!content && (
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
ref={contentRef}
|
||||||
|
data-read-more={readMoreText}
|
||||||
|
>
|
||||||
|
<PostContent
|
||||||
|
post={status}
|
||||||
|
instance={instance}
|
||||||
|
previewMode={previewMode}
|
||||||
/>
|
/>
|
||||||
))}
|
<QuoteStatuses id={id} instance={instance} level={quoted} />
|
||||||
</div>
|
</div>
|
||||||
</MultipleMediaFigure>
|
)}
|
||||||
|
{!!poll && (
|
||||||
|
<Poll
|
||||||
|
lang={language}
|
||||||
|
poll={poll}
|
||||||
|
readOnly={readOnly || !sameInstance || !authenticated}
|
||||||
|
onUpdate={(newPoll) => {
|
||||||
|
states.statuses[sKey].poll = newPoll;
|
||||||
|
}}
|
||||||
|
refresh={() => {
|
||||||
|
return masto.v1.polls
|
||||||
|
.$select(poll.id)
|
||||||
|
.fetch()
|
||||||
|
.then((pollResponse) => {
|
||||||
|
states.statuses[sKey].poll = pollResponse;
|
||||||
|
})
|
||||||
|
.catch((e) => {}); // Silently fail
|
||||||
|
}}
|
||||||
|
votePoll={(choices) => {
|
||||||
|
return masto.v1.polls
|
||||||
|
.$select(poll.id)
|
||||||
|
.votes.create({
|
||||||
|
choices,
|
||||||
|
})
|
||||||
|
.then((pollResponse) => {
|
||||||
|
states.statuses[sKey].poll = pollResponse;
|
||||||
|
})
|
||||||
|
.catch((e) => {}); // Silently fail
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(((enableTranslate || inlineTranslate) &&
|
||||||
|
!!content.trim() &&
|
||||||
|
!!getHTMLText(emojifyText(content, emojis)) &&
|
||||||
|
differentLanguage) ||
|
||||||
|
forceTranslate) && (
|
||||||
|
<TranslationBlock
|
||||||
|
forceTranslate={forceTranslate || inlineTranslate}
|
||||||
|
mini={!isSizeLarge && !withinContext}
|
||||||
|
sourceLanguage={language}
|
||||||
|
text={getPostText(status)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!previewMode &&
|
||||||
|
sensitive &&
|
||||||
|
!!mediaAttachments.length &&
|
||||||
|
readingExpandMedia !== 'show_all' && (
|
||||||
|
<button
|
||||||
|
class={`plain spoiler-media-button ${
|
||||||
|
showSpoilerMedia ? 'spoiling' : ''
|
||||||
|
}`}
|
||||||
|
type="button"
|
||||||
|
hidden={!readingExpandSpoilers && !!spoilerText}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (showSpoilerMedia) {
|
||||||
|
delete states.spoilersMedia[id];
|
||||||
|
} else {
|
||||||
|
states.spoilersMedia[id] = true;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={showSpoilerMedia ? 'eye-open' : 'eye-close'}
|
||||||
|
/>{' '}
|
||||||
|
{showSpoilerMedia ? 'Show less' : 'Show media'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{!!mediaAttachments.length && (
|
||||||
|
<MultipleMediaFigure
|
||||||
|
lang={language}
|
||||||
|
enabled={showMultipleMediaCaptions}
|
||||||
|
captionChildren={captionChildren}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={mediaContainerRef}
|
||||||
|
class={`media-container media-eq${
|
||||||
|
mediaAttachments.length
|
||||||
|
} ${mediaAttachments.length > 2 ? 'media-gt2' : ''} ${
|
||||||
|
mediaAttachments.length > 4 ? 'media-gt4' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{displayedMediaAttachments.map((media, i) => (
|
||||||
|
<Media
|
||||||
|
key={media.id}
|
||||||
|
media={media}
|
||||||
|
autoAnimate={isSizeLarge}
|
||||||
|
showCaption={mediaAttachments.length === 1}
|
||||||
|
allowLongerCaption={
|
||||||
|
!content && mediaAttachments.length === 1
|
||||||
|
}
|
||||||
|
lang={language}
|
||||||
|
altIndex={
|
||||||
|
showMultipleMediaCaptions &&
|
||||||
|
!!media.description &&
|
||||||
|
i + 1
|
||||||
|
}
|
||||||
|
to={`/${instance}/s/${id}?${
|
||||||
|
withinContext ? 'media' : 'media-only'
|
||||||
|
}=${i + 1}`}
|
||||||
|
onClick={
|
||||||
|
onMediaClick
|
||||||
|
? (e) => {
|
||||||
|
onMediaClick(e, i, media, status);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</MultipleMediaFigure>
|
||||||
|
)}
|
||||||
|
{!!card &&
|
||||||
|
/^https/i.test(card?.url) &&
|
||||||
|
!sensitive &&
|
||||||
|
!spoilerText &&
|
||||||
|
!poll &&
|
||||||
|
!mediaAttachments.length &&
|
||||||
|
!snapStates.statusQuotes[sKey] && (
|
||||||
|
<Card
|
||||||
|
card={card}
|
||||||
|
selfReferential={
|
||||||
|
card?.url === status.url || card?.url === status.uri
|
||||||
|
}
|
||||||
|
instance={currentInstance}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{!!card &&
|
|
||||||
/^https/i.test(card?.url) &&
|
|
||||||
!sensitive &&
|
|
||||||
!spoilerText &&
|
|
||||||
!poll &&
|
|
||||||
!mediaAttachments.length &&
|
|
||||||
!snapStates.statusQuotes[sKey] && (
|
|
||||||
<Card
|
|
||||||
card={card}
|
|
||||||
selfReferential={
|
|
||||||
card?.url === status.url || card?.url === status.uri
|
|
||||||
}
|
|
||||||
instance={currentInstance}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{!isSizeLarge && showCommentCount && (
|
{!isSizeLarge && showCommentCount && (
|
||||||
<div class="content-comment-hint insignificant">
|
<div class="content-comment-hint insignificant">
|
||||||
|
@ -2171,6 +2253,101 @@ function MultipleMediaFigure(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MediaFirstContainer(props) {
|
||||||
|
const { mediaAttachments, language, postID, instance } = props;
|
||||||
|
const moreThanOne = mediaAttachments.length > 1;
|
||||||
|
|
||||||
|
const carouselRef = useRef();
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let handleScroll = () => {
|
||||||
|
const { clientWidth, scrollLeft } = carouselRef.current;
|
||||||
|
const index = Math.round(scrollLeft / clientWidth);
|
||||||
|
setCurrentIndex(index);
|
||||||
|
};
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current.addEventListener('scroll', handleScroll, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current.removeEventListener('scroll', handleScroll);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div class="media-first-container" ref={carouselRef}>
|
||||||
|
{mediaAttachments.map((media, i) => (
|
||||||
|
<div class="media-first-item" key={media.id}>
|
||||||
|
<Media
|
||||||
|
media={media}
|
||||||
|
lang={language}
|
||||||
|
to={`/${instance}/s/${postID}?media-only=${i + 1}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{moreThanOne && (
|
||||||
|
<div class="media-carousel-controls">
|
||||||
|
<div class="carousel-indexer">
|
||||||
|
{currentIndex + 1}/{mediaAttachments.length}
|
||||||
|
</div>
|
||||||
|
<label class="media-carousel-button">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="carousel-button"
|
||||||
|
hidden={currentIndex === 0}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
carouselRef.current.focus();
|
||||||
|
carouselRef.current.scrollTo({
|
||||||
|
left: carouselRef.current.clientWidth * (currentIndex - 1),
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-left" />
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
<label class="media-carousel-button">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="carousel-button"
|
||||||
|
hidden={currentIndex === mediaAttachments.length - 1}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
carouselRef.current.focus();
|
||||||
|
carouselRef.current.scrollTo({
|
||||||
|
left: carouselRef.current.clientWidth * (currentIndex + 1),
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-right" />
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{moreThanOne && (
|
||||||
|
<div class="media-carousel-dots">
|
||||||
|
{mediaAttachments.map((media, i) => (
|
||||||
|
<span
|
||||||
|
key={media.id}
|
||||||
|
class={`carousel-dot ${i === currentIndex ? 'active' : ''}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Card({ card, selfReferential, instance }) {
|
function Card({ card, selfReferential, instance }) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'preact/hooks';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { InView } from 'react-intersection-observer';
|
import { InView } from 'react-intersection-observer';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
@ -9,6 +15,7 @@ import FilterContext from '../utils/filter-context';
|
||||||
import { filteredItems, isFiltered } from '../utils/filters';
|
import { filteredItems, isFiltered } from '../utils/filters';
|
||||||
import states, { statusKey } from '../utils/states';
|
import states, { statusKey } from '../utils/states';
|
||||||
import statusPeek from '../utils/status-peek';
|
import statusPeek from '../utils/status-peek';
|
||||||
|
import { isMediaFirstInstance } from '../utils/store-utils';
|
||||||
import { groupBoosts, groupContext } from '../utils/timeline-utils';
|
import { groupBoosts, groupContext } from '../utils/timeline-utils';
|
||||||
import useInterval from '../utils/useInterval';
|
import useInterval from '../utils/useInterval';
|
||||||
import usePageVisibility from '../utils/usePageVisibility';
|
import usePageVisibility from '../utils/usePageVisibility';
|
||||||
|
@ -59,6 +66,8 @@ function Timeline({
|
||||||
|
|
||||||
console.debug('RENDER Timeline', id, refresh);
|
console.debug('RENDER Timeline', id, refresh);
|
||||||
|
|
||||||
|
const mediaFirst = useMemo(() => isMediaFirstInstance(), []);
|
||||||
|
|
||||||
const allowGrouping = view !== 'media';
|
const allowGrouping = view !== 'media';
|
||||||
const loadItems = useDebouncedCallback(
|
const loadItems = useDebouncedCallback(
|
||||||
(firstLoad) => {
|
(firstLoad) => {
|
||||||
|
@ -355,7 +364,9 @@ function Timeline({
|
||||||
<FilterContext.Provider value={filterContext}>
|
<FilterContext.Provider value={filterContext}>
|
||||||
<div
|
<div
|
||||||
id={`${id}-page`}
|
id={`${id}-page`}
|
||||||
class="deck-container"
|
class={`deck-container ${
|
||||||
|
mediaFirst ? 'deck-container-media-first' : ''
|
||||||
|
}`}
|
||||||
ref={(node) => {
|
ref={(node) => {
|
||||||
scrollableRef.current = node;
|
scrollableRef.current = node;
|
||||||
jRef.current = node;
|
jRef.current = node;
|
||||||
|
@ -432,6 +443,7 @@ function Timeline({
|
||||||
view={view}
|
view={view}
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
showReplyParent={showReplyParent}
|
showReplyParent={showReplyParent}
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{showMore &&
|
{showMore &&
|
||||||
|
@ -443,14 +455,14 @@ function Timeline({
|
||||||
height: '20vh',
|
height: '20vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Status skeleton />
|
<Status skeleton mediaFirst={mediaFirst} />
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
style={{
|
style={{
|
||||||
height: '25vh',
|
height: '25vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Status skeleton />
|
<Status skeleton mediaFirst={mediaFirst} />
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
|
@ -490,7 +502,7 @@ function Timeline({
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<Status skeleton />
|
<Status skeleton mediaFirst={mediaFirst} />
|
||||||
</li>
|
</li>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
@ -525,6 +537,7 @@ const TimelineItem = memo(
|
||||||
view,
|
view,
|
||||||
showFollowedTags,
|
showFollowedTags,
|
||||||
showReplyParent,
|
showReplyParent,
|
||||||
|
mediaFirst,
|
||||||
}) => {
|
}) => {
|
||||||
console.debug('RENDER TimelineItem', status.id);
|
console.debug('RENDER TimelineItem', status.id);
|
||||||
const { id: statusID, reblog, items, type, _pinned } = status;
|
const { id: statusID, reblog, items, type, _pinned } = status;
|
||||||
|
@ -533,6 +546,7 @@ const TimelineItem = memo(
|
||||||
const url = instance
|
const url = instance
|
||||||
? `/${instance}/s/${actualStatusID}`
|
? `/${instance}/s/${actualStatusID}`
|
||||||
: `/s/${actualStatusID}`;
|
: `/s/${actualStatusID}`;
|
||||||
|
|
||||||
if (items) {
|
if (items) {
|
||||||
const fItems = filteredItems(items, filterContext);
|
const fItems = filteredItems(items, filterContext);
|
||||||
let title = '';
|
let title = '';
|
||||||
|
@ -585,6 +599,7 @@ const TimelineItem = memo(
|
||||||
contentTextWeight
|
contentTextWeight
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Status
|
<Status
|
||||||
|
@ -594,6 +609,7 @@ const TimelineItem = memo(
|
||||||
contentTextWeight
|
contentTextWeight
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -689,6 +705,7 @@ const TimelineItem = memo(
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
showReplyParent={showReplyParent}
|
showReplyParent={showReplyParent}
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Status
|
<Status
|
||||||
|
@ -698,6 +715,7 @@ const TimelineItem = memo(
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
showReplyParent={showReplyParent}
|
showReplyParent={showReplyParent}
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
|
mediaFirst={mediaFirst}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -126,3 +126,8 @@ export function getCurrentInstanceConfiguration() {
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
return getInstanceConfiguration(instance);
|
return getInstanceConfiguration(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isMediaFirstInstance() {
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
return /pixelfed/i.test(instance?.version);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue