mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-11-28 12:18:51 +03:00
Enable on-demand posting stats
- Slight refactor - Make sure stats also work when switching instances - Make sure zero stats fallback
This commit is contained in:
parent
a095a30500
commit
d1b8d737cc
2 changed files with 232 additions and 122 deletions
|
@ -342,23 +342,82 @@
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.account-container .posting-stats {
|
||||
font-size: 90%;
|
||||
color: var(--text-insignificant-color);
|
||||
.account-container .posting-stats-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
color: inherit;
|
||||
background-color: var(--bg-faded-color);
|
||||
padding: 8px 12px;
|
||||
--size: 8px;
|
||||
--original-color: var(--link-color);
|
||||
font-size: 90%;
|
||||
color: var(--text-insignificant-color);
|
||||
line-height: 1;
|
||||
vertical-align: text-top;
|
||||
border-radius: 4px;
|
||||
|
||||
&:is(:hover, :focus-within) {
|
||||
color: var(--text-color);
|
||||
background-color: var(--link-bg-hover-color);
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
.loader-container {
|
||||
margin: 0;
|
||||
opacity: 0.5;
|
||||
transform: scale(0.75);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wobble {
|
||||
0% {
|
||||
transform: rotate(-4deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(4deg);
|
||||
}
|
||||
}
|
||||
@keyframes loading-spin {
|
||||
0% {
|
||||
transform: rotate(0deg) scale(0.75);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg) scale(0.75);
|
||||
}
|
||||
}
|
||||
.posting-stats-icon {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 8px;
|
||||
filter: opacity(0.75);
|
||||
animation: wobble 2s linear both infinite alternate !important;
|
||||
|
||||
&.loading {
|
||||
animation: loading-spin 0.35s linear both infinite !important;
|
||||
}
|
||||
}
|
||||
|
||||
.account-container {
|
||||
--posting-stats-size: 8px;
|
||||
--original-color: var(--link-color);
|
||||
|
||||
.posting-stats {
|
||||
font-size: 90%;
|
||||
color: var(--text-insignificant-color);
|
||||
background-color: var(--bg-faded-color);
|
||||
padding: 8px 12px;
|
||||
|
||||
&:is(:hover, :focus-within) {
|
||||
background-color: var(--link-bg-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
.posting-stats-bar {
|
||||
--gap: 0.5px;
|
||||
--gap-color: var(--outline-color);
|
||||
height: var(--size);
|
||||
border-radius: var(--size);
|
||||
height: var(--posting-stats-size);
|
||||
border-radius: var(--posting-stats-size);
|
||||
overflow: hidden;
|
||||
margin: 8px 0;
|
||||
box-shadow: inset 0 0 0 1px var(--outline-color),
|
||||
|
@ -388,9 +447,9 @@
|
|||
|
||||
.posting-stats-legend-item {
|
||||
display: inline-block;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
border-radius: var(--size);
|
||||
width: var(--posting-stats-size);
|
||||
height: var(--posting-stats-size);
|
||||
border-radius: var(--posting-stats-size);
|
||||
background-color: var(--text-insignificant-color);
|
||||
vertical-align: middle;
|
||||
margin: 0 4px 2px;
|
||||
|
|
|
@ -206,71 +206,85 @@ function AccountInfo({
|
|||
|
||||
const [familiarFollowers, setFamiliarFollowers] = useState([]);
|
||||
const [postingStats, setPostingStats] = useState();
|
||||
const hasPostingStats = postingStats?.total >= 3;
|
||||
const [postingStatsUIState, setPostingStatsUIState] = useState('default');
|
||||
const hasPostingStats = !!postingStats?.total;
|
||||
const currentIDRef = useRef();
|
||||
|
||||
const renderFamiliarFollowers = async () => {
|
||||
if (!currentIDRef.current) return;
|
||||
const currentID = currentIDRef.current;
|
||||
try {
|
||||
const fetchFamiliarFollowers =
|
||||
currentMasto.v1.accounts.familiarFollowers.fetch({
|
||||
id: [currentID],
|
||||
});
|
||||
|
||||
const followers = await fetchFamiliarFollowers;
|
||||
console.log('fetched familiar followers', followers);
|
||||
setFamiliarFollowers(
|
||||
followers[0].accounts.slice(0, FAMILIAR_FOLLOWERS_LIMIT),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const renderPostingStats = async () => {
|
||||
setPostingStatsUIState('loading');
|
||||
try {
|
||||
const fetchStatuses = masto.v1.accounts
|
||||
.$select(id)
|
||||
.statuses.list({
|
||||
limit: 20,
|
||||
})
|
||||
.next();
|
||||
|
||||
const { value: statuses } = await fetchStatuses;
|
||||
console.log('fetched statuses', statuses);
|
||||
const stats = {
|
||||
total: statuses.length,
|
||||
originals: 0,
|
||||
replies: 0,
|
||||
boosts: 0,
|
||||
};
|
||||
// Categories statuses by type
|
||||
// - Original posts (not replies to others)
|
||||
// - Threads (self-replies + 1st original post)
|
||||
// - Boosts (reblogs)
|
||||
// - Replies (not-self replies)
|
||||
statuses.forEach((status) => {
|
||||
if (status.reblog) {
|
||||
stats.boosts++;
|
||||
} else if (status.inReplyToAccountId !== id && !!status.inReplyToId) {
|
||||
stats.replies++;
|
||||
} else {
|
||||
stats.originals++;
|
||||
}
|
||||
});
|
||||
|
||||
// Count days since last post
|
||||
stats.daysSinceLastPost = Math.ceil(
|
||||
(Date.now() - new Date(statuses[statuses.length - 1].createdAt)) /
|
||||
86400000,
|
||||
);
|
||||
|
||||
console.log('posting stats', stats);
|
||||
setPostingStats(stats);
|
||||
setPostingStatsUIState('default');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setPostingStatsUIState('error');
|
||||
}
|
||||
};
|
||||
|
||||
const onRelationshipChange = useCallback(
|
||||
({ relationship, currentID }) => {
|
||||
currentIDRef.current = currentID;
|
||||
if (!relationship.following) {
|
||||
(async () => {
|
||||
try {
|
||||
const fetchFamiliarFollowers =
|
||||
currentMasto.v1.accounts.familiarFollowers.fetch({
|
||||
id: [currentID],
|
||||
});
|
||||
const fetchStatuses = currentMasto.v1.accounts
|
||||
.$select(currentID)
|
||||
.statuses.list({
|
||||
limit: 20,
|
||||
})
|
||||
.next();
|
||||
|
||||
const followers = await fetchFamiliarFollowers;
|
||||
console.log('fetched familiar followers', followers);
|
||||
setFamiliarFollowers(
|
||||
followers[0].accounts.slice(0, FAMILIAR_FOLLOWERS_LIMIT),
|
||||
);
|
||||
|
||||
if (!standalone) {
|
||||
const { value: statuses } = await fetchStatuses;
|
||||
console.log('fetched statuses', statuses);
|
||||
const stats = {
|
||||
total: statuses.length,
|
||||
originals: 0,
|
||||
replies: 0,
|
||||
boosts: 0,
|
||||
};
|
||||
// Categories statuses by type
|
||||
// - Original posts (not replies to others)
|
||||
// - Threads (self-replies + 1st original post)
|
||||
// - Boosts (reblogs)
|
||||
// - Replies (not-self replies)
|
||||
statuses.forEach((status) => {
|
||||
if (status.reblog) {
|
||||
stats.boosts++;
|
||||
} else if (
|
||||
status.inReplyToAccountId !== currentID &&
|
||||
!!status.inReplyToId
|
||||
) {
|
||||
stats.replies++;
|
||||
} else {
|
||||
stats.originals++;
|
||||
}
|
||||
});
|
||||
|
||||
// Count days since last post
|
||||
stats.daysSinceLastPost = Math.ceil(
|
||||
(Date.now() -
|
||||
new Date(statuses[statuses.length - 1].createdAt)) /
|
||||
86400000,
|
||||
);
|
||||
|
||||
console.log('posting stats', stats);
|
||||
setPostingStats(stats);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
})();
|
||||
renderFamiliarFollowers();
|
||||
if (!standalone) {
|
||||
renderPostingStats();
|
||||
}
|
||||
}
|
||||
},
|
||||
[standalone],
|
||||
|
@ -586,8 +600,8 @@ function AccountInfo({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
{hasPostingStats && (
|
||||
<Link
|
||||
{!!postingStats && (
|
||||
<LinkOrDiv
|
||||
to={accountLink}
|
||||
class="account-metadata-box"
|
||||
onClick={() => {
|
||||
|
@ -596,60 +610,97 @@ function AccountInfo({
|
|||
>
|
||||
<div class="shazam-container">
|
||||
<div class="shazam-container-inner">
|
||||
<div
|
||||
class="posting-stats"
|
||||
title={`${Math.round(
|
||||
(postingStats.originals / postingStats.total) * 100,
|
||||
)}% original posts, ${Math.round(
|
||||
(postingStats.replies / postingStats.total) * 100,
|
||||
)}% replies, ${Math.round(
|
||||
(postingStats.boosts / postingStats.total) * 100,
|
||||
)}% boosts`}
|
||||
>
|
||||
<div>
|
||||
{postingStats.daysSinceLastPost < 365
|
||||
? `Last ${postingStats.total} posts in the past
|
||||
${postingStats.daysSinceLastPost} day${
|
||||
postingStats.daysSinceLastPost > 1 ? 's' : ''
|
||||
}`
|
||||
: `
|
||||
Last ${postingStats.total} posts in the past year(s)
|
||||
`}
|
||||
</div>
|
||||
{hasPostingStats ? (
|
||||
<div
|
||||
class="posting-stats-bar"
|
||||
style={{
|
||||
// [originals | replies | boosts]
|
||||
'--originals-percentage': `${
|
||||
(postingStats.originals / postingStats.total) *
|
||||
100
|
||||
}%`,
|
||||
'--replies-percentage': `${
|
||||
((postingStats.originals + postingStats.replies) /
|
||||
postingStats.total) *
|
||||
100
|
||||
}%`,
|
||||
}}
|
||||
/>
|
||||
<div class="posting-stats-legends">
|
||||
<span class="ib">
|
||||
<span class="posting-stats-legend-item posting-stats-legend-item-originals" />{' '}
|
||||
Original
|
||||
</span>{' '}
|
||||
<span class="ib">
|
||||
<span class="posting-stats-legend-item posting-stats-legend-item-replies" />{' '}
|
||||
Replies
|
||||
</span>{' '}
|
||||
<span class="ib">
|
||||
<span class="posting-stats-legend-item posting-stats-legend-item-boosts" />{' '}
|
||||
Boosts
|
||||
</span>
|
||||
class="posting-stats"
|
||||
title={`${Math.round(
|
||||
(postingStats.originals / postingStats.total) * 100,
|
||||
)}% original posts, ${Math.round(
|
||||
(postingStats.replies / postingStats.total) * 100,
|
||||
)}% replies, ${Math.round(
|
||||
(postingStats.boosts / postingStats.total) * 100,
|
||||
)}% boosts`}
|
||||
>
|
||||
<div>
|
||||
{postingStats.daysSinceLastPost < 365
|
||||
? `Last ${postingStats.total} posts in the past
|
||||
${postingStats.daysSinceLastPost} day${
|
||||
postingStats.daysSinceLastPost > 1 ? 's' : ''
|
||||
}`
|
||||
: `
|
||||
Last ${postingStats.total} posts in the past year(s)
|
||||
`}
|
||||
</div>
|
||||
<div
|
||||
class="posting-stats-bar"
|
||||
style={{
|
||||
// [originals | replies | boosts]
|
||||
'--originals-percentage': `${
|
||||
(postingStats.originals / postingStats.total) *
|
||||
100
|
||||
}%`,
|
||||
'--replies-percentage': `${
|
||||
((postingStats.originals +
|
||||
postingStats.replies) /
|
||||
postingStats.total) *
|
||||
100
|
||||
}%`,
|
||||
}}
|
||||
/>
|
||||
<div class="posting-stats-legends">
|
||||
<span class="ib">
|
||||
<span class="posting-stats-legend-item posting-stats-legend-item-originals" />{' '}
|
||||
Original
|
||||
</span>{' '}
|
||||
<span class="ib">
|
||||
<span class="posting-stats-legend-item posting-stats-legend-item-replies" />{' '}
|
||||
Replies
|
||||
</span>{' '}
|
||||
<span class="ib">
|
||||
<span class="posting-stats-legend-item posting-stats-legend-item-boosts" />{' '}
|
||||
Boosts
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div class="posting-stats">Post stats unavailable.</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</LinkOrDiv>
|
||||
)}
|
||||
<div class="account-metadata-box">
|
||||
<div
|
||||
class="shazam-container no-animation"
|
||||
hidden={!!postingStats}
|
||||
>
|
||||
<div class="shazam-container-inner">
|
||||
<button
|
||||
type="button"
|
||||
class="posting-stats-button"
|
||||
disabled={postingStatsUIState === 'loading'}
|
||||
onClick={() => {
|
||||
renderPostingStats();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={`posting-stats-bar posting-stats-icon ${
|
||||
postingStatsUIState === 'loading' ? 'loading' : ''
|
||||
}`}
|
||||
style={{
|
||||
'--originals-percentage': '33%',
|
||||
'--replies-percentage': '66%',
|
||||
}}
|
||||
/>
|
||||
View post stats{' '}
|
||||
{/* <Loader
|
||||
abrupt
|
||||
hidden={postingStatsUIState !== 'loading'}
|
||||
/> */}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RelatedActions
|
||||
info={info}
|
||||
instance={instance}
|
||||
|
|
Loading…
Reference in a new issue