show lists the user is in

This commit is contained in:
hzrd149 2023-06-13 08:54:42 -05:00
parent 63474a7413
commit 66a52c6ef1
5 changed files with 92 additions and 48 deletions

View File

@ -1,3 +1,4 @@
import { useCallback, useState } from "react";
import {
Button,
ButtonProps,
@ -9,30 +10,69 @@ import {
MenuGroup,
MenuOptionGroup,
MenuDivider,
useToast,
} from "@chakra-ui/react";
import { useCurrentAccount } from "../hooks/use-current-account";
import useSubject from "../hooks/use-subject";
import clientFollowingService from "../services/client-following";
import { useUserContacts } from "../hooks/use-user-contacts";
import "../services/lists";
import { ArrowDownSIcon, FollowIcon, PlusCircleIcon, TrashIcon, UnfollowIcon } from "./icons";
import { ArrowDownSIcon, FollowIcon, PlusCircleIcon, UnfollowIcon } from "./icons";
import useUserLists from "../hooks/use-user-lists";
import { useReadRelayUrls } from "../hooks/use-client-relays";
import { useAdditionalRelayContext } from "../providers/additional-relay-context";
function UsersLists() {
function UsersLists({ pubkey }: { pubkey: string }) {
const toast = useToast();
const account = useCurrentAccount()!;
const [isLoading, setLoading] = useState(false);
const readRelays = useReadRelayUrls(useAdditionalRelayContext());
const lists = useUserLists(account.pubkey, readRelays);
const listsArray = Array.from(Object.values(lists));
const inLists = listsArray.filter((list) => list.people.value.some((p) => p.pubkey === pubkey));
const handleChange = useCallback(async (names: string | string[]) => {
if (!Array.isArray(names)) return;
setLoading(true);
try {
const addToList = listsArray.find((list) => !inLists.includes(list) && names.includes(list.name));
const removeFromList = listsArray.find((list) => inLists.includes(list) && !names.includes(list.name));
if (addToList) {
const draft = addToList.draftAddPerson(pubkey);
console.log(draft);
} else if (removeFromList) {
const draft = removeFromList.draftRemovePerson(pubkey);
console.log(draft);
}
} catch (e) {
if (e instanceof Error) {
toast({ description: e.message });
}
}
setLoading(false);
}, []);
return (
<>
{Array.from(Object.entries(lists)).map(([name, list]) => (
<MenuItem isDisabled={account.readonly} isTruncated maxW="90vw">
{name}
</MenuItem>
))}
{listsArray.length > 0 && (
<MenuOptionGroup title="Lists" type="checkbox" value={inLists.map((l) => l.name)} onChange={handleChange}>
{listsArray.map((list) => (
<MenuItemOption
key={list.event.id}
value={list.name}
isDisabled={account.readonly && isLoading}
isTruncated
maxW="90vw"
>
{list.name}
</MenuItemOption>
))}
</MenuOptionGroup>
)}
</>
);
}
@ -53,7 +93,7 @@ export const UserFollowButton = ({
const followLabel = account && isFollowingMe ? "Follow Back" : "Follow";
return (
<Menu>
<Menu closeOnSelect={false}>
<MenuButton
as={Button}
colorScheme="brand"
@ -83,15 +123,12 @@ export const UserFollowButton = ({
)}
{account && (
<>
<MenuItem icon={<TrashIcon />} isDisabled={account.readonly}>
Remove from all
</MenuItem>
<MenuDivider />
<UsersLists />
<MenuDivider />
<UsersLists pubkey={pubkey} />
{/* <MenuDivider />
<MenuItem icon={<PlusCircleIcon />} isDisabled={account.readonly}>
New list
</MenuItem>
</MenuItem> */}
</>
)}
</MenuList>

View File

@ -61,6 +61,17 @@ export class List {
return draft;
}
draftRemovePerson(pubkey: string) {
const draft: DraftNostrEvent = {
created_at: dayjs().unix(),
kind: this.event.kind,
content: this.event.content,
tags: this.event.tags.filter((t) => t[0] !== "p" || t[1] !== pubkey),
};
return draft;
}
}
class ListsService {

View File

@ -1,5 +1,5 @@
import React from "react";
import { useOutletContext } from "react-router-dom";
import { useOutletContext, Link as RouterLink } from "react-router-dom";
import dayjs from "dayjs";
import {
Accordion,
@ -40,6 +40,10 @@ import { readablizeSats } from "../../helpers/bolt11";
import { UserAvatar } from "../../components/user-avatar";
import { useIsMobile } from "../../hooks/use-is-mobile";
import { getUserDisplayName } from "../../helpers/user-metadata";
import { ChatIcon } from "@chakra-ui/icons";
import { UserFollowButton } from "../../components/user-follow-button";
import { UserTipButton } from "../../components/user-tip-button";
import { UserProfileMenu } from "./components/user-profile-menu";
function buildDescriptionContent(description: string) {
let content: EmbedableContent = [description.trim()];
@ -105,6 +109,20 @@ export default function UserAboutTab() {
<Heading>{getUserDisplayName(metadata, pubkey)}</Heading>
<UserDnsIdentityIcon pubkey={pubkey} />
</Box>
<Flex gap="2" ml="auto">
<UserTipButton pubkey={pubkey} size="sm" variant="link" />
<IconButton
as={RouterLink}
size="sm"
icon={<ChatIcon />}
aria-label="Message"
to={`/dm/${npub ?? pubkey}`}
/>
<UserFollowButton pubkey={pubkey} size="sm" />
<UserProfileMenu pubkey={pubkey} aria-label="More Options" size="sm" />
</Flex>
</Flex>
<IconButton
icon={expanded.isOpen ? <ArrowUpSIcon /> : <ArrowDownSIcon />}
@ -170,9 +188,7 @@ export default function UserAboutTab() {
<Stat>
<StatLabel>Following</StatLabel>
<StatNumber>{contacts ? readablizeSats(contacts.contacts.length) : "Unknown"}</StatNumber>
{contacts && (
<StatHelpText>Updated {dayjs.unix(contacts.created_at).fromNow()}</StatHelpText>
)}
{contacts && <StatHelpText>Updated {dayjs.unix(contacts.created_at).fromNow()}</StatHelpText>}
</Stat>
{stats && (

View File

@ -1,21 +1,13 @@
import { Flex, Heading, SkeletonText, Text, Link, IconButton, Spacer } from "@chakra-ui/react";
import { useNavigate, Link as RouterLink } from "react-router-dom";
import { CopyIconButton } from "../../../components/copy-icon-button";
import { ChatIcon, EditIcon, ExternalLinkIcon, KeyIcon, SettingsIcon } from "../../../components/icons";
import { QrIconButton } from "./share-qr-button";
import { Flex, Heading, IconButton, Spacer } from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { EditIcon } from "../../../components/icons";
import { UserAvatar } from "../../../components/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import { UserFollowButton } from "../../../components/user-follow-button";
import { UserTipButton } from "../../../components/user-tip-button";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
import { truncatedId } from "../../../helpers/nostr-event";
import { fixWebsiteUrl, getUserDisplayName } from "../../../helpers/user-metadata";
import { getUserDisplayName } from "../../../helpers/user-metadata";
import { useCurrentAccount } from "../../../hooks/use-current-account";
import { useIsMobile } from "../../../hooks/use-is-mobile";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import { UserProfileMenu } from "./user-profile-menu";
import { embedUrls } from "../../../helpers/embeds";
import { renderGenericUrl } from "../../../components/embed-types";
export default function Header({
pubkey,
@ -27,7 +19,6 @@ export default function Header({
const isMobile = useIsMobile();
const navigate = useNavigate();
const metadata = useUserMetadata(pubkey);
const npub = normalizeToBech32(pubkey, Bech32Prefix.Pubkey);
const account = useCurrentAccount();
const isSelf = pubkey === account?.pubkey;
@ -48,19 +39,6 @@ export default function Header({
onClick={() => navigate("/profile")}
/>
)}
{!isSelf && (
<>
<UserTipButton pubkey={pubkey} size="sm" variant="link" />
<IconButton
as={RouterLink}
size="sm"
icon={<ChatIcon />}
aria-label="Message"
to={`/dm/${npub ?? pubkey}`}
/>
<UserFollowButton pubkey={pubkey} size="sm" />
</>
)}
<UserProfileMenu
pubkey={pubkey}
aria-label="More Options"

View File

@ -17,7 +17,7 @@ export const UserProfileMenu = ({
pubkey,
showRelaySelectionModal,
...props
}: { pubkey: string; showRelaySelectionModal: () => void } & Omit<MenuIconButtonProps, "children">) => {
}: { pubkey: string; showRelaySelectionModal?: () => void } & Omit<MenuIconButtonProps, "children">) => {
const metadata = useUserMetadata(pubkey);
const userRelays = useUserRelays(pubkey);
const infoModal = useDisclosure();
@ -52,9 +52,11 @@ export const UserProfileMenu = ({
<MenuItem onClick={infoModal.onOpen} icon={<CodeIcon />}>
View Raw
</MenuItem>
<MenuItem icon={<RelayIcon />} onClick={showRelaySelectionModal}>
Relay selection
</MenuItem>
{showRelaySelectionModal && (
<MenuItem icon={<RelayIcon />} onClick={showRelaySelectionModal}>
Relay selection
</MenuItem>
)}
</MenuIconButton>
{infoModal.isOpen && (
<UserDebugModal pubkey={pubkey} isOpen={infoModal.isOpen} onClose={infoModal.onClose} size="6xl" />