Skip to content

Commit

Permalink
feat: lists feeds management
Browse files Browse the repository at this point in the history
  • Loading branch information
DIYgod committed Sep 20, 2024
1 parent 6e4316f commit 958d687
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 9 deletions.
18 changes: 12 additions & 6 deletions apps/renderer/src/components/feed-summary.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { WEB_URL } from "@follow/shared/constants"

import { FeedIcon } from "~/components/feed-icon"
import { cn } from "~/lib/utils"
import type { FeedModel } from "~/models"
import type { TargetModel } from "~/models"

import { FeedCertification } from "./feed-certification"
import { EllipsisHorizontalTextWithTooltip } from "./ui/typography"
Expand All @@ -10,14 +12,14 @@ export function FollowSummary({
docs,
className,
}: {
feed: FeedModel
feed: TargetModel
docs?: string
className?: string
}) {
return (
<div className={cn("flex select-text flex-col gap-2 text-sm", className)}>
<a
href={feed.siteUrl || void 0}
href={feed.type === "feed" ? feed.siteUrl || void 0 : `${WEB_URL}/list/${feed.id}`}
target="_blank"
className="flex items-center"
rel="noreferrer"
Expand All @@ -31,7 +33,7 @@ export function FollowSummary({
<div className="truncate text-base font-semibold leading-tight">
<div className="flex items-center">
{feed.title}
<FeedCertification className="center" feed={feed} />
{feed.type === "feed" && <FeedCertification className="center" feed={feed} />}
</div>
<EllipsisHorizontalTextWithTooltip className="truncate text-xs font-normal text-zinc-500">
{feed.description}
Expand All @@ -40,8 +42,12 @@ export function FollowSummary({
</a>
<div className="flex items-center gap-1 truncate text-zinc-500">
<i className="i-mgc-right-cute-re shrink-0" />
<a href={feed.url || docs} target="_blank" rel="noreferrer">
{feed.url || docs}
<a
href={feed.type === "feed" ? feed.url || docs : `${WEB_URL}/list/${feed.id}`}
target="_blank"
rel="noreferrer"
>
{feed.type === "feed" ? feed.url || docs : `/list/${feed.id}`}
</a>
</div>
</div>
Expand Down
130 changes: 128 additions & 2 deletions apps/renderer/src/modules/settings/tabs/lists.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { WEB_URL } from "@follow/shared/constants"
import { zodResolver } from "@hookform/resolvers/zod"
import { useMutation } from "@tanstack/react-query"
import { useState } from "react"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { toast } from "sonner"
import { z } from "zod"

import { FeedCertification } from "~/components/feed-certification"
import { Avatar, AvatarImage } from "~/components/ui/avatar"
import { Button } from "~/components/ui/button"
import { Card, CardHeader } from "~/components/ui/card"
Expand Down Expand Up @@ -37,7 +39,7 @@ import { apiClient } from "~/lib/api-fetch"
import { cn } from "~/lib/utils"
import type { ListModel } from "~/models"
import { Queries } from "~/queries"
import { useFeedById } from "~/store/feed"
import { feedActions, useFeedById } from "~/store/feed"

export const SettingLists = () => {
const { t: appT } = useTranslation()
Expand Down Expand Up @@ -75,9 +77,12 @@ export const SettingLists = () => {
<TableHead className="w-28 text-center" size="sm">
{t("lists.view")}
</TableHead>
<TableHead className="w-48 text-center" size="sm">
<TableHead className="w-20 text-center" size="sm">
{t("lists.fee")}
</TableHead>
<TableHead className="w-20 text-center" size="sm">
{t("lists.feeds")}
</TableHead>
<TableHead className="w-20 text-center" size="sm">
{t("lists.edit")}
</TableHead>
Expand Down Expand Up @@ -114,6 +119,19 @@ export const SettingLists = () => {
<i className="i-mgc-power shrink-0 text-lg text-accent" />
</div>
</TableCell>
<TableCell align="center" size="sm">
<Button
variant="ghost"
onClick={() => {
present({
title: t("lists.feeds.manage"),
content: () => <ListFeedsModalContent id={row.id} />,
})
}}
>
<i className="i-mgc-inbox-cute-re" />
</Button>
</TableCell>
<TableCell align="center" size="sm">
<Button
variant="ghost"
Expand Down Expand Up @@ -327,3 +345,111 @@ const ListCreationModalContent = ({ dismiss, id }: { dismiss: () => void; id?: s
</Form>
)
}

export const ListFeedsModalContent = ({ id }: { id: string }) => {
const list = useFeedById(id) as ListModel
const { t } = useTranslation("settings")

const [addValue, setAddValue] = useState("")
const addMutation = useMutation({
mutationFn: async (values: string) => {
const feed = await apiClient.lists.feeds.$post({
json: {
listId: id,
feedId: values,
},
})
feedActions.upsertMany([feed.data])
},
onSuccess: () => {
toast.success(t("lists.feeds.add.success"))
Queries.lists.list().invalidate()
},
async onError() {
toast.error(t("lists.feeds.add.error"))
},
})

const removeMutation = useMutation({
mutationFn: async (id: string) => {
await apiClient.lists.feeds.$delete({
json: {
listId: id,
feedId: id,
},
})
},
onSuccess: () => {
toast.success(t("lists.feeds.delete.success"))
Queries.lists.list().invalidate()
},
async onError() {
toast.error(t("lists.feeds.delete.error"))
},
})

return (
<div>
<div className="flex items-center gap-2">
<Input
placeholder={t("lists.feeds.id")}
value={addValue}
onChange={(e) => setAddValue(e.target.value)}
/>
<Button onClick={() => addMutation.mutate(addValue)}>{t("lists.feeds.add")}</Button>
</div>
<Divider className="mt-8" />
<ScrollArea.ScrollArea viewportClassName="max-h-[380px]">
<Table className="mt-4">
<TableHeader className="border-b">
<TableRow className="[&_*]:!font-semibold">
<TableHead className="w-20 text-center" size="sm">
{t("lists.feeds.id")}
</TableHead>
<TableHead className="text-center" size="sm">
{t("lists.feeds.title")}
</TableHead>
<TableHead className="w-20 text-center" size="sm">
{t("lists.feeds.owner")}
</TableHead>
<TableHead className="w-20 text-center" size="sm">
{t("lists.feeds.remove")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="border-t-[12px] border-transparent">
{list.feeds?.map((row) => (
<TableRow key={row.title} className="h-8">
<TableCell align="center" size="sm">
{row.id}
</TableCell>
<TableCell align="center" size="sm">
<a
target="_blank"
href={`${WEB_URL}/list/${row.id}`}
className="flex items-center justify-center gap-2 font-semibold"
>
{row.image && (
<Avatar className="size-6">
<AvatarImage src={row.image} />
</Avatar>
)}
<span className="inline-block max-w-[200px] truncate">{row.title}</span>
</a>
</TableCell>
<TableCell align="center" size="sm">
<FeedCertification className="ml-0" feed={row} />
</TableCell>
<TableCell align="center" size="sm">
<Button variant="ghost" onClick={() => removeMutation.mutate(row.id)}>
<i className="i-mgc-delete-2-cute-re" />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</ScrollArea.ScrollArea>
</div>
)
}
3 changes: 3 additions & 0 deletions apps/renderer/src/store/feed/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class FeedActions {
const nonce = feed["nonce"] || nanoid(8)
state.feeds[nonce] = { ...feed, id: nonce }
}
if ("feeds" in feed && feed.feeds) {
this.upsertMany(feed.feeds)
}
}
}),
)
Expand Down
1 change: 1 addition & 0 deletions icons/mgc/inbox_cute_re.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion locales/settings/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,19 @@
"lists.edit": "Edit",
"lists.edit.error": "Failed to edit list.",
"lists.edit.success": "List edited successfully!",
"lists.fee": "Subscription Fee",
"lists.fee": "Fee",
"lists.fee.description": "The fee others need to pay you to subscribe to this list.",
"lists.feeds": "Feeds",
"lists.feeds.add": "Add",
"lists.feeds.add.error": "Failed to add feed to list.",
"lists.feeds.add.success": "Feed added to list.",
"lists.feeds.delete.error": "Failed to remove feed from list.",
"lists.feeds.delete.success": "Feed removed from list.",
"lists.feeds.id": "Feed ID",
"lists.feeds.manage": "Manage Feeds",
"lists.feeds.owner": "Owner",
"lists.feeds.remove": "Remove",
"lists.feeds.title": "Title",
"lists.image": "Image",
"lists.info": "Lists are collections of feeds that you can share or sell for others to subscribe to. Subscribers will synchronize and access all feeds in the list.",
"lists.noLists": "No lists",
Expand Down
56 changes: 56 additions & 0 deletions packages/shared/src/hono.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3110,6 +3110,62 @@ declare const _routes: hono_hono_base.HonoBase<Env, {
status: 200;
};
};
"/lists/feeds": {
$post: {
input: {
json: {
feedId: string;
listId: string;
};
};
output: {
code: 0;
data: {
type: "feed";
id: string;
url: string;
description?: string | null | undefined;
title?: string | null | undefined;
image?: string | null | undefined;
siteUrl?: string | null | undefined;
errorMessage?: string | null | undefined;
errorAt?: string | null | undefined;
ownerUserId?: string | null | undefined;
owner?: {
name: string | null;
id: string;
emailVerified: string | null;
image: string | null;
handle: string | null;
createdAt: string;
} | null | undefined;
tipUsers?: {
name: string | null;
id: string;
emailVerified: string | null;
image: string | null;
handle: string | null;
createdAt: string;
}[] | null | undefined;
};
};
outputFormat: "json" | "text";
status: 200;
};
$delete: {
input: {
json: {
feedId: string;
listId: string;
};
};
output: {
code: 0;
};
outputFormat: "json" | "text";
status: 200;
};
};
} & {
"/wallets/transactions/tip": {
$post: {
Expand Down

0 comments on commit 958d687

Please sign in to comment.