Reuse BoostCarousel for pinned posts

Now we can show *anything* into a carousel
This commit is contained in:
Lim Chee Aun 2023-02-17 10:55:16 +08:00
parent a1edc142ae
commit 0430f4ae89
3 changed files with 85 additions and 54 deletions

View file

@ -550,46 +550,44 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
filter: brightness(0.95); filter: brightness(0.95);
} }
.boost-carousel { .status-carousel {
--carousel-faded-color: var(--bg-faded-color);
background: linear-gradient( background: linear-gradient(
to bottom right, to bottom right,
var(--reblog-faded-color), var(--carousel-faded-color),
transparent 150% transparent 150%
); );
position: relative; position: relative;
} }
.boost-carousel:after { .status-carousel:after {
content: ''; content: '';
position: absolute; position: absolute;
inset: 0; inset: 0;
pointer-events: none; pointer-events: none;
background-image: radial-gradient( background-image: radial-gradient(
ellipse 50% 32px at bottom center, ellipse 50% 32px at bottom center,
var(--reblog-faded-color), var(--carousel-faded-color),
transparent transparent
), ),
linear-gradient(to top, var(--bg-color), transparent 64px); linear-gradient(to top, var(--bg-color), transparent 64px);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: bottom center; background-position: bottom center;
} }
.boost-carousel .status-reblog { .status-carousel header {
background-image: none;
}
.boost-carousel header {
padding: 8px 16px 0; padding: 8px 16px 0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.boost-carousel h3 { .status-carousel h3 {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-size: 14px; font-size: 14px;
text-transform: uppercase; text-transform: uppercase;
color: var(--reblog-color); color: var(--carousel-color);
text-shadow: 0 1px var(--bg-color); text-shadow: 0 1px var(--bg-color);
} }
.boost-carousel ul { .status-carousel ul {
display: flex; display: flex;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
@ -601,7 +599,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
align-items: flex-start; align-items: flex-start;
counter-reset: index; counter-reset: index;
} }
.boost-carousel ul > li { .status-carousel ul > li {
scroll-snap-align: center; scroll-snap-align: center;
scroll-snap-stop: always; scroll-snap-stop: always;
flex-shrink: 0; flex-shrink: 0;
@ -616,12 +614,19 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
counter-increment: index; counter-increment: index;
position: relative; position: relative;
} }
.boost-carousel ul > li:before { .status-carousel.boosts-carousel {
--carousel-color: var(--reblog-color);
--carousel-faded-color: var(--reblog-faded-color);
}
.status-carousel.boosts-carousel .status-reblog {
background-image: none;
}
.status-carousel.boosts-carousel ul > li:before {
content: counter(index); content: counter(index);
position: absolute; position: absolute;
left: 0; left: 0;
font-size: 10px; font-size: 10px;
color: var(--reblog-color); color: var(--carousel-color);
padding: 8px; padding: 8px;
} }
@ -630,7 +635,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
text-align: center; text-align: center;
} }
.status-boost-link { .status-carousel-link {
display: block; display: block;
width: 100%; width: 100%;
text-decoration-line: none; text-decoration-line: none;
@ -645,15 +650,15 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
overflow: hidden; overflow: hidden;
box-shadow: 0 1px var(--bg-color); box-shadow: 0 1px var(--bg-color);
} }
.status-boost-link::focus { .status-carousel-link::focus {
background-color: var(--link-bg-hover-color); background-color: var(--link-bg-hover-color);
} }
@media (hover: hover) { @media (hover: hover) {
.status-boost-link:hover { .status-carousel-link:hover {
background-color: var(--link-bg-hover-color); background-color: var(--link-bg-hover-color);
} }
} }
.status-boost-link:active:not(:has(:is(.media, button):active)) { .status-carousel-link:active:not(:has(:is(.media, button):active)) {
filter: brightness(0.95); filter: brightness(0.95);
} }
@ -1417,9 +1422,9 @@ ul.link-list li a .icon {
transform: translate3d(-2.5vw, 0, 0); transform: translate3d(-2.5vw, 0, 0);
} }
.timeline:not(.flat) .timeline:not(.flat)
> li:not(:has(.boost-carousel)):has(+ li .status-link.is-active), > li:not(:has(.status-carousel)):has(+ li .status-link.is-active),
.timeline:not(.flat) .timeline:not(.flat)
> li:not(:has(.boost-carousel)):has(.status-link.is-active) > li:not(:has(.status-carousel)):has(.status-link.is-active)
+ li { + li {
transition: var(--back-transition); transition: var(--back-transition);
transform: translate3d(-1.25vw, 0, 0); transform: translate3d(-1.25vw, 0, 0);
@ -1430,7 +1435,7 @@ ul.link-list li a .icon {
/* :is(.carousel-top-controls, .carousel-controls) { /* :is(.carousel-top-controls, .carousel-controls) {
padding: 32px; padding: 32px;
} */ } */
li:has(.boost-carousel) { li:has(.status-carousel) {
width: 95vw; width: 95vw;
max-width: calc(320px * 3.3); max-width: calc(320px * 3.3);
transform: translateX(calc(-50% + 20em)); transform: translateX(calc(-50% + 20em));

View file

@ -293,19 +293,51 @@ function Timeline({
<> <>
<ul class="timeline"> <ul class="timeline">
{items.map((status) => { {items.map((status) => {
const { id: statusID, reblog, boosts } = status; const { id: statusID, reblog, items, type } = status;
const actualStatusID = reblog?.id || statusID; const actualStatusID = reblog?.id || statusID;
const url = instance const url = instance
? `/${instance}/s/${actualStatusID}` ? `/${instance}/s/${actualStatusID}`
: `/s/${actualStatusID}`; : `/s/${actualStatusID}`;
if (boosts) { let title = '';
if (type === 'boosts') {
title = `${items.length} Boosts`;
} else if (type === 'pinned') {
title = 'Pinned posts';
}
if (items) {
return ( return (
<li key={`timeline-${statusID}`}> <li key={`timeline-${statusID}`}>
<BoostsCarousel <StatusCarousel title={title} class={`${type}-carousel`}>
boosts={boosts} {items.map((item) => {
useItemID={useItemID} const { id: statusID, reblog } = item;
instance={instance} const actualStatusID = reblog?.id || statusID;
/> const url = instance
? `/${instance}/s/${actualStatusID}`
: `/s/${actualStatusID}`;
return (
<li key={statusID}>
<Link
class="status-carousel-link timeline-item-alt"
to={url}
>
{useItemID ? (
<Status
statusID={statusID}
instance={instance}
size="s"
/>
) : (
<Status
status={item}
instance={instance}
size="s"
/>
)}
</Link>
</li>
);
})}
</StatusCarousel>
</li> </li>
); );
} }
@ -406,7 +438,10 @@ function groupBoosts(values) {
const boostStashID = boostStash.map((status) => status.id); const boostStashID = boostStash.map((status) => status.id);
if (boostStash.length > (values.length * 3) / 4) { if (boostStash.length > (values.length * 3) / 4) {
// insert boost array at the end of specialHome list // insert boost array at the end of specialHome list
newValues = [...newValues, { id: boostStashID, boosts: boostStash }]; newValues = [
...newValues,
{ id: boostStashID, items: boostStash, type: 'boosts' },
];
} else { } else {
// insert boosts array in the middle of specialHome list // insert boosts array in the middle of specialHome list
const half = Math.floor(newValues.length / 2); const half = Math.floor(newValues.length / 2);
@ -414,7 +449,8 @@ function groupBoosts(values) {
...newValues.slice(0, half), ...newValues.slice(0, half),
{ {
id: boostStashID, id: boostStashID,
boosts: boostStash, items: boostStash,
type: 'boosts',
}, },
...newValues.slice(half), ...newValues.slice(half),
]; ];
@ -425,7 +461,7 @@ function groupBoosts(values) {
} }
} }
function BoostsCarousel({ boosts, useItemID, instance }) { function StatusCarousel({ title, class: className, children }) {
const carouselRef = useRef(); const carouselRef = useRef();
const { reachStart, reachEnd, init } = useScroll({ const { reachStart, reachEnd, init } = useScroll({
scrollableElement: carouselRef.current, scrollableElement: carouselRef.current,
@ -436,9 +472,9 @@ function BoostsCarousel({ boosts, useItemID, instance }) {
}, []); }, []);
return ( return (
<div class="boost-carousel"> <div class={`status-carousel ${className}`}>
<header> <header>
<h3>{boosts.length} Boosts</h3> <h3>{title}</h3>
<span> <span>
<button <button
type="button" type="button"
@ -468,26 +504,7 @@ function BoostsCarousel({ boosts, useItemID, instance }) {
</button> </button>
</span> </span>
</header> </header>
<ul ref={carouselRef}> <ul ref={carouselRef}>{children}</ul>
{boosts.map((boost) => {
const { id: statusID, reblog } = boost;
const actualStatusID = reblog?.id || statusID;
const url = instance
? `/${instance}/s/${actualStatusID}`
: `/s/${actualStatusID}`;
return (
<li key={statusID}>
<Link class="status-boost-link timeline-item-alt" to={url}>
{useItemID ? (
<Status statusID={statusID} instance={instance} size="s" />
) : (
<Status status={boost} instance={instance} size="s" />
)}
</Link>
</li>
);
})}
</ul>
</div> </div>
); );
} }

View file

@ -27,7 +27,16 @@ function AccountStatuses() {
pinnedStatuses.forEach((status) => { pinnedStatuses.forEach((status) => {
status._pinned = true; status._pinned = true;
}); });
results.push(...pinnedStatuses); if (pinnedStatuses.length > 1) {
const pinnedStatusesIds = pinnedStatuses.map((status) => status.id);
results.push({
id: pinnedStatusesIds,
items: pinnedStatuses,
type: 'pinned',
});
} else {
results.push(...pinnedStatuses);
}
} }
} }
if (firstLoad || !accountStatusesIterator.current) { if (firstLoad || !accountStatusesIterator.current) {