Fix familiar followers leaked to other profiles
Mistake for using global state when it should be per-profile
This commit is contained in:
parent
12d0e6aed8
commit
39f7d4e00d
1 changed files with 159 additions and 146 deletions
|
@ -1,8 +1,14 @@
|
||||||
import './account-info.css';
|
import './account-info.css';
|
||||||
|
|
||||||
import { Menu, MenuDivider, MenuItem, SubMenu } from '@szhsin/react-menu';
|
import { Menu, MenuDivider, MenuItem, SubMenu } from '@szhsin/react-menu';
|
||||||
import { useEffect, useMemo, useReducer, useRef, useState } from 'preact/hooks';
|
import {
|
||||||
import { proxy, useSnapshot } from 'valtio';
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useReducer,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'preact/hooks';
|
||||||
|
|
||||||
import { api } from '../utils/api';
|
import { api } from '../utils/api';
|
||||||
import enhanceContent from '../utils/enhance-content';
|
import enhanceContent from '../utils/enhance-content';
|
||||||
|
@ -49,10 +55,6 @@ const MUTE_DURATIONS_LABELS = {
|
||||||
|
|
||||||
const LIMIT = 80;
|
const LIMIT = 80;
|
||||||
|
|
||||||
const accountInfoStates = proxy({
|
|
||||||
familiarFollowers: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
function AccountInfo({
|
function AccountInfo({
|
||||||
account,
|
account,
|
||||||
fetchAccount = () => {},
|
fetchAccount = () => {},
|
||||||
|
@ -63,10 +65,10 @@ function AccountInfo({
|
||||||
const { masto } = api({
|
const { masto } = api({
|
||||||
instance,
|
instance,
|
||||||
});
|
});
|
||||||
|
const { masto: currentMasto } = api();
|
||||||
const [uiState, setUIState] = useState('default');
|
const [uiState, setUIState] = useState('default');
|
||||||
const isString = typeof account === 'string';
|
const isString = typeof account === 'string';
|
||||||
const [info, setInfo] = useState(isString ? null : account);
|
const [info, setInfo] = useState(isString ? null : account);
|
||||||
const snapAccountInfoStates = useSnapshot(accountInfoStates);
|
|
||||||
|
|
||||||
const isSelf = useMemo(
|
const isSelf = useMemo(
|
||||||
() => account.id === store.session.get('currentAccount'),
|
() => account.id === store.session.get('currentAccount'),
|
||||||
|
@ -202,6 +204,75 @@ function AccountInfo({
|
||||||
const LinkOrDiv = standalone ? 'div' : Link;
|
const LinkOrDiv = standalone ? 'div' : Link;
|
||||||
const accountLink = instance ? `/${instance}/a/${id}` : `/a/${id}`;
|
const accountLink = instance ? `/${instance}/a/${id}` : `/a/${id}`;
|
||||||
|
|
||||||
|
const [familiarFollowers, setFamiliarFollowers] = useState([]);
|
||||||
|
const [postingStats, setPostingStats] = useState();
|
||||||
|
const hasPostingStats = postingStats?.total >= 3;
|
||||||
|
|
||||||
|
const onRelationshipChange = useCallback(
|
||||||
|
({ relationship, currentID }) => {
|
||||||
|
if (!relationship.following) {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const fetchFamiliarFollowers =
|
||||||
|
currentMasto.v1.accounts.fetchFamiliarFollowers(currentID);
|
||||||
|
const fetchStatuses = currentMasto.v1.accounts
|
||||||
|
.listStatuses(currentID, {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={`account-container ${uiState === 'loading' ? 'skeleton' : ''}`}
|
class={`account-container ${uiState === 'loading' ? 'skeleton' : ''}`}
|
||||||
|
@ -447,19 +518,17 @@ function AccountInfo({
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!!snapAccountInfoStates.familiarFollowers.length && (
|
{!!familiarFollowers.length && (
|
||||||
<span class="shazam-container-horizontal">
|
<span class="shazam-container-horizontal">
|
||||||
<span class="shazam-container-inner stats-avatars-bunch">
|
<span class="shazam-container-inner stats-avatars-bunch">
|
||||||
{(snapAccountInfoStates.familiarFollowers || []).map(
|
{familiarFollowers.map((follower) => (
|
||||||
(follower) => (
|
<Avatar
|
||||||
<Avatar
|
url={follower.avatarStatic}
|
||||||
url={follower.avatarStatic}
|
size="s"
|
||||||
size="s"
|
alt={`${follower.displayName} @${follower.acct}`}
|
||||||
alt={`${follower.displayName} @${follower.acct}`}
|
squircle={follower?.bot}
|
||||||
squircle={follower?.bot}
|
/>
|
||||||
/>
|
))}
|
||||||
),
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -514,11 +583,75 @@ function AccountInfo({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{hasPostingStats && (
|
||||||
|
<Link
|
||||||
|
to={accountLink}
|
||||||
|
class="account-metadata-box"
|
||||||
|
onClick={() => {
|
||||||
|
states.showAccount = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<RelatedActions
|
<RelatedActions
|
||||||
info={info}
|
info={info}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
authenticated={authenticated}
|
authenticated={authenticated}
|
||||||
standalone={standalone}
|
onRelationshipChange={onRelationshipChange}
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
|
@ -530,7 +663,12 @@ function AccountInfo({
|
||||||
|
|
||||||
const FAMILIAR_FOLLOWERS_LIMIT = 3;
|
const FAMILIAR_FOLLOWERS_LIMIT = 3;
|
||||||
|
|
||||||
function RelatedActions({ info, instance, authenticated, standalone }) {
|
function RelatedActions({
|
||||||
|
info,
|
||||||
|
instance,
|
||||||
|
authenticated,
|
||||||
|
onRelationshipChange = () => {},
|
||||||
|
}) {
|
||||||
if (!info) return null;
|
if (!info) return null;
|
||||||
const {
|
const {
|
||||||
masto: currentMasto,
|
masto: currentMasto,
|
||||||
|
@ -541,7 +679,6 @@ function RelatedActions({ info, instance, authenticated, standalone }) {
|
||||||
|
|
||||||
const [relationshipUIState, setRelationshipUIState] = useState('default');
|
const [relationshipUIState, setRelationshipUIState] = useState('default');
|
||||||
const [relationship, setRelationship] = useState(null);
|
const [relationship, setRelationship] = useState(null);
|
||||||
const [postingStats, setPostingStats] = useState();
|
|
||||||
|
|
||||||
const { id, acct, url, username, locked, lastStatusAt, note, fields, moved } =
|
const { id, acct, url, username, locked, lastStatusAt, note, fields, moved } =
|
||||||
info;
|
info;
|
||||||
|
@ -604,8 +741,6 @@ function RelatedActions({ info, instance, authenticated, standalone }) {
|
||||||
if (moved) return;
|
if (moved) return;
|
||||||
|
|
||||||
setRelationshipUIState('loading');
|
setRelationshipUIState('loading');
|
||||||
accountInfoStates.familiarFollowers = [];
|
|
||||||
setPostingStats(null);
|
|
||||||
|
|
||||||
const fetchRelationships = currentMasto.v1.accounts.fetchRelationships([
|
const fetchRelationships = currentMasto.v1.accounts.fetchRelationships([
|
||||||
currentID,
|
currentID,
|
||||||
|
@ -619,63 +754,7 @@ function RelatedActions({ info, instance, authenticated, standalone }) {
|
||||||
if (relationships.length) {
|
if (relationships.length) {
|
||||||
const relationship = relationships[0];
|
const relationship = relationships[0];
|
||||||
setRelationship(relationship);
|
setRelationship(relationship);
|
||||||
|
onRelationshipChange({ relationship, currentID });
|
||||||
if (!relationship.following) {
|
|
||||||
try {
|
|
||||||
const fetchFamiliarFollowers =
|
|
||||||
currentMasto.v1.accounts.fetchFamiliarFollowers(currentID);
|
|
||||||
const fetchStatuses = currentMasto.v1.accounts
|
|
||||||
.listStatuses(currentID, {
|
|
||||||
limit: 20,
|
|
||||||
})
|
|
||||||
.next();
|
|
||||||
|
|
||||||
const followers = await fetchFamiliarFollowers;
|
|
||||||
console.log('fetched familiar followers', followers);
|
|
||||||
accountInfoStates.familiarFollowers =
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -697,74 +776,8 @@ function RelatedActions({ info, instance, authenticated, standalone }) {
|
||||||
const [showTranslatedBio, setShowTranslatedBio] = useState(false);
|
const [showTranslatedBio, setShowTranslatedBio] = useState(false);
|
||||||
const [showAddRemoveLists, setShowAddRemoveLists] = useState(false);
|
const [showAddRemoveLists, setShowAddRemoveLists] = useState(false);
|
||||||
|
|
||||||
const hasPostingStats = postingStats?.total >= 3;
|
|
||||||
const accountLink = instance ? `/${instance}/a/${id}` : `/a/${id}`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasPostingStats && (
|
|
||||||
<Link
|
|
||||||
to={accountLink}
|
|
||||||
class="account-metadata-box"
|
|
||||||
onClick={() => {
|
|
||||||
states.showAccount = false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<p class="actions">
|
<p class="actions">
|
||||||
<span>
|
<span>
|
||||||
{followedBy ? (
|
{followedBy ? (
|
||||||
|
|
Loading…
Add table
Reference in a new issue