followed by foafs

This commit is contained in:
Martti Malmi 2024-01-10 15:40:40 +02:00
parent 062212f311
commit 35d7ec4685
8 changed files with 57 additions and 36 deletions

View File

@ -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";

View File

@ -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(),

View File

@ -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 followedByFriends = socialGraphInstance.followedByFriends(pubkey); const followDistance = socialGraphInstance.getFollowDistance(pubkey);
const followedByFriendsArray = Array.from(followedByFriends).slice(0, MAX_FOLLOWED_BY_FRIENDS); const { followedByFriendsArray, totalFollowedByFriends } = useMemo(() => {
const followedByFriends = socialGraphInstance.followedByFriends(pubkey);
return {
followedByFriendsArray: Array.from(followedByFriends).slice(0, MAX_FOLLOWED_BY_FRIENDS),
totalFollowedByFriends: followedByFriends.size,
};
}, [pubkey, followDistance]);
const renderFollowedByFriends = () => {
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} />
</div>
));
};
const renderFollowedByFriendsLinks = () => {
return followedByFriendsArray.map((a, index) => (
<Fragment key={a}>
<ProfileLink pubkey={a} className="link inline">
<DisplayName user={undefined} pubkey={a} />
</ProfileLink>
{index < followedByFriendsArray.length - 1 && ","}{" "}
</Fragment>
));
};
return ( return (
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<FollowDistanceIndicator className="p-2" pubkey={pubkey} /> <FollowDistanceIndicator className="p-2" pubkey={pubkey} />
{followedByFriendsArray.map((a, index) => { {renderFollowedByFriends()}
const zIndex = followedByFriendsArray.length - index;
return (
<div className={`inline-block ${index > 0 ? "-ml-5" : ""}`} key={a} style={{ zIndex }}>
<ProfileImage showFollowDistance={false} pubkey={a} size={24} showUsername={false} />
</div>
);
})}
</div> </div>
{followedByFriends.size > 0 && ( {totalFollowedByFriends > 0 && (
<div className="text-gray-light"> <div className="text-gray-light">
<span className="mr-1"> <span className="mr-1">
<FormattedMessage defaultMessage="Followed by" id="6mr8WU" /> <FormattedMessage defaultMessage="Followed by" id="6mr8WU" />
</span> </span>
{followedByFriendsArray.map((a, index) => ( {renderFollowedByFriendsLinks()}
<Fragment key={a}> {totalFollowedByFriends > MAX_FOLLOWED_BY_FRIENDS && (
<ProfileLink pubkey={a} className="link inline">
<DisplayName user={undefined} pubkey={a} />
</ProfileLink>
{index < followedByFriendsArray.length - 1 && ","}{" "}
</Fragment>
))}
{followedByFriends.size > 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>
); );
} }

View File

@ -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>
); );
}; };

View File

@ -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"
}, },

View File

@ -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,
} },
}), }),
], ],
}), }),

View File

@ -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",

View File

@ -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,