followed by foafs
This commit is contained in:
parent
062212f311
commit
35d7ec4685
@ -1,7 +1,7 @@
|
|||||||
import "./LinkPreview.css";
|
import "./LinkPreview.css";
|
||||||
|
|
||||||
import { CSSProperties, useEffect, useState } from "react";
|
import { CSSProperties, useEffect, useState } from "react";
|
||||||
import { LRUCache } from 'typescript-lru-cache';
|
import { LRUCache } from "typescript-lru-cache";
|
||||||
|
|
||||||
import { MediaElement } from "@/Components/Embed/MediaElement";
|
import { MediaElement } from "@/Components/Embed/MediaElement";
|
||||||
import Spinner from "@/Components/Icons/Spinner";
|
import Spinner from "@/Components/Icons/Spinner";
|
||||||
|
@ -12,10 +12,10 @@ import { DisplayAs, DisplayAsSelector } from "@/Components/Feed/DisplayAsSelecto
|
|||||||
import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer";
|
import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer";
|
||||||
import { LiveStreams } from "@/Components/LiveStream/LiveStreams";
|
import { LiveStreams } from "@/Components/LiveStream/LiveStreams";
|
||||||
import useHashtagsFeed from "@/Feed/HashtagsFeed";
|
import useHashtagsFeed from "@/Feed/HashtagsFeed";
|
||||||
|
import useHistoryState from "@/Hooks/useHistoryState";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
import useModeration from "@/Hooks/useModeration";
|
import useModeration from "@/Hooks/useModeration";
|
||||||
import { dedupeByPubkey, findTag, orderDescending } from "@/Utils";
|
import { dedupeByPubkey, findTag, orderDescending } from "@/Utils";
|
||||||
import useHistoryState from "@/Hooks/useHistoryState";
|
|
||||||
|
|
||||||
export interface TimelineFollowsProps {
|
export interface TimelineFollowsProps {
|
||||||
postsOnly: boolean;
|
postsOnly: boolean;
|
||||||
@ -34,7 +34,7 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
|
|||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const displayAsInitial = props.displayAs ?? login.feedDisplayAs ?? "list";
|
const displayAsInitial = props.displayAs ?? login.feedDisplayAs ?? "list";
|
||||||
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
|
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
|
||||||
const [latest, setLatest] = useHistoryState(unixNow(), 'TimelineFollowsLatest');
|
const [latest, setLatest] = useHistoryState(unixNow(), "TimelineFollowsLatest");
|
||||||
const feed = useSyncExternalStore(
|
const feed = useSyncExternalStore(
|
||||||
cb => FollowsFeed.hook(cb, "*"),
|
cb => FollowsFeed.hook(cb, "*"),
|
||||||
() => FollowsFeed.snapshot(),
|
() => FollowsFeed.snapshot(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { socialGraphInstance } from "@snort/system";
|
import { HexKey, socialGraphInstance } from "@snort/system";
|
||||||
import { Fragment } from "react";
|
import React, { Fragment, useMemo } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import DisplayName from "@/Components/User/DisplayName";
|
import DisplayName from "@/Components/User/DisplayName";
|
||||||
@ -9,52 +9,71 @@ import { ProfileLink } from "@/Components/User/ProfileLink";
|
|||||||
|
|
||||||
const MAX_FOLLOWED_BY_FRIENDS = 3;
|
const MAX_FOLLOWED_BY_FRIENDS = 3;
|
||||||
|
|
||||||
export default function FollowedBy({ pubkey }: { pubkey: string }) {
|
export default function FollowedBy({ pubkey }: { pubkey: HexKey }) {
|
||||||
|
const followDistance = socialGraphInstance.getFollowDistance(pubkey);
|
||||||
|
const { followedByFriendsArray, totalFollowedByFriends } = useMemo(() => {
|
||||||
const followedByFriends = socialGraphInstance.followedByFriends(pubkey);
|
const followedByFriends = socialGraphInstance.followedByFriends(pubkey);
|
||||||
const followedByFriendsArray = Array.from(followedByFriends).slice(0, MAX_FOLLOWED_BY_FRIENDS);
|
return {
|
||||||
return (
|
followedByFriendsArray: Array.from(followedByFriends).slice(0, MAX_FOLLOWED_BY_FRIENDS),
|
||||||
<div className="flex flex-row items-center">
|
totalFollowedByFriends: followedByFriends.size,
|
||||||
<div className="flex flex-row items-center">
|
};
|
||||||
<FollowDistanceIndicator className="p-2" pubkey={pubkey} />
|
}, [pubkey, followDistance]);
|
||||||
{followedByFriendsArray.map((a, index) => {
|
|
||||||
const zIndex = followedByFriendsArray.length - index;
|
|
||||||
|
|
||||||
return (
|
const renderFollowedByFriends = () => {
|
||||||
<div className={`inline-block ${index > 0 ? "-ml-5" : ""}`} key={a} style={{ zIndex }}>
|
return followedByFriendsArray.map((a, index) => (
|
||||||
|
<div
|
||||||
|
className={`inline-block ${index > 0 ? "-ml-5" : ""}`}
|
||||||
|
key={a}
|
||||||
|
style={{ zIndex: followedByFriendsArray.length - index }}>
|
||||||
<ProfileImage showFollowDistance={false} pubkey={a} size={24} showUsername={false} />
|
<ProfileImage showFollowDistance={false} pubkey={a} size={24} showUsername={false} />
|
||||||
</div>
|
</div>
|
||||||
);
|
));
|
||||||
})}
|
};
|
||||||
</div>
|
|
||||||
{followedByFriends.size > 0 && (
|
const renderFollowedByFriendsLinks = () => {
|
||||||
<div className="text-gray-light">
|
return followedByFriendsArray.map((a, index) => (
|
||||||
<span className="mr-1">
|
|
||||||
<FormattedMessage defaultMessage="Followed by" id="6mr8WU" />
|
|
||||||
</span>
|
|
||||||
{followedByFriendsArray.map((a, index) => (
|
|
||||||
<Fragment key={a}>
|
<Fragment key={a}>
|
||||||
<ProfileLink pubkey={a} className="link inline">
|
<ProfileLink pubkey={a} className="link inline">
|
||||||
<DisplayName user={undefined} pubkey={a} />
|
<DisplayName user={undefined} pubkey={a} />
|
||||||
</ProfileLink>
|
</ProfileLink>
|
||||||
{index < followedByFriendsArray.length - 1 && ","}{" "}
|
{index < followedByFriendsArray.length - 1 && ","}{" "}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
));
|
||||||
{followedByFriends.size > MAX_FOLLOWED_BY_FRIENDS && (
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<FollowDistanceIndicator className="p-2" pubkey={pubkey} />
|
||||||
|
{renderFollowedByFriends()}
|
||||||
|
</div>
|
||||||
|
{totalFollowedByFriends > 0 && (
|
||||||
|
<div className="text-gray-light">
|
||||||
|
<span className="mr-1">
|
||||||
|
<FormattedMessage defaultMessage="Followed by" id="6mr8WU" />
|
||||||
|
</span>
|
||||||
|
{renderFollowedByFriendsLinks()}
|
||||||
|
{totalFollowedByFriends > MAX_FOLLOWED_BY_FRIENDS && (
|
||||||
<span>
|
<span>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="and {count} others you follow"
|
defaultMessage="and {count} others you follow"
|
||||||
id="CYkOCI"
|
id="CYkOCI"
|
||||||
values={{ count: followedByFriends.size - MAX_FOLLOWED_BY_FRIENDS }}
|
values={{ count: totalFollowedByFriends - MAX_FOLLOWED_BY_FRIENDS }}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{followedByFriends.size === 0 && (
|
{followDistance > 3 && (
|
||||||
<div className="text-gray-light">
|
<div className="text-gray-light">
|
||||||
<FormattedMessage defaultMessage="Not followed by anyone you follow" id="IgsWFG" />
|
<FormattedMessage defaultMessage="Not followed by anyone you follow" id="IgsWFG" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{followDistance === 3 && (
|
||||||
|
<div className="text-gray-light">
|
||||||
|
<FormattedMessage defaultMessage="Followed by friends of friends" id="2oCF7O" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ const ErrorPage = () => {
|
|||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
<h5>{error.message}</h5>
|
<h5>{error.message}</h5>
|
||||||
<div className="my-2">{error.message}</div>
|
<div className="my-2">{error.message}</div>
|
||||||
<pre className="my-2 whitespace-pre-wrap">
|
<pre className="my-2 whitespace-pre-wrap">{error.stack}</pre>
|
||||||
{error.stack}
|
|
||||||
</pre>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -147,6 +147,9 @@
|
|||||||
"2mcwT8": {
|
"2mcwT8": {
|
||||||
"defaultMessage": "New Note"
|
"defaultMessage": "New Note"
|
||||||
},
|
},
|
||||||
|
"2oCF7O": {
|
||||||
|
"defaultMessage": "Followed by friends of friends"
|
||||||
|
},
|
||||||
"2ukA4d": {
|
"2ukA4d": {
|
||||||
"defaultMessage": "{n} hours"
|
"defaultMessage": "{n} hours"
|
||||||
},
|
},
|
||||||
|
@ -26,7 +26,7 @@ registerRoute(
|
|||||||
maxEntries: 200,
|
maxEntries: 200,
|
||||||
matchOptions: {
|
matchOptions: {
|
||||||
ignoreVary: true,
|
ignoreVary: true,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -51,7 +51,7 @@ registerRoute(
|
|||||||
maxEntries: 200,
|
maxEntries: 200,
|
||||||
matchOptions: {
|
matchOptions: {
|
||||||
ignoreVary: true,
|
ignoreVary: true,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
"2a2YiP": "{n} Bookmarks",
|
"2a2YiP": "{n} Bookmarks",
|
||||||
"2k0Cv+": "Dislikes ({n})",
|
"2k0Cv+": "Dislikes ({n})",
|
||||||
"2mcwT8": "New Note",
|
"2mcwT8": "New Note",
|
||||||
|
"2oCF7O": "Followed by friends of friends",
|
||||||
"2ukA4d": "{n} hours",
|
"2ukA4d": "{n} hours",
|
||||||
"2zJXeA": "Profiles",
|
"2zJXeA": "Profiles",
|
||||||
"39AHJm": "Sign Up",
|
"39AHJm": "Sign Up",
|
||||||
|
@ -19,9 +19,9 @@ export default defineConfig({
|
|||||||
type: "module",
|
type: "module",
|
||||||
},
|
},
|
||||||
workbox: {
|
workbox: {
|
||||||
globPatterns: ['**/*.{js,html,wasm,woff,woff2,ttf,svg,png,jpg,jpeg,webp,ico,json}'],
|
globPatterns: ["**/*.{js,html,wasm,woff,woff2,ttf,svg,png,jpg,jpeg,webp,ico,json}"],
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
visualizer({
|
visualizer({
|
||||||
open: true,
|
open: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user