mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-03-27 11:49:16 +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;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.account-container .posting-stats {
|
.account-container .posting-stats-button {
|
||||||
font-size: 90%;
|
display: flex;
|
||||||
color: var(--text-insignificant-color);
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
color: inherit;
|
||||||
background-color: var(--bg-faded-color);
|
background-color: var(--bg-faded-color);
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
--size: 8px;
|
font-size: 90%;
|
||||||
--original-color: var(--link-color);
|
color: var(--text-insignificant-color);
|
||||||
|
line-height: 1;
|
||||||
|
vertical-align: text-top;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
&:is(:hover, :focus-within) {
|
&:is(:hover, :focus-within) {
|
||||||
|
color: var(--text-color);
|
||||||
background-color: var(--link-bg-hover-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 {
|
.posting-stats-bar {
|
||||||
--gap: 0.5px;
|
--gap: 0.5px;
|
||||||
--gap-color: var(--outline-color);
|
--gap-color: var(--outline-color);
|
||||||
height: var(--size);
|
height: var(--posting-stats-size);
|
||||||
border-radius: var(--size);
|
border-radius: var(--posting-stats-size);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
box-shadow: inset 0 0 0 1px var(--outline-color),
|
box-shadow: inset 0 0 0 1px var(--outline-color),
|
||||||
|
@ -388,9 +447,9 @@
|
||||||
|
|
||||||
.posting-stats-legend-item {
|
.posting-stats-legend-item {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: var(--size);
|
width: var(--posting-stats-size);
|
||||||
height: var(--size);
|
height: var(--posting-stats-size);
|
||||||
border-radius: var(--size);
|
border-radius: var(--posting-stats-size);
|
||||||
background-color: var(--text-insignificant-color);
|
background-color: var(--text-insignificant-color);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: 0 4px 2px;
|
margin: 0 4px 2px;
|
||||||
|
|
|
@ -206,71 +206,85 @@ function AccountInfo({
|
||||||
|
|
||||||
const [familiarFollowers, setFamiliarFollowers] = useState([]);
|
const [familiarFollowers, setFamiliarFollowers] = useState([]);
|
||||||
const [postingStats, setPostingStats] = 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(
|
const onRelationshipChange = useCallback(
|
||||||
({ relationship, currentID }) => {
|
({ relationship, currentID }) => {
|
||||||
|
currentIDRef.current = currentID;
|
||||||
if (!relationship.following) {
|
if (!relationship.following) {
|
||||||
(async () => {
|
renderFamiliarFollowers();
|
||||||
try {
|
if (!standalone) {
|
||||||
const fetchFamiliarFollowers =
|
renderPostingStats();
|
||||||
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);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[standalone],
|
[standalone],
|
||||||
|
@ -586,8 +600,8 @@ function AccountInfo({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{hasPostingStats && (
|
{!!postingStats && (
|
||||||
<Link
|
<LinkOrDiv
|
||||||
to={accountLink}
|
to={accountLink}
|
||||||
class="account-metadata-box"
|
class="account-metadata-box"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -596,60 +610,97 @@ function AccountInfo({
|
||||||
>
|
>
|
||||||
<div class="shazam-container">
|
<div class="shazam-container">
|
||||||
<div class="shazam-container-inner">
|
<div class="shazam-container-inner">
|
||||||
<div
|
{hasPostingStats ? (
|
||||||
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
|
<div
|
||||||
class="posting-stats-bar"
|
class="posting-stats"
|
||||||
style={{
|
title={`${Math.round(
|
||||||
// [originals | replies | boosts]
|
(postingStats.originals / postingStats.total) * 100,
|
||||||
'--originals-percentage': `${
|
)}% original posts, ${Math.round(
|
||||||
(postingStats.originals / postingStats.total) *
|
(postingStats.replies / postingStats.total) * 100,
|
||||||
100
|
)}% replies, ${Math.round(
|
||||||
}%`,
|
(postingStats.boosts / postingStats.total) * 100,
|
||||||
'--replies-percentage': `${
|
)}% boosts`}
|
||||||
((postingStats.originals + postingStats.replies) /
|
>
|
||||||
postingStats.total) *
|
<div>
|
||||||
100
|
{postingStats.daysSinceLastPost < 365
|
||||||
}%`,
|
? `Last ${postingStats.total} posts in the past
|
||||||
}}
|
${postingStats.daysSinceLastPost} day${
|
||||||
/>
|
postingStats.daysSinceLastPost > 1 ? 's' : ''
|
||||||
<div class="posting-stats-legends">
|
}`
|
||||||
<span class="ib">
|
: `
|
||||||
<span class="posting-stats-legend-item posting-stats-legend-item-originals" />{' '}
|
Last ${postingStats.total} posts in the past year(s)
|
||||||
Original
|
`}
|
||||||
</span>{' '}
|
</div>
|
||||||
<span class="ib">
|
<div
|
||||||
<span class="posting-stats-legend-item posting-stats-legend-item-replies" />{' '}
|
class="posting-stats-bar"
|
||||||
Replies
|
style={{
|
||||||
</span>{' '}
|
// [originals | replies | boosts]
|
||||||
<span class="ib">
|
'--originals-percentage': `${
|
||||||
<span class="posting-stats-legend-item posting-stats-legend-item-boosts" />{' '}
|
(postingStats.originals / postingStats.total) *
|
||||||
Boosts
|
100
|
||||||
</span>
|
}%`,
|
||||||
|
'--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>
|
) : (
|
||||||
|
<div class="posting-stats">Post stats unavailable.</div>
|
||||||
|
)}
|
||||||
</div>
|
</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
|
<RelatedActions
|
||||||
info={info}
|
info={info}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
|
|
Loading…
Add table
Reference in a new issue