Skip to content

Commit

Permalink
client-web: initial implementation of lists
Browse files Browse the repository at this point in the history
  • Loading branch information
franzos committed Sep 7, 2023
1 parent 90fcd2a commit c8370df
Show file tree
Hide file tree
Showing 13 changed files with 589 additions and 34 deletions.
17 changes: 13 additions & 4 deletions client-web/src/components/event.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ import { CreateEventForm } from "./create-event-form";
export interface EventProps extends ProcessedEvent {
userComponent?: JSX.Element;
level: number;
lists: {
id: string;
title: string;
}[];
}

export function Event({
Expand All @@ -51,6 +55,7 @@ export function Event({
replies,
eventRelayUrls,
level,
lists,
}: EventProps) {
const [isReady] = useNClient((state) => [
state.connected && state.keystore !== "none",
Expand Down Expand Up @@ -363,21 +368,21 @@ export function Event({
</CardHeader>
<CardBody p={4}>
<Box
padding={2}
background={"blackAlpha.100"}
borderRadius={4}
style={{ overflowWrap: "anywhere" }}
dangerouslySetInnerHTML={{
__html: makeLinksClickable(event.content),
}}
/>
<Text fontWeight="bold" fontSize={12} marginTop={2}>
{unixTimeToRelative(event.created_at)}
</Text>
</CardBody>
<CardFooter p={4}>
<HStack width="100%">
<ActionButtons />

<Spacer />

<Text fontSize={12}>{unixTimeToRelative(event.created_at)}</Text>
<Button
size={"sm"}
variant="outline"
Expand Down Expand Up @@ -429,6 +434,7 @@ export function Event({
relayUrls: eventRelayUrls,
reaction: r.event.content,
avatarSize: "xs",
lists: lists,
}}
/>
</Box>
Expand All @@ -451,6 +457,7 @@ export function Event({
showBanner: true,
relayUrls: eventRelayUrls,
avatarSize: "xs",
lists: lists,
}}
/>
<Icon as={RepeatIcon} />
Expand All @@ -474,6 +481,7 @@ export function Event({
showBanner: true,
relayUrls: eventRelayUrls,
avatarSize: "xs",
lists: lists,
}}
/>
</Box>
Expand All @@ -494,6 +502,7 @@ export function Event({
userComponent={userComponent}
eventRelayUrls={eventRelayUrls}
level={level + 1}
lists={lists}
/>
</Box>
);
Expand Down
10 changes: 9 additions & 1 deletion client-web/src/components/events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export function Events(props: {
view: string;
filters: NFilters;
connected: boolean;
lists?: {
id: string;
title: string;
}[];
}) {
const [events, maxEvents] = useNClient((state) => [
state.events,
Expand Down Expand Up @@ -85,7 +89,6 @@ export function Events(props: {
setMoreEventsCount(moreEventsCount + 1);
}
if (currentPage < totalPages) {
console.log("nextPage");
await nextPage();
return;
}
Expand Down Expand Up @@ -129,6 +132,8 @@ export function Events(props: {
options={{
showFollowing: true,
relayUrls: event.eventRelayUrls,
lists: props.lists || [],
showBlock: true,
}}
/>
) : (
Expand All @@ -138,12 +143,15 @@ export function Events(props: {
}}
options={{
relayUrls: event.eventRelayUrls,
lists: props.lists || [],
showBlock: true,
}}
/>
)
) : undefined
}
level={0}
lists={props.lists || []}
/>
</Box>
);
Expand Down
42 changes: 42 additions & 0 deletions client-web/src/components/feeds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export function EventsFeeds() {
]
);

const [lists, setLists] = useState<
{
id: string;
title: string;
}[]
>([]);

const [eventFilters, setEventFilters] = useState<NFilters>(defaultFilters);
const [activeView, setActiveView] = useState<string>("global");
const [isLoading, setIsLoading] = useState<boolean>(false);
Expand All @@ -33,6 +40,16 @@ export function EventsFeeds() {
reset: true,
});
setIsLoading(false);

const lists = await useNClient.getState().getAllLists();
if (lists) {
setLists(
lists.map((item) => ({
id: item.id,
title: item.title,
}))
);
}
};

/**
Expand Down Expand Up @@ -82,7 +99,24 @@ export function EventsFeeds() {
"#p": [keypair.publicKey],
});
setEventFilters(filters);
} else if (lists && lists.length > 0) {
const list = lists.find((item) => item.id === feedName);
if (list) {
const listRecord = await useNClient.getState().getList(list.id);
if (!listRecord || !listRecord.userPubkeys) return;
setActiveView(feedName);
filters = new NFilters({
kinds: [NEVENT_KIND.SHORT_TEXT_NOTE, NEVENT_KIND.LONG_FORM_CONTENT],
limit: MAX_EVENTS,
authors: listRecord?.userPubkeys,
});
setEventFilters(filters);
} else {
console.log("list not found");
return;
}
} else {
console.log("list not found");
return;
}
console.log(filters);
Expand All @@ -101,6 +135,13 @@ export function EventsFeeds() {
<Radio value="following">Following</Radio>
)}
{keypairIsLoaded && <Radio value="mentions">Mentions</Radio>}
{lists &&
lists.length > 0 &&
lists.map((list) => (
<Radio key={list.id} value={list.id}>
{list.title}
</Radio>
))}
</Stack>
</RadioGroup>
<Box overflowY="auto">
Expand All @@ -110,6 +151,7 @@ export function EventsFeeds() {
view="welcome"
filters={eventFilters}
connected={connected}
lists={lists}
/>
) : (
<Box marginTop={5} marginBottom={5} textAlign={"center"}>
Expand Down
111 changes: 97 additions & 14 deletions client-web/src/components/user-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import {
Image,
Text,
HStack,
Select,
useToast,
Modal,
ModalOverlay,
ModalHeader,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
useDisclosure,
} from "@chakra-ui/react";
import { Link } from "react-router-dom";
import { useNClient } from "../state/client";
Expand All @@ -19,8 +29,10 @@ export function UserInfo({
showBanner,
following,
showFollowing,
showBlock,
relayUrls,
isBlocked,
lists,
},
}: UserInfoProps) {
const name = data && data.name ? data.name : "Anonymous";
Expand All @@ -44,6 +56,69 @@ export function UserInfo({

const profileLink = `/p/${bech32ProfileLink}`;

const toast = useToast();

const { isOpen, onOpen, onClose } = useDisclosure();

const assignToList = async (listId: string) => {
if (listId === "0") return;
try {
await useNClient.getState().addUserToList(listId, pubkey);
toast({
title: "User added to list",
description: `User added to ${listId}`,
status: "success",
duration: 9000,
isClosable: true,
});
} catch (e) {
toast({
title: "Already added",
description: "User already on list",
status: "warning",
duration: 9000,
isClosable: true,
});
}
};

const SelectModal = (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Add to list</ModalHeader>
<ModalCloseButton />
<ModalBody>
{lists && lists.length > 0 ? (
<Select
onChange={(ev) => assignToList(ev.target.value)}
width={180}
>
<option key={"0"} value={"0"}>
{""}
</option>
{lists.map((list) => {
return (
<option key={list.id} value={list.id}>
{list.title}
</option>
);
})}
</Select>
) : (
<Text>No lists found. Create one first.</Text>
)}
</ModalBody>

<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);

return (
<>
{showBanner && banner && (
Expand All @@ -64,20 +139,27 @@ export function UserInfo({
<Text size="xs">{displayName}</Text>

<Spacer />
<Button
variant="outline"
size={"sm"}
onClick={() =>
isBlocked
? useNClient.getState().unblockUser(pubkey)
: useNClient.getState().blockUser({
pubkey: pubkey,
relayUrls,
})
}
>
{isBlocked ? "Unblock" : "Block"}
</Button>
{showBlock && (
<Button
variant="outline"
size={"sm"}
onClick={() =>
isBlocked
? useNClient.getState().unblockUser(pubkey)
: useNClient.getState().blockUser({
pubkey: pubkey,
relayUrls,
})
}
>
{isBlocked ? "Unblock" : "Block"}
</Button>
)}
{lists && lists.length > 0 && (
<Button variant="outline" size={"sm"} onClick={onOpen}>
Lists
</Button>
)}
{showFollowing && (
<Button
variant="outline"
Expand All @@ -99,6 +181,7 @@ export function UserInfo({
<Box overflowWrap="anywhere" mt={2}>
{showAbout && about && <Text fontSize="sm">{about}</Text>}
</Box>
{SelectModal}
</>
);
}
6 changes: 6 additions & 0 deletions client-web/src/layouts/primary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ export function PrimaryLayout() {
leftIcon={<Icon as={AccountMultipleIcon} marginRight={1} />}
/>

<MenuItem
label="Lists"
to="/lists"
leftIcon={<Icon as={AccountMultipleIcon} marginRight={1} />}
/>

{publicKey && publicKey !== "" && (
<MenuItem
label="Profile"
Expand Down
8 changes: 8 additions & 0 deletions client-web/src/lib/user-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ export interface UserOptions {
showBanner?: boolean;
following?: boolean;
showFollowing?: boolean;
/**
* Show block button
*/
showBlock?: boolean;
relayUrls: string[];
isBlocked?: boolean;
lists?: {
id: string;
title: string;
}[];

/**
* For pop-over
Expand Down
2 changes: 2 additions & 0 deletions client-web/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { RelaysRoute } from "./routes/relays.tsx";
import { PublishingQueueRoute } from "./routes/queue.tsx";
import { UserProfileRoute } from "./routes/user-profile.tsx";
import { BlockedUsersRoute } from "./routes/blocked.tsx";
import { ListsRoute } from "./routes/lists.tsx";
import "./index.css";
import theme from "./theme.ts";

Expand All @@ -38,6 +39,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<Route path="/following" element={<FollowingUsersRoute />} />
<Route path="/blocked" element={<BlockedUsersRoute />} />
<Route path="/subscriptions" element={<SubscriptionsRoute />} />
<Route path="/lists" element={<ListsRoute />} />
<Route path="/relays" element={<RelaysRoute />} />
<Route path="/queue" element={<PublishingQueueRoute />} />
<Route path="/p/:nprofile" element={<ProfileRoute />} />
Expand Down
Loading

0 comments on commit c8370df

Please sign in to comment.