feat: show rsshub error message
This commit is contained in:
parent
4213459ae0
commit
ed95fb6b23
|
|
@ -0,0 +1,45 @@
|
|||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipPortal,
|
||||
TooltipTrigger,
|
||||
} from "@follow/components/ui/tooltip/index.js"
|
||||
import dayjs from "dayjs"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
export function ErrorTooltip({
|
||||
errorAt,
|
||||
errorMessage,
|
||||
children,
|
||||
showWhenError = false,
|
||||
}: {
|
||||
errorMessage?: string | null
|
||||
errorAt?: string | null
|
||||
children: React.ReactNode
|
||||
showWhenError?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
if (!errorAt || !errorMessage) {
|
||||
return showWhenError ? children : null
|
||||
}
|
||||
return (
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>
|
||||
<div className="flex items-center gap-1">
|
||||
<i className="i-mgc-time-cute-re" />
|
||||
{t("feed_item.error_since")}{" "}
|
||||
{dayjs.duration(dayjs(errorAt).diff(dayjs(), "minute"), "minute").humanize(true)}
|
||||
</div>
|
||||
{!!errorMessage && (
|
||||
<div className="flex items-center gap-1">
|
||||
<i className="i-mgc-bug-cute-re" />
|
||||
{errorMessage}
|
||||
</div>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
|
@ -11,13 +11,13 @@ import { EllipsisHorizontalTextWithTooltip } from "@follow/components/ui/typogra
|
|||
import type { FeedViewType } from "@follow/constants"
|
||||
import { nextFrame } from "@follow/utils/dom"
|
||||
import { cn, isKeyForMultiSelectPressed } from "@follow/utils/utils"
|
||||
import dayjs from "dayjs"
|
||||
import { createElement, memo, use, useCallback, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { MenuItemSeparator, MenuItemText, useShowContextMenu } from "~/atoms/context-menu"
|
||||
import { getMainContainerElement } from "~/atoms/dom"
|
||||
import { useGeneralSettingKey } from "~/atoms/settings/general"
|
||||
import { ErrorTooltip } from "~/components/common/ErrorTooltip"
|
||||
import { useFeedActions, useInboxActions, useListActions } from "~/hooks/biz/useFeedActions"
|
||||
import { useFollow } from "~/hooks/biz/useFollow"
|
||||
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
|
||||
|
|
@ -187,29 +187,10 @@ const FeedItemImpl = ({ view, feedId, className, isPreview }: FeedItemProps) =>
|
|||
<div className={cn("flex min-w-0 items-center", isFeed && feed.errorAt && "text-red")}>
|
||||
<FeedIcon fallback feed={feed} size={16} />
|
||||
<FeedTitle feed={feed} />
|
||||
{isFeed && feed.errorAt && (
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger asChild>
|
||||
<i className="i-mgc-wifi-off-cute-re ml-1 shrink-0 text-base" />
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>
|
||||
<div className="flex items-center gap-1">
|
||||
<i className="i-mgc-time-cute-re" />
|
||||
{t("feed_item.error_since")}{" "}
|
||||
{dayjs
|
||||
.duration(dayjs(feed.errorAt).diff(dayjs(), "minute"), "minute")
|
||||
.humanize(true)}
|
||||
</div>
|
||||
{!!feed.errorMessage && (
|
||||
<div className="flex items-center gap-1">
|
||||
<i className="i-mgc-bug-cute-re" />
|
||||
{feed.errorMessage}
|
||||
</div>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
{isFeed && (
|
||||
<ErrorTooltip errorAt={feed.errorAt} errorMessage={feed.errorMessage}>
|
||||
<i className="i-mgc-wifi-off-cute-re ml-1 shrink-0 text-base" />
|
||||
</ErrorTooltip>
|
||||
)}
|
||||
{subscription?.isPrivate && (
|
||||
<Tooltip delayDuration={300}>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { useTranslation } from "react-i18next"
|
|||
|
||||
import RSSHubIconUrl from "~/assets/rsshub-icon.png?url"
|
||||
import { whoami } from "~/atoms/user"
|
||||
import { ErrorTooltip } from "~/components/common/ErrorTooltip"
|
||||
import { useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
import { useAuthQuery } from "~/hooks/common"
|
||||
import { useSubViewTitle } from "~/modules/app-layout/subview/hooks"
|
||||
|
|
@ -129,6 +130,13 @@ function List({ data }: { data?: RSSHubModel[] }) {
|
|||
return 1
|
||||
}
|
||||
|
||||
if (a.errorMessage && !b.errorMessage) {
|
||||
return 1
|
||||
}
|
||||
if (!a.errorMessage && b.errorMessage) {
|
||||
return -1
|
||||
}
|
||||
|
||||
const loadA = Math.min((a.userCount ?? 0) / (a.userLimit ?? Infinity), 1)
|
||||
const loadB = Math.min((b.userCount ?? 0) / (b.userLimit ?? Infinity), 1)
|
||||
|
||||
|
|
@ -191,31 +199,7 @@ function List({ data }: { data?: RSSHubModel[] }) {
|
|||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex w-max items-center gap-2">
|
||||
<Button
|
||||
buttonClassName="shrink-0"
|
||||
disabled={
|
||||
instance.userCount && instance.userLimit
|
||||
? instance.userCount >= instance.userLimit
|
||||
: false
|
||||
}
|
||||
variant={
|
||||
status?.data?.usage?.rsshubId === instance.id ? "outline" : "primary"
|
||||
}
|
||||
onClick={() => {
|
||||
present({
|
||||
title: t("rsshub.useModal.title"),
|
||||
content: ({ dismiss }) => (
|
||||
<SetModalContent dismiss={dismiss} instance={instance} />
|
||||
),
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
status?.data?.usage?.rsshubId === instance.id
|
||||
? "rsshub.table.inuse"
|
||||
: "rsshub.table.use",
|
||||
)}
|
||||
</Button>
|
||||
<SelectInstanceButton instance={instance} />
|
||||
{me?.id === instance.ownerUserId && (
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -256,3 +240,39 @@ function List({ data }: { data?: RSSHubModel[] }) {
|
|||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectInstanceButton({ instance }: { instance: RSSHubModel }) {
|
||||
const { t } = useTranslation("settings")
|
||||
const { present } = useModalStack()
|
||||
const status = useAuthQuery(Queries.rsshub.status())
|
||||
|
||||
const isNotAvailable = !!instance.errorMessage
|
||||
const limitReached =
|
||||
instance.userCount && instance.userLimit ? instance.userCount >= instance.userLimit : false
|
||||
|
||||
return (
|
||||
<ErrorTooltip errorAt={instance.errorAt} errorMessage={instance.errorMessage} showWhenError>
|
||||
<Button
|
||||
buttonClassName="shrink-0"
|
||||
disabled={isNotAvailable || limitReached}
|
||||
variant={status?.data?.usage?.rsshubId === instance.id ? "outline" : "primary"}
|
||||
onClick={() => {
|
||||
present({
|
||||
title: t("rsshub.useModal.title"),
|
||||
content: ({ dismiss }) => <SetModalContent dismiss={dismiss} instance={instance} />,
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
status?.data?.usage?.rsshubId === instance.id
|
||||
? "rsshub.table.inuse"
|
||||
: isNotAvailable
|
||||
? "rsshub.table.unavailable"
|
||||
: limitReached
|
||||
? "rsshub.table.limit_reached"
|
||||
: "rsshub.table.use",
|
||||
)}
|
||||
</Button>
|
||||
</ErrorTooltip>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"13004": "RSSHub user limit exceeded",
|
||||
"13005": "RSSHub purchase not found",
|
||||
"13006": "RSSHub config invalid",
|
||||
"13007": "This RSSHub instance is unavailable currently",
|
||||
"14000": "Invalid file",
|
||||
"14001": "File too large",
|
||||
"14002": "Upload failed"
|
||||
|
|
|
|||
|
|
@ -393,10 +393,12 @@
|
|||
"rsshub.table.description": "Description",
|
||||
"rsshub.table.edit": "Edit",
|
||||
"rsshub.table.inuse": "In Use",
|
||||
"rsshub.table.limit_reached": "Limit Reached",
|
||||
"rsshub.table.official": "Official",
|
||||
"rsshub.table.owner": "Owner",
|
||||
"rsshub.table.price": "Monthly Price",
|
||||
"rsshub.table.private": "Private",
|
||||
"rsshub.table.unavailable": "Unavailable",
|
||||
"rsshub.table.unlimited": "Unlimited",
|
||||
"rsshub.table.use": "Use",
|
||||
"rsshub.table.userCount": "User Count",
|
||||
|
|
|
|||
|
|
@ -5211,6 +5211,40 @@ declare const rsshub: drizzle_orm_pg_core.PgTableWithColumns<{
|
|||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
errorMessage: drizzle_orm_pg_core.PgColumn<{
|
||||
name: "error_message";
|
||||
tableName: "rsshub";
|
||||
dataType: "string";
|
||||
columnType: "PgText";
|
||||
data: string;
|
||||
driverParam: string;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: [string, ...string[]];
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
errorAt: drizzle_orm_pg_core.PgColumn<{
|
||||
name: "error_at";
|
||||
tableName: "rsshub";
|
||||
dataType: "date";
|
||||
columnType: "PgTimestamp";
|
||||
data: Date;
|
||||
driverParam: string;
|
||||
notNull: false;
|
||||
hasDefault: false;
|
||||
isPrimaryKey: false;
|
||||
isAutoincrement: false;
|
||||
hasRuntimeDefault: false;
|
||||
enumValues: undefined;
|
||||
baseColumn: never;
|
||||
identity: undefined;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
};
|
||||
dialect: "pg";
|
||||
}>;
|
||||
|
|
@ -5222,9 +5256,13 @@ declare const rsshubOpenAPISchema: zod.ZodObject<{
|
|||
price: zod.ZodNumber;
|
||||
description: zod.ZodNullable<zod.ZodString>;
|
||||
userLimit: zod.ZodNullable<zod.ZodNumber>;
|
||||
errorMessage: zod.ZodNullable<zod.ZodString>;
|
||||
errorAt: zod.ZodNullable<zod.ZodString>;
|
||||
}, zod.UnknownKeysParam, zod.ZodTypeAny, {
|
||||
id: string;
|
||||
description: string | null;
|
||||
errorMessage: string | null;
|
||||
errorAt: string | null;
|
||||
ownerUserId: string;
|
||||
baseUrl: string;
|
||||
accessKey: string | null;
|
||||
|
|
@ -5233,6 +5271,8 @@ declare const rsshubOpenAPISchema: zod.ZodObject<{
|
|||
}, {
|
||||
id: string;
|
||||
description: string | null;
|
||||
errorMessage: string | null;
|
||||
errorAt: string | null;
|
||||
ownerUserId: string;
|
||||
baseUrl: string;
|
||||
accessKey: string | null;
|
||||
|
|
@ -16844,6 +16884,8 @@ declare const _routes: hono_hono_base.HonoBase<Env, ({
|
|||
data: {
|
||||
id: string;
|
||||
description: string | null;
|
||||
errorMessage: string | null;
|
||||
errorAt: string | null;
|
||||
ownerUserId: string;
|
||||
owner: {
|
||||
id: string;
|
||||
|
|
@ -16910,6 +16952,8 @@ declare const _routes: hono_hono_base.HonoBase<Env, ({
|
|||
instance: {
|
||||
id: string;
|
||||
description: string | null;
|
||||
errorMessage: string | null;
|
||||
errorAt: string | null;
|
||||
ownerUserId: string;
|
||||
price: number;
|
||||
userLimit: number | null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue