feature: add subscription manage

This commit is contained in:
master 2025-04-30 01:59:14 +08:00
parent 9fdb778330
commit 4301f1dbab
128 changed files with 2286 additions and 740 deletions

2
Cargo.lock generated
View File

@ -250,6 +250,7 @@ dependencies = [
"async-stream",
"async-trait",
"base64 0.22.1",
"bigdecimal",
"bytes",
"chrono",
"fast_chemail",
@ -273,6 +274,7 @@ dependencies = [
"static_assertions_next",
"tempfile",
"thiserror 1.0.69",
"time",
]
[[package]]

View File

@ -5,6 +5,7 @@
}
```
^https://konobangu.com/api*** reqHeaders://{x-forwarded.json} http://127.0.0.1:5001/api$1
#^https://konobangu.com/api*** statusCode://500
^https://konobangu.com/api*** reqHeaders://{x-forwarded.json} http://127.0.0.1:5001/api$1
^https://konobangu.com/*** reqHeaders://{x-forwarded.json} http://127.0.0.1:5000/$1 excludeFilter://^https://konobangu.com/api***
^wss://konobangu.com/*** reqHeaders://{x-forwarded.json} ws://127.0.0.1:5000/$1 excludeFilter://^wss://konobangu.com/api

View File

@ -72,7 +72,15 @@ jwt-authorizer = "0.15.0"
log = "0.4"
async-graphql = { version = "7", features = [] }
async-graphql-axum = "7"
seaography = { version = "1.1" }
seaography = { version = "1.1", features = [
"with-json",
"with-chrono",
"with-time",
"with-uuid",
"with-decimal",
"with-bigdecimal",
"with-postgres-array",
] }
base64 = "0.22.1"
tower = "0.5.2"
tower-http = { version = "0.6", features = [

View File

@ -366,7 +366,10 @@ impl AuthServiceTrait for OidcAuthService {
}) => crate::models::auth::Model::create_from_oidc(ctx, sub.to_string()).await,
r => r,
}
.map_err(|_| AuthError::FindAuthRecordError)?;
.map_err(|e| {
tracing::error!("Error finding auth record: {:?}", e);
AuthError::FindAuthRecordError
})?;
Ok(AuthUserInfo {
subscriber_auth,

View File

@ -2,7 +2,10 @@ use std::collections::HashSet;
use async_trait::async_trait;
use sea_orm::{DeriveIden, Statement};
use sea_orm_migration::prelude::{extension::postgres::IntoTypeRef, *};
use sea_orm_migration::{
prelude::{extension::postgres::IntoTypeRef, *},
schema::timestamp_with_time_zone,
};
use crate::migrations::extension::postgres::Type;
@ -144,6 +147,17 @@ macro_rules! create_postgres_enum_for_active_enum {
};
}
pub fn timestamps_z(t: TableCreateStatement) -> TableCreateStatement {
let mut t = t;
t.col(timestamp_with_time_zone(GeneralIds::CreatedAt).default(Expr::current_timestamp()))
.col(timestamp_with_time_zone(GeneralIds::UpdatedAt).default(Expr::current_timestamp()))
.take()
}
pub fn table_auto_z<T: IntoIden + 'static>(name: T) -> TableCreateStatement {
timestamps_z(Table::create().table(name).if_not_exists().take())
}
#[async_trait]
pub trait CustomSchemaManagerExt {
async fn create_postgres_auto_update_ts_fn(&self, col_name: &str) -> Result<(), DbErr>;

View File

@ -3,7 +3,7 @@ use sea_orm_migration::{prelude::*, schema::*};
use super::defs::{
Bangumi, CustomSchemaManagerExt, Episodes, GeneralIds, Subscribers, SubscriptionBangumi,
SubscriptionEpisode, Subscriptions,
SubscriptionEpisode, Subscriptions, table_auto_z,
};
use crate::models::{
subscribers::SEED_SUBSCRIBER,
@ -22,7 +22,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Subscribers::Table)
table_auto_z(Subscribers::Table)
.col(pk_auto(Subscribers::Id))
.col(string(Subscribers::DisplayName))
.col(json_binary_null(Subscribers::BangumiConf))
@ -57,7 +57,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Subscriptions::Table)
table_auto_z(Subscriptions::Table)
.col(pk_auto(Subscriptions::Id))
.col(string(Subscriptions::DisplayName))
.col(integer(Subscriptions::SubscriberId))
@ -89,7 +89,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Bangumi::Table)
table_auto_z(Bangumi::Table)
.col(pk_auto(Bangumi::Id))
.col(text_null(Bangumi::MikanBangumiId))
.col(integer(Bangumi::SubscriberId))
@ -156,7 +156,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(SubscriptionBangumi::Table)
table_auto_z(SubscriptionBangumi::Table)
.col(pk_auto(SubscriptionBangumi::Id))
.col(integer(SubscriptionBangumi::SubscriberId))
.col(integer(SubscriptionBangumi::SubscriptionId))
@ -206,7 +206,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Episodes::Table)
table_auto_z(Episodes::Table)
.col(pk_auto(Episodes::Id))
.col(text_null(Episodes::MikanEpisodeId))
.col(text(Episodes::RawName))
@ -275,7 +275,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(SubscriptionEpisode::Table)
table_auto_z(SubscriptionEpisode::Table)
.col(pk_auto(SubscriptionEpisode::Id))
.col(integer(SubscriptionEpisode::SubscriptionId))
.col(integer(SubscriptionEpisode::EpisodeId))

View File

@ -23,7 +23,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Downloaders::Table)
table_auto_z(Downloaders::Table)
.col(pk_auto(Downloaders::Id))
.col(text(Downloaders::Endpoint))
.col(string_null(Downloaders::Username))
@ -78,7 +78,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Downloads::Table)
table_auto_z(Downloads::Table)
.col(pk_auto(Downloads::Id))
.col(string(Downloads::RawName))
.col(string(Downloads::DisplayName))

View File

@ -1,5 +1,6 @@
use sea_orm_migration::{prelude::*, schema::*};
use super::defs::table_auto_z;
use crate::{
migrations::defs::{CustomSchemaManagerExt, Downloaders, GeneralIds, Subscribers},
models::downloaders::{DownloaderCategory, DownloaderCategoryEnum},
@ -20,7 +21,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Downloaders::Table)
table_auto_z(Downloaders::Table)
.col(pk_auto(Downloaders::Id))
.col(text(Downloaders::Endpoint))
.col(string_null(Downloaders::Username))

View File

@ -1,7 +1,7 @@
use async_trait::async_trait;
use sea_orm_migration::{prelude::*, schema::*};
use super::defs::Auth;
use super::defs::{Auth, table_auto_z};
use crate::{
migrations::defs::{CustomSchemaManagerExt, GeneralIds, Subscribers},
models::{
@ -26,7 +26,7 @@ impl MigrationTrait for Migration {
manager
.create_table(
table_auto(Auth::Table)
table_auto_z(Auth::Table)
.col(pk_auto(Auth::Id))
.col(text(Auth::Pid))
.col(enumeration(

View File

@ -24,9 +24,9 @@ pub enum AuthType {
#[sea_orm(table_name = "auth")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]

View File

@ -30,9 +30,9 @@ pub struct BangumiExtra {
#[sea_orm(table_name = "bangumi")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
pub mikan_bangumi_id: Option<String>,

View File

@ -23,9 +23,9 @@ pub enum DownloaderCategory {
#[sea_orm(table_name = "downloaders")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
pub category: DownloaderCategory,

View File

@ -39,9 +39,9 @@ pub enum DownloadMime {
#[sea_orm(table_name = "downloads")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
pub raw_name: String,

View File

@ -28,9 +28,9 @@ pub struct EpisodeExtra {
#[sea_orm(table_name = "episodes")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(indexed)]

View File

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use crate::{
app::AppContextTrait,
errors::app_error::{RecorderResult, RecorderError},
errors::app_error::{RecorderError, RecorderResult},
};
pub const SEED_SUBSCRIBER: &str = "konobangu";
@ -21,9 +21,9 @@ pub struct SubscriberBangumiConfig {
#[sea_orm(table_name = "subscribers")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
pub display_name: String,

View File

@ -44,9 +44,9 @@ pub enum SubscriptionCategory {
#[sea_orm(table_name = "subscriptions")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTime,
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTime,
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
pub display_name: String,

View File

@ -10,11 +10,11 @@
"cssVariables": true
},
"aliases": {
"components": "@/views/components",
"utils": "@/views/utils",
"ui": "@/views/components/ui",
"lib": "@/views/lib",
"hooks": "@/views/hooks"
"components": "@/components",
"utils": "@/presentation/utils",
"ui": "@/components/ui",
"lib": "@/presentation/lib",
"hooks": "@/presentation/hooks"
},
"iconLibrary": "lucide"
}

View File

@ -49,6 +49,7 @@
"@rsbuild/plugin-react": "^1.2.0",
"@tanstack/react-query": "^5.75.6",
"@tanstack/react-router": "^1.112.13",
"@tanstack/react-table": "^8.21.3",
"@tanstack/router-devtools": "^1.112.13",
"arktype": "^2.1.6",
"chart.js": "^4.4.8",
@ -84,6 +85,7 @@
"@graphql-codegen/client-preset": "^4.8.1",
"@graphql-codegen/typescript": "^4.1.6",
"@graphql-typed-document-node/core": "^3.2.0",
"@parcel/watcher": "^2.5.1",
"@rsbuild/core": "^1.2.15",
"@tailwindcss/postcss": "^4.0.9",
"@tanstack/react-router": "^1.112.0",

View File

@ -2,9 +2,9 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from '@/views/components/ui/sidebar';
} from '@/components/ui/sidebar';
import { Image } from '@/views/components/ui/image';
import { Image } from '@/components/ui/image';
export function AppIcon() {
return (

View File

@ -1,6 +1,4 @@
import type { RouteStateDataOption } from '@/infra/routes/traits';
import type { RouteBreadcrumbItem } from '@/infra/routes/traits';
import { AppSidebar } from '@/views/components/layout/app-sidebar';
import { AppSidebar } from '@/components/layout/app-sidebar';
import {
Breadcrumb,
BreadcrumbItem,
@ -8,14 +6,16 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/views/components/ui/breadcrumb';
import { Separator } from '@/views/components/ui/separator';
} from '@/components/ui/breadcrumb';
import { Separator } from '@/components/ui/separator';
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from '@/views/components/ui/sidebar';
import { cn } from '@/views/utils';
} from '@/components/ui/sidebar';
import type { RouteStateDataOption } from '@/infra/routes/traits';
import type { RouteBreadcrumbItem } from '@/infra/routes/traits';
import { cn } from '@/presentation/utils';
import { useMatches } from '@tanstack/react-router';
import {
type DetailedHTMLProps,

View File

@ -1,11 +1,11 @@
import { AppNavMainData } from '@/infra/routes/nav';
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarRail,
} from '@/views/components/ui/sidebar';
} from '@/components/ui/sidebar';
import { AppNavMainData } from '@/infra/routes/nav';
import type { ComponentPropsWithoutRef } from 'react';
import { AppIcon } from './app-icon';
import { NavMain } from './nav-main';

View File

@ -6,7 +6,7 @@ import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '@/views/components/ui/collapsible';
} from '@/components/ui/collapsible';
import {
SidebarGroup,
SidebarGroupLabel,
@ -16,7 +16,7 @@ import {
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from '@/views/components/ui/sidebar';
} from '@/components/ui/sidebar';
import { useMatches } from '@tanstack/react-router';
import { ProLink, type ProLinkProps } from '../ui/pro-link';

View File

@ -16,7 +16,7 @@ import {
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/views/components/ui/dropdown-menu';
} from '@/components/ui/dropdown-menu';
import {
SidebarGroup,
SidebarGroupLabel,
@ -25,7 +25,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from '@/views/components/ui/sidebar';
} from '@/components/ui/sidebar';
import type { ComponentProps } from 'react';
export function NavProjects({

View File

@ -9,11 +9,7 @@ import {
Sparkles,
} from 'lucide-react';
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/views/components/ui/avatar';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import {
DropdownMenu,
DropdownMenuContent,
@ -22,13 +18,13 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/views/components/ui/dropdown-menu';
} from '@/components/ui/dropdown-menu';
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from '@/views/components/ui/sidebar';
} from '@/components/ui/sidebar';
export function NavUser({
user,

View File

@ -2,7 +2,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Accordion({
...props

View File

@ -3,8 +3,8 @@
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import * as React from "react";
import { buttonVariants } from "@/views/components/ui/button";
import { cn } from "@/views/utils";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/presentation/utils";
function AlertDialog({
...props

View File

@ -1,7 +1,7 @@
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",

View File

@ -3,7 +3,7 @@
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Avatar({
className,

View File

@ -2,7 +2,7 @@ import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
const badgeVariants = cva(
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",

View File

@ -2,7 +2,7 @@ import { Slot } from "@radix-ui/react-slot";
import { ChevronRight, MoreHorizontal } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;

View File

@ -2,7 +2,7 @@ import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",

View File

@ -2,8 +2,8 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
import * as React from "react";
import { DayPicker } from "react-day-picker";
import { buttonVariants } from "@/views/components/ui/button";
import { cn } from "@/views/utils";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/presentation/utils";
function Calendar({
className,

View File

@ -1,6 +1,6 @@
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (

View File

@ -6,8 +6,8 @@ import useEmblaCarousel, {
import { ArrowLeft, ArrowRight } from "lucide-react";
import * as React from "react";
import { Button } from "@/views/components/ui/button";
import { cn } from "@/views/utils";
import { Button } from "@/components/ui/button";
import { cn } from "@/presentation/utils";
type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;

View File

@ -1,7 +1,7 @@
import * as React from "react";
import * as RechartsPrimitive from "recharts";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const;

View File

@ -4,7 +4,7 @@ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { CheckIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Checkbox({
className,

View File

@ -10,8 +10,8 @@ import {
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/views/components/ui/dialog";
import { cn } from "@/views/utils";
} from "@/components/ui/dialog";
import { cn } from "@/presentation/utils";
function Command({
className,

View File

@ -4,7 +4,7 @@ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function ContextMenu({
...props

View File

@ -0,0 +1,67 @@
import { Column } from "@tanstack/react-table";
import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/presentation/utils";
import { HTMLAttributes } from "react";
interface DataTableColumnHeaderProps<TData, TValue>
extends HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>;
title: string;
}
export function DataTableColumnHeader<TData, TValue>({
column,
title,
className,
}: DataTableColumnHeaderProps<TData, TValue>) {
if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div>;
}
return (
<div className={cn("flex items-center space-x-2", className)}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="-ml-3 h-8 data-[state=open]:bg-accent"
>
<span>{title}</span>
{column.getIsSorted() === "desc" ? (
<ArrowDown />
) : column.getIsSorted() === "asc" ? (
<ArrowUp />
) : (
<ChevronsUpDown />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUp className="h-3.5 w-3.5 text-muted-foreground/70" />
Asc
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDown className="h-3.5 w-3.5 text-muted-foreground/70" />
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeOff className="h-3.5 w-3.5 text-muted-foreground/70" />
Hide
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}

View File

@ -0,0 +1,109 @@
import { Table } from "@tanstack/react-table";
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { useMemo } from "react";
interface DataTablePaginationProps<TData> {
table: Table<TData>;
showSelectedRowCount?: boolean;
}
export function DataTablePagination<TData>({
table,
showSelectedRowCount = false,
}: DataTablePaginationProps<TData>) {
const renderRowsPerPage = () => {
return (
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
};
return (
<div className="flex items-center justify-between px-2">
{showSelectedRowCount ? (
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
) : (
<div className="flex-1 text-sm items-center">{renderRowsPerPage()}</div>
)}
<div className="flex items-center space-x-6 lg:space-x-8">
{showSelectedRowCount && renderRowsPerPage()}
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<ChevronsLeft />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeft />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRight />
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<ChevronsRight />
</Button>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,70 @@
'use client';
import type { Row } from '@tanstack/react-table';
import { MoreHorizontal } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useMemo } from 'react';
interface DataTableRowActionsProps<DataView, Id> {
row: Row<DataView>;
getId: (row: Row<DataView>) => Id;
showDetail?: boolean;
showEdit?: boolean;
showDelete?: boolean;
onDetail?: (id: Id) => void;
onDelete?: (id: Id) => void;
onEdit?: (id: Id) => void;
}
export function DataTableRowActions<DataView, Id>({
row,
getId,
showDetail,
showDelete,
showEdit,
onDetail,
onDelete,
onEdit,
}: DataTableRowActionsProps<DataView, Id>) {
const id = useMemo(() => getId(row), [getId, row]);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
>
<MoreHorizontal />
<span className="sr-only">Open menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[160px]">
{showDetail && (
<DropdownMenuItem onClick={() => onDetail?.(id)}>
Detail
</DropdownMenuItem>
)}
{showEdit && (
<DropdownMenuItem onClick={() => onEdit?.(id)}>Edit</DropdownMenuItem>
)}
{(showDetail || showEdit) && showDelete && <DropdownMenuSeparator />}
{showDelete && (
<DropdownMenuItem onClick={() => onDelete?.(id)}>
Delete
<DropdownMenuShortcut></DropdownMenuShortcut>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -0,0 +1,59 @@
"use client";
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
import { Table } from "@tanstack/react-table";
import { Settings2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
interface DataTableViewOptionsProps<TData> {
table: Table<TData>;
}
export function DataTableViewOptions<TData>({
table,
}: DataTableViewOptionsProps<TData>) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
>
<Settings2 />
Columns
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[150px]">
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== "undefined" && column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -2,7 +2,7 @@ import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Dialog({
...props

View File

@ -1,7 +1,7 @@
import * as React from "react";
import { Drawer as DrawerPrimitive } from "vaul";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Drawer({
...props

View File

@ -4,7 +4,7 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function DropdownMenu({
...props

View File

@ -11,8 +11,8 @@ import {
useFormState,
} from "react-hook-form";
import { Label } from "@/views/components/ui/label";
import { cn } from "@/views/utils";
import { Label } from "@/components/ui/label";
import { cn } from "@/presentation/utils";
const Form = FormProvider;

View File

@ -1,7 +1,7 @@
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function HoverCard({
...props

View File

@ -4,7 +4,7 @@ import { OTPInput, OTPInputContext } from "input-otp";
import { MinusIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function InputOTP({
className,

View File

@ -1,6 +1,6 @@
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (

View File

@ -3,7 +3,7 @@
import * as LabelPrimitive from "@radix-ui/react-label";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Label({
className,

View File

@ -2,7 +2,7 @@ import * as MenubarPrimitive from "@radix-ui/react-menubar";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Menubar({
className,

View File

@ -3,7 +3,7 @@ import { cva } from "class-variance-authority";
import { ChevronDownIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function NavigationMenu({
className,

View File

@ -5,8 +5,8 @@ import {
} from "lucide-react";
import * as React from "react";
import { Button, buttonVariants } from "@/views/components/ui/button";
import { cn } from "@/views/utils";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/presentation/utils";
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return (

View File

@ -3,7 +3,7 @@
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Popover({
...props

View File

@ -1,4 +1,4 @@
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
import type { FC, HTMLAttributes } from "react";
type Size = "xs" | "sm" | "md" | "lg" | "xl";

View File

@ -1,7 +1,7 @@
import * as ProgressPrimitive from "@radix-ui/react-progress";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Progress({
className,

View File

@ -0,0 +1,34 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { AlertCircle } from "lucide-react";
export interface QueryErrorViewProps {
title?: string;
message: string;
onRetry?: () => void;
}
export function QueryErrorView({
title = "Error",
message,
onRetry,
}: QueryErrorViewProps) {
return (
<div className="container mx-auto flex h-[50vh] items-center justify-center">
<div className="w-full max-w-md">
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>{title}</AlertTitle>
<AlertDescription>{message}</AlertDescription>
{onRetry && (
<div className="mt-4">
<Button variant="outline" onClick={() => onRetry()}>
Retry
</Button>
</div>
)}
</Alert>
</div>
</div>
);
}

View File

@ -4,7 +4,7 @@ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { CircleIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function RadioGroup({
className,

View File

@ -2,7 +2,7 @@ import { GripVerticalIcon } from "lucide-react";
import * as React from "react";
import * as ResizablePrimitive from "react-resizable-panels";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function ResizablePanelGroup({
className,

View File

@ -3,7 +3,7 @@
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function ScrollArea({
className,

View File

@ -2,7 +2,7 @@ import * as SelectPrimitive from "@radix-ui/react-select";
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Select({
...props

View File

@ -3,7 +3,7 @@
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Separator({
className,

View File

@ -2,7 +2,7 @@ import * as SheetPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot="sheet" {...props} />;

View File

@ -5,25 +5,25 @@ import { VariantProps, cva } from "class-variance-authority";
import { PanelLeftIcon } from "lucide-react";
import * as React from "react";
import { Button } from "@/views/components/ui/button";
import { Input } from "@/views/components/ui/input";
import { Separator } from "@/views/components/ui/separator";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from "@/views/components/ui/sheet";
import { Skeleton } from "@/views/components/ui/skeleton";
} from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/views/components/ui/tooltip";
import { useIsMobile } from "@/views/hooks/use-mobile";
import { cn } from "@/views/utils";
} from "@/components/ui/tooltip";
import { useIsMobile } from "@/presentation/hooks/use-mobile";
import { cn } from "@/presentation/utils";
const SIDEBAR_COOKIE_NAME = "sidebar_state";
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;

View File

@ -1,4 +1,4 @@
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
import { ComponentProps } from "react";
function Skeleton({ className, ...props }: ComponentProps<"div">) {

View File

@ -3,7 +3,7 @@
import * as SliderPrimitive from "@radix-ui/react-slider";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Slider({
className,

View File

@ -1,4 +1,4 @@
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
import {
Loader,
LoaderCircle,

View File

@ -3,7 +3,7 @@
import * as SwitchPrimitive from "@radix-ui/react-switch";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Switch({
className,

View File

@ -1,6 +1,6 @@
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Table({ className, ...props }: React.ComponentProps<"table">) {
return (

View File

@ -3,7 +3,7 @@
import * as TabsPrimitive from "@radix-ui/react-tabs";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Tabs({
className,

View File

@ -1,6 +1,6 @@
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (

View File

@ -4,8 +4,8 @@ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
import { type VariantProps } from "class-variance-authority";
import * as React from "react";
import { toggleVariants } from "@/views/components/ui/toggle";
import { cn } from "@/views/utils";
import { toggleVariants } from "@/components/ui/toggle";
import { cn } from "@/presentation/utils";
const ToggleGroupContext = React.createContext<
VariantProps<typeof toggleVariants>

View File

@ -2,7 +2,7 @@ import * as TogglePrimitive from "@radix-ui/react-toggle";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
const toggleVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",

View File

@ -1,7 +1,7 @@
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";
import { cn } from "@/views/utils";
import { cn } from "@/presentation/utils";
function TooltipProvider({
delayDuration = 0,

View File

@ -1,15 +1,13 @@
/* eslint-disable */
import type {
DocumentTypeDecoration,
ResultOf,
TypedDocumentNode,
} from '@graphql-typed-document-node/core';
import type { FragmentDefinitionNode } from 'graphql';
import type { Incremental } from './graphql';
import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import { FragmentDefinitionNode } from 'graphql';
import { Incremental } from './graphql';
export type FragmentType<
TDocumentType extends DocumentTypeDecoration<any, any>,
> = TDocumentType extends DocumentTypeDecoration<infer TType, any>
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
infer TType,
any
>
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
? TKey extends string
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
@ -35,10 +33,7 @@ export function useFragment<TType>(
// return nullable if `fragmentType` is nullable or undefined
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| FragmentType<DocumentTypeDecoration<TType, any>>
| null
| undefined
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
): TType | null | undefined;
// return array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
@ -48,10 +43,7 @@ export function useFragment<TType>(
// return array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| Array<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): Array<TType> | null | undefined;
// return readonly array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
@ -61,50 +53,35 @@ export function useFragment<TType>(
// return readonly array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): ReadonlyArray<TType> | null | undefined;
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| FragmentType<DocumentTypeDecoration<TType, any>>
| Array<FragmentType<DocumentTypeDecoration<TType, any>>>
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
return fragmentType as any;
}
export function makeFragmentData<
F extends DocumentTypeDecoration<any, any>,
FT extends ResultOf<F>,
FT extends ResultOf<F>
>(data: FT, _fragment: F): FragmentType<F> {
return data as FragmentType<F>;
}
export function isFragmentReady<TQuery, TFrag>(
queryNode: DocumentTypeDecoration<TQuery, any>,
fragmentNode: TypedDocumentNode<TFrag>,
data:
| FragmentType<TypedDocumentNode<Incremental<TFrag>, any>>
| null
| undefined
data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
): data is FragmentType<typeof fragmentNode> {
const deferredFields = (
queryNode as {
__meta__?: { deferredFields: Record<string, (keyof TFrag)[]> };
}
).__meta__?.deferredFields;
const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
?.deferredFields;
if (!deferredFields) return true;
const fragDef = fragmentNode.definitions[0] as
| FragmentDefinitionNode
| undefined;
const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
const fragName = fragDef?.name?.value;
const fields = (fragName && deferredFields[fragName]) || [];
return fields.length > 0 && fields.every((field) => data && field in data);
return fields.length > 0 && fields.every(field => data && field in data);
}

View File

@ -1,6 +1,6 @@
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
/* eslint-disable */
import * as types from './graphql';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
/**
* Map of all GraphQL operations in the project.
@ -14,14 +14,18 @@ import * as types from './graphql';
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
*/
type Documents = {
'\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n': typeof types.CreateSubscriptionDocument;
'\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n': typeof types.GetSubscriptionsDocument;
"\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetSubscriptionsDocument,
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument,
"\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n": typeof types.DeleteSubscriptionsDocument,
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument,
"\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n": typeof types.CreateSubscriptionDocument,
};
const documents: Documents = {
'\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n':
types.CreateSubscriptionDocument,
'\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n':
types.GetSubscriptionsDocument,
"\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetSubscriptionsDocument,
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument,
"\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n": types.DeleteSubscriptionsDocument,
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument,
"\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n": types.CreateSubscriptionDocument,
};
/**
@ -41,19 +45,26 @@ export function gql(source: string): unknown;
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n'
): (typeof documents)['\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n'];
export function gql(source: "\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"): (typeof documents)["\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n'
): (typeof documents)['\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n'];
export function gql(source: "\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n"): (typeof documents)["\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(source: "\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n"): (typeof documents)["\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(source: "\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n"): (typeof documents)["\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n"];
export function gql(source: string) {
return (documents as any)[source] ?? {};
}
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> =
TDocumentNode extends DocumentNode<infer TType, any> ? TType : never;
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;

View File

@ -1,32 +1,19 @@
/* eslint-disable */
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K];
};
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]?: Maybe<T[SubKey]>;
};
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>;
};
export type MakeEmpty<
T extends { [key: string]: unknown },
K extends keyof T,
> = { [_ in K]?: never };
export type Incremental<T> =
| T
| {
[P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never;
};
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string };
String: { input: string; output: string };
Boolean: { input: boolean; output: boolean };
Int: { input: number; output: number };
Float: { input: number; output: number };
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
};
export type Bangumi = {
@ -53,18 +40,21 @@ export type Bangumi = {
updatedAt: Scalars['String']['output'];
};
export type BangumiEpisodeArgs = {
filters?: InputMaybe<EpisodesFilterInput>;
orderBy?: InputMaybe<EpisodesOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type BangumiSubscriptionArgs = {
filters?: InputMaybe<SubscriptionsFilterInput>;
orderBy?: InputMaybe<SubscriptionsOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type BangumiSubscriptionBangumiArgs = {
filters?: InputMaybe<SubscriptionBangumiFilterInput>;
orderBy?: InputMaybe<SubscriptionBangumiOrderInput>;
@ -203,7 +193,7 @@ export type CursorInput = {
export enum DownloadMimeEnum {
Applicationoctetstream = 'applicationoctetstream',
Applicationxbittorrent = 'applicationxbittorrent',
Applicationxbittorrent = 'applicationxbittorrent'
}
export type DownloadMimeEnumFilterInput = {
@ -225,7 +215,7 @@ export enum DownloadStatusEnum {
Downloading = 'downloading',
Failed = 'failed',
Paused = 'paused',
Pending = 'pending',
Pending = 'pending'
}
export type DownloadStatusEnumFilterInput = {
@ -243,7 +233,7 @@ export type DownloadStatusEnumFilterInput = {
export enum DownloaderCategoryEnum {
Dandanplay = 'dandanplay',
Qbittorrent = 'qbittorrent',
Qbittorrent = 'qbittorrent'
}
export type DownloaderCategoryEnumFilterInput = {
@ -274,6 +264,7 @@ export type Downloaders = {
username: Scalars['String']['output'];
};
export type DownloadersDownloadArgs = {
filters?: InputMaybe<DownloadsFilterInput>;
orderBy?: InputMaybe<DownloadsOrderInput>;
@ -510,18 +501,21 @@ export type Episodes = {
updatedAt: Scalars['String']['output'];
};
export type EpisodesDownloadArgs = {
filters?: InputMaybe<SubscriptionsFilterInput>;
orderBy?: InputMaybe<SubscriptionsOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type EpisodesSubscriptionArgs = {
filters?: InputMaybe<DownloadsFilterInput>;
orderBy?: InputMaybe<DownloadsOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type EpisodesSubscriptionEpisodeArgs = {
filters?: InputMaybe<SubscriptionEpisodeFilterInput>;
orderBy?: InputMaybe<SubscriptionEpisodeOrderInput>;
@ -702,120 +696,148 @@ export type Mutation = {
subscriptionsUpdate: Array<SubscriptionsBasic>;
};
export type MutationBangumiCreateBatchArgs = {
data: Array<BangumiInsertInput>;
};
export type MutationBangumiCreateOneArgs = {
data: BangumiInsertInput;
};
export type MutationBangumiDeleteArgs = {
filter?: InputMaybe<BangumiFilterInput>;
};
export type MutationBangumiUpdateArgs = {
data: BangumiUpdateInput;
filter?: InputMaybe<BangumiFilterInput>;
};
export type MutationDownloadersCreateBatchArgs = {
data: Array<DownloadersInsertInput>;
};
export type MutationDownloadersCreateOneArgs = {
data: DownloadersInsertInput;
};
export type MutationDownloadersDeleteArgs = {
filter?: InputMaybe<DownloadersFilterInput>;
};
export type MutationDownloadersUpdateArgs = {
data: DownloadersUpdateInput;
filter?: InputMaybe<DownloadersFilterInput>;
};
export type MutationDownloadsCreateBatchArgs = {
data: Array<DownloadsInsertInput>;
};
export type MutationDownloadsCreateOneArgs = {
data: DownloadsInsertInput;
};
export type MutationDownloadsDeleteArgs = {
filter?: InputMaybe<DownloadsFilterInput>;
};
export type MutationDownloadsUpdateArgs = {
data: DownloadsUpdateInput;
filter?: InputMaybe<DownloadsFilterInput>;
};
export type MutationEpisodesCreateBatchArgs = {
data: Array<EpisodesInsertInput>;
};
export type MutationEpisodesCreateOneArgs = {
data: EpisodesInsertInput;
};
export type MutationEpisodesDeleteArgs = {
filter?: InputMaybe<EpisodesFilterInput>;
};
export type MutationEpisodesUpdateArgs = {
data: EpisodesUpdateInput;
filter?: InputMaybe<EpisodesFilterInput>;
};
export type MutationSubscriptionBangumiCreateBatchArgs = {
data: Array<SubscriptionBangumiInsertInput>;
};
export type MutationSubscriptionBangumiCreateOneArgs = {
data: SubscriptionBangumiInsertInput;
};
export type MutationSubscriptionBangumiDeleteArgs = {
filter?: InputMaybe<SubscriptionBangumiFilterInput>;
};
export type MutationSubscriptionBangumiUpdateArgs = {
data: SubscriptionBangumiUpdateInput;
filter?: InputMaybe<SubscriptionBangumiFilterInput>;
};
export type MutationSubscriptionEpisodeCreateBatchArgs = {
data: Array<SubscriptionEpisodeInsertInput>;
};
export type MutationSubscriptionEpisodeCreateOneArgs = {
data: SubscriptionEpisodeInsertInput;
};
export type MutationSubscriptionEpisodeDeleteArgs = {
filter?: InputMaybe<SubscriptionEpisodeFilterInput>;
};
export type MutationSubscriptionEpisodeUpdateArgs = {
data: SubscriptionEpisodeUpdateInput;
filter?: InputMaybe<SubscriptionEpisodeFilterInput>;
};
export type MutationSubscriptionsCreateBatchArgs = {
data: Array<SubscriptionsInsertInput>;
};
export type MutationSubscriptionsCreateOneArgs = {
data: SubscriptionsInsertInput;
};
export type MutationSubscriptionsDeleteArgs = {
filter?: InputMaybe<SubscriptionsFilterInput>;
};
export type MutationSubscriptionsUpdateArgs = {
data: SubscriptionsUpdateInput;
filter?: InputMaybe<SubscriptionsFilterInput>;
@ -828,7 +850,7 @@ export type OffsetInput = {
export enum OrderByEnum {
Asc = 'ASC',
Desc = 'DESC',
Desc = 'DESC'
}
export type PageInfo = {
@ -871,52 +893,61 @@ export type Query = {
subscriptions: SubscriptionsConnection;
};
export type Query_Sea_Orm_Entity_MetadataArgs = {
table_name: Scalars['String']['input'];
};
export type QueryBangumiArgs = {
filters?: InputMaybe<BangumiFilterInput>;
orderBy?: InputMaybe<BangumiOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QueryDownloadersArgs = {
filters?: InputMaybe<DownloadersFilterInput>;
orderBy?: InputMaybe<DownloadersOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QueryDownloadsArgs = {
filters?: InputMaybe<DownloadsFilterInput>;
orderBy?: InputMaybe<DownloadsOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QueryEpisodesArgs = {
filters?: InputMaybe<EpisodesFilterInput>;
orderBy?: InputMaybe<EpisodesOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QuerySubscribersArgs = {
filters?: InputMaybe<SubscribersFilterInput>;
orderBy?: InputMaybe<SubscribersOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QuerySubscriptionBangumiArgs = {
filters?: InputMaybe<SubscriptionBangumiFilterInput>;
orderBy?: InputMaybe<SubscriptionBangumiOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QuerySubscriptionEpisodeArgs = {
filters?: InputMaybe<SubscriptionEpisodeFilterInput>;
orderBy?: InputMaybe<SubscriptionEpisodeOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type QuerySubscriptionsArgs = {
filters?: InputMaybe<SubscriptionsFilterInput>;
orderBy?: InputMaybe<SubscriptionsOrderInput>;
@ -959,24 +990,28 @@ export type Subscribers = {
updatedAt: Scalars['String']['output'];
};
export type SubscribersBangumiArgs = {
filters?: InputMaybe<BangumiFilterInput>;
orderBy?: InputMaybe<BangumiOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type SubscribersDownloaderArgs = {
filters?: InputMaybe<DownloadersFilterInput>;
orderBy?: InputMaybe<DownloadersOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type SubscribersEpisodeArgs = {
filters?: InputMaybe<EpisodesFilterInput>;
orderBy?: InputMaybe<EpisodesOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type SubscribersSubscriptionArgs = {
filters?: InputMaybe<SubscriptionsFilterInput>;
orderBy?: InputMaybe<SubscriptionsOrderInput>;
@ -1073,7 +1108,7 @@ export type SubscriptionBangumiUpdateInput = {
export enum SubscriptionCategoryEnum {
Manual = 'manual',
Mikan = 'mikan',
Mikan = 'mikan'
}
export type SubscriptionCategoryEnumFilterInput = {
@ -1166,24 +1201,28 @@ export type Subscriptions = {
updatedAt: Scalars['String']['output'];
};
export type SubscriptionsBangumiArgs = {
filters?: InputMaybe<BangumiFilterInput>;
orderBy?: InputMaybe<BangumiOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type SubscriptionsEpisodeArgs = {
filters?: InputMaybe<EpisodesFilterInput>;
orderBy?: InputMaybe<EpisodesOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type SubscriptionsSubscriptionBangumiArgs = {
filters?: InputMaybe<SubscriptionBangumiFilterInput>;
orderBy?: InputMaybe<SubscriptionBangumiOrderInput>;
pagination?: InputMaybe<PaginationInput>;
};
export type SubscriptionsSubscriptionEpisodeArgs = {
filters?: InputMaybe<SubscriptionEpisodeFilterInput>;
orderBy?: InputMaybe<SubscriptionEpisodeOrderInput>;
@ -1275,264 +1314,47 @@ export type TextFilterInput = {
not_between?: InputMaybe<Array<Scalars['String']['input']>>;
};
export type GetSubscriptionsQueryVariables = Exact<{
page: PageInput;
filters: SubscriptionsFilterInput;
orderBy: SubscriptionsOrderInput;
}>;
export type GetSubscriptionsQuery = { __typename?: 'Query', subscriptions: { __typename?: 'SubscriptionsConnection', nodes: Array<{ __typename?: 'Subscriptions', id: number, createdAt: string, updatedAt: string, displayName: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean }>, paginationInfo?: { __typename?: 'PaginationInfo', total: number, pages: number } | null } };
export type UpdateSubscriptionsMutationVariables = Exact<{
data: SubscriptionsUpdateInput;
filters: SubscriptionsFilterInput;
}>;
export type UpdateSubscriptionsMutation = { __typename?: 'Mutation', subscriptionsUpdate: Array<{ __typename?: 'SubscriptionsBasic', id: number, createdAt: string, updatedAt: string, displayName: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean }> };
export type DeleteSubscriptionsMutationVariables = Exact<{
filters?: InputMaybe<SubscriptionsFilterInput>;
}>;
export type DeleteSubscriptionsMutation = { __typename?: 'Mutation', subscriptionsDelete: number };
export type GetSubscriptionDetailQueryVariables = Exact<{
id: Scalars['Int']['input'];
}>;
export type GetSubscriptionDetailQuery = { __typename?: 'Query', subscriptions: { __typename?: 'SubscriptionsConnection', nodes: Array<{ __typename?: 'Subscriptions', id: number, displayName: string, createdAt: string, updatedAt: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean, bangumi: { __typename?: 'BangumiConnection', nodes: Array<{ __typename?: 'Bangumi', createdAt: string, updatedAt: string, id: number, mikanBangumiId?: string | null, displayName: string, rawName: string, season: number, seasonRaw?: string | null, fansub?: string | null, mikanFansubId?: string | null, rssLink?: string | null, posterLink?: string | null, savePath?: string | null, deleted: boolean, homepage?: string | null }> } }> } };
export type CreateSubscriptionMutationVariables = Exact<{
input: SubscriptionsInsertInput;
}>;
export type CreateSubscriptionMutation = {
__typename?: 'Mutation';
subscriptionsCreateOne: {
__typename?: 'SubscriptionsBasic';
id: number;
displayName: string;
sourceUrl: string;
enabled: boolean;
category: SubscriptionCategoryEnum;
subscriberId: number;
};
};
export type GetSubscriptionsQueryVariables = Exact<{
page: Scalars['Int']['input'];
pageSize: Scalars['Int']['input'];
}>;
export type CreateSubscriptionMutation = { __typename?: 'Mutation', subscriptionsCreateOne: { __typename?: 'SubscriptionsBasic', id: number, displayName: string, sourceUrl: string, enabled: boolean, category: SubscriptionCategoryEnum } };
export type GetSubscriptionsQuery = {
__typename?: 'Query';
subscriptions: {
__typename?: 'SubscriptionsConnection';
nodes: Array<{
__typename?: 'Subscriptions';
id: number;
displayName: string;
category: SubscriptionCategoryEnum;
enabled: boolean;
bangumi: {
__typename?: 'BangumiConnection';
nodes: Array<{
__typename?: 'Bangumi';
id: number;
displayName: string;
posterLink?: string | null;
season: number;
fansub?: string | null;
homepage?: string | null;
}>;
};
}>;
};
};
export const CreateSubscriptionDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'CreateSubscription' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'input' },
},
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: { kind: 'Name', value: 'SubscriptionsInsertInput' },
},
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'subscriptionsCreateOne' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'data' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'input' },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'displayName' } },
{ kind: 'Field', name: { kind: 'Name', value: 'sourceUrl' } },
{ kind: 'Field', name: { kind: 'Name', value: 'enabled' } },
{ kind: 'Field', name: { kind: 'Name', value: 'category' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'subscriberId' },
},
],
},
},
],
},
},
],
} as unknown as DocumentNode<
CreateSubscriptionMutation,
CreateSubscriptionMutationVariables
>;
export const GetSubscriptionsDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: { kind: 'Name', value: 'GetSubscriptions' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: { kind: 'Variable', name: { kind: 'Name', value: 'page' } },
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } },
},
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'pageSize' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'subscriptions' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'pagination' },
value: {
kind: 'ObjectValue',
fields: [
{
kind: 'ObjectField',
name: { kind: 'Name', value: 'page' },
value: {
kind: 'ObjectValue',
fields: [
{
kind: 'ObjectField',
name: { kind: 'Name', value: 'page' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'page' },
},
},
{
kind: 'ObjectField',
name: { kind: 'Name', value: 'limit' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'pageSize' },
},
},
],
},
},
],
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'nodes' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'displayName' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'category' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'enabled' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'bangumi' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'nodes' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'id' },
},
{
kind: 'Field',
name: {
kind: 'Name',
value: 'displayName',
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'posterLink' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'season' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'fansub' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'homepage' },
},
],
},
},
],
},
},
],
},
},
],
},
},
],
},
},
],
} as unknown as DocumentNode<
GetSubscriptionsQuery,
GetSubscriptionsQueryVariables
>;
export const GetSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsOrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionsQuery, GetSubscriptionsQueryVariables>;
export const UpdateSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsUpdateInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]} as unknown as DocumentNode<UpdateSubscriptionsMutation, UpdateSubscriptionsMutationVariables>;
export const DeleteSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}]}]}}]} as unknown as DocumentNode<DeleteSubscriptionsMutation, DeleteSubscriptionsMutationVariables>;
export const GetSubscriptionDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptionDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"bangumi"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mikanBangumiId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"rawName"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"seasonRaw"}},{"kind":"Field","name":{"kind":"Name","value":"fansub"}},{"kind":"Field","name":{"kind":"Name","value":"mikanFansubId"}},{"kind":"Field","name":{"kind":"Name","value":"rssLink"}},{"kind":"Field","name":{"kind":"Name","value":"posterLink"}},{"kind":"Field","name":{"kind":"Name","value":"savePath"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"homepage"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionDetailQuery, GetSubscriptionDetailQueryVariables>;
export const CreateSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsInsertInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsCreateOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"category"}}]}}]}}]} as unknown as DocumentNode<CreateSubscriptionMutation, CreateSubscriptionMutationVariables>;

View File

@ -1,2 +1,2 @@
export * from './fragment-masking';
export * from './gql';
export * from "./fragment-masking";
export * from "./gql";

View File

@ -0,0 +1,10 @@
import type { Injector } from '@outposts/injection-js';
import { IntlService } from './intl.service';
export function intlContextFromInjector(injector: Injector) {
const intlService = injector.get(IntlService);
return {
intlService,
};
}

View File

@ -0,0 +1,9 @@
import { useInjector } from 'oidc-client-rx/adapters/react';
import { useMemo } from 'react';
import { intlContextFromInjector } from './context';
export function useIntl() {
const injector = useInjector();
return useMemo(() => intlContextFromInjector(injector), [injector]);
}

View File

@ -0,0 +1,27 @@
import { inject } from '@outposts/injection-js';
import { DOCUMENT } from '../platform/injection';
export class IntlService {
document = inject(DOCUMENT);
formatTimestamp(timestamp: number, options?: Intl.DateTimeFormatOptions) {
const defaultOptions: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
...options,
};
return new Intl.DateTimeFormat(
this.document.defaultView?.navigator.language,
{
...defaultOptions,
...options,
}
).format(new Date(timestamp));
}
}

View File

@ -1,4 +1,4 @@
import type { ProLinkProps } from '@/views/components/ui/pro-link';
import type { ProLinkProps } from '@/components/ui/pro-link';
import type { Injector } from '@outposts/injection-js';
import type { LucideIcon } from 'lucide-react';

View File

@ -1,5 +1,5 @@
import { guardRouteIndexAsNotFound } from '@/components/layout/app-not-found';
import type { RouteStateDataOption } from '@/infra/routes/traits';
import { guardRouteIndexAsNotFound } from '@/views/components/layout/app-not-found';
import { Outlet } from '@tanstack/react-router';
export interface BuildVirtualBranchRouteOptions {

View File

@ -1,10 +1,10 @@
import '@abraham/reflection';
import { provideAuth, setupAuthContext } from '@/app/auth/context';
import { AppNotFoundComponent } from '@/components/layout/app-not-found';
import { providePlatform } from '@/infra/platform/context';
import { provideStorages } from '@/infra/storage/context';
import { provideStyles } from '@/infra/styles/context';
import { AppNotFoundComponent } from '@/views/components/layout/app-not-found';
import { routeTree } from '@/views/routeTree.gen';
import { routeTree } from '@/presentation/routeTree.gen';
import { type Injector, ReflectiveInjector } from '@outposts/injection-js';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import {

View File

@ -0,0 +1,40 @@
import { useEffect } from 'react';
import { useStateRef } from './use-state-ref.ts';
export interface UseDebouncedSkeletonProps {
minSkeletonDuration?: number;
loading?: boolean;
}
export function useDebouncedSkeleton({
minSkeletonDuration = 100,
loading,
}: UseDebouncedSkeletonProps) {
const [showSkeleton, setShowSkeleton, showSkeletonRef] = useStateRef(loading);
useEffect(() => {
if (loading && !showSkeleton) {
setShowSkeleton(true);
}
if (!loading && showSkeleton) {
const timeout = setTimeout(() => {
if (showSkeletonRef.current) {
setShowSkeleton(false);
}
}, minSkeletonDuration);
return () => {
clearTimeout(timeout);
};
}
}, [
loading,
showSkeleton,
setShowSkeleton,
minSkeletonDuration,
showSkeletonRef,
]);
return {
showSkeleton,
};
}

View File

@ -0,0 +1,18 @@
import { useCallback, useInsertionEffect, useRef } from 'react';
export function useEvent<
const T extends (
...args: // eslint-disable-next-line @typescript-eslint/no-explicit-any
any[]
) => void,
>(fn: T): T {
const ref = useRef<T | null>(fn);
useInsertionEffect(() => {
ref.current = fn;
}, [fn]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return useCallback((...args: any) => {
const latestFn = ref.current!;
return latestFn(...args);
}, []) as unknown as T;
}

View File

@ -0,0 +1,28 @@
import {
type Dispatch,
type RefObject,
type SetStateAction,
useCallback,
useRef,
useState,
} from 'react';
export function useStateRef<T>(
initialValue: T
): [T, Dispatch<SetStateAction<T>>, RefObject<T>] {
const [state, _setState] = useState(initialValue);
const ref = useRef(initialValue);
const setState = useCallback((value: T | ((prev: T) => T)) => {
let nextValue: T;
if (typeof value === 'function') {
nextValue = (value as (prev: T) => T)(ref.current);
} else {
nextValue = value;
}
ref.current = nextValue;
_setState(nextValue);
}, []);
return [state, setState, ref] as const;
}

View File

@ -0,0 +1,663 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
// Import Routes
import { Route as R404Import } from './routes/404.tsx';
import { Route as rootRoute } from './routes/__root.tsx';
import { Route as AppExploreExploreImport } from './routes/_app/_explore/explore.tsx';
import { Route as AppExploreFeedImport } from './routes/_app/_explore/feed.tsx';
import { Route as AppBangumiManageImport } from './routes/_app/bangumi/manage.tsx';
import { Route as AppBangumiRouteImport } from './routes/_app/bangumi/route.tsx';
import { Route as AppPlaygroundGraphqlApiImport } from './routes/_app/playground/graphql-api.tsx';
import { Route as AppPlaygroundRouteImport } from './routes/_app/playground/route.tsx';
import { Route as AppRouteImport } from './routes/_app/route.tsx';
import { Route as AppSettingsDownloaderImport } from './routes/_app/settings/downloader.tsx';
import { Route as AppSettingsRouteImport } from './routes/_app/settings/route.tsx';
import { Route as AppSubscriptionsCreateImport } from './routes/_app/subscriptions/create.tsx';
import { Route as AppSubscriptionsDetailSubscriptionIdImport } from './routes/_app/subscriptions/detail.$subscriptionId.tsx';
import { Route as AppSubscriptionsEditSubscriptionIdImport } from './routes/_app/subscriptions/edit.$subscriptionId.tsx';
import { Route as AppSubscriptionsManageImport } from './routes/_app/subscriptions/manage.tsx';
import { Route as AppSubscriptionsRouteImport } from './routes/_app/subscriptions/route.tsx';
import { Route as AboutImport } from './routes/about.tsx';
import { Route as AuthOidcCallbackImport } from './routes/auth/oidc/callback.tsx';
import { Route as AuthSignInImport } from './routes/auth/sign-in.tsx';
import { Route as AuthSignUpImport } from './routes/auth/sign-up.tsx';
import { Route as IndexImport } from './routes/index.tsx';
// Create/Update Routes
const AboutRoute = AboutImport.update({
id: '/about',
path: '/about',
getParentRoute: () => rootRoute,
} as any);
const R404Route = R404Import.update({
id: '/404',
path: '/404',
getParentRoute: () => rootRoute,
} as any);
const AppRouteRoute = AppRouteImport.update({
id: '/_app',
getParentRoute: () => rootRoute,
} as any);
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any);
const AuthSignUpRoute = AuthSignUpImport.update({
id: '/auth/sign-up',
path: '/auth/sign-up',
getParentRoute: () => rootRoute,
} as any);
const AuthSignInRoute = AuthSignInImport.update({
id: '/auth/sign-in',
path: '/auth/sign-in',
getParentRoute: () => rootRoute,
} as any);
const AppSubscriptionsRouteRoute = AppSubscriptionsRouteImport.update({
id: '/subscriptions',
path: '/subscriptions',
getParentRoute: () => AppRouteRoute,
} as any);
const AppSettingsRouteRoute = AppSettingsRouteImport.update({
id: '/settings',
path: '/settings',
getParentRoute: () => AppRouteRoute,
} as any);
const AppPlaygroundRouteRoute = AppPlaygroundRouteImport.update({
id: '/playground',
path: '/playground',
getParentRoute: () => AppRouteRoute,
} as any);
const AppBangumiRouteRoute = AppBangumiRouteImport.update({
id: '/bangumi',
path: '/bangumi',
getParentRoute: () => AppRouteRoute,
} as any);
const AuthOidcCallbackRoute = AuthOidcCallbackImport.update({
id: '/auth/oidc/callback',
path: '/auth/oidc/callback',
getParentRoute: () => rootRoute,
} as any);
const AppSubscriptionsManageRoute = AppSubscriptionsManageImport.update({
id: '/manage',
path: '/manage',
getParentRoute: () => AppSubscriptionsRouteRoute,
} as any);
const AppSubscriptionsCreateRoute = AppSubscriptionsCreateImport.update({
id: '/create',
path: '/create',
getParentRoute: () => AppSubscriptionsRouteRoute,
} as any);
const AppSettingsDownloaderRoute = AppSettingsDownloaderImport.update({
id: '/downloader',
path: '/downloader',
getParentRoute: () => AppSettingsRouteRoute,
} as any);
const AppPlaygroundGraphqlApiRoute = AppPlaygroundGraphqlApiImport.update({
id: '/graphql-api',
path: '/graphql-api',
getParentRoute: () => AppPlaygroundRouteRoute,
} as any).lazy(() =>
import('./routes/_app/playground/graphql-api.lazy.tsx').then((d) => d.Route)
);
const AppBangumiManageRoute = AppBangumiManageImport.update({
id: '/manage',
path: '/manage',
getParentRoute: () => AppBangumiRouteRoute,
} as any);
const AppExploreFeedRoute = AppExploreFeedImport.update({
id: '/_explore/feed',
path: '/feed',
getParentRoute: () => AppRouteRoute,
} as any);
const AppExploreExploreRoute = AppExploreExploreImport.update({
id: '/_explore/explore',
path: '/explore',
getParentRoute: () => AppRouteRoute,
} as any);
const AppSubscriptionsEditSubscriptionIdRoute =
AppSubscriptionsEditSubscriptionIdImport.update({
id: '/edit/$subscriptionId',
path: '/edit/$subscriptionId',
getParentRoute: () => AppSubscriptionsRouteRoute,
} as any);
const AppSubscriptionsDetailSubscriptionIdRoute =
AppSubscriptionsDetailSubscriptionIdImport.update({
id: '/detail/$subscriptionId',
path: '/detail/$subscriptionId',
getParentRoute: () => AppSubscriptionsRouteRoute,
} as any);
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/';
path: '/';
fullPath: '/';
preLoaderRoute: typeof IndexImport;
parentRoute: typeof rootRoute;
};
'/_app': {
id: '/_app';
path: '';
fullPath: '';
preLoaderRoute: typeof AppRouteImport;
parentRoute: typeof rootRoute;
};
'/404': {
id: '/404';
path: '/404';
fullPath: '/404';
preLoaderRoute: typeof R404Import;
parentRoute: typeof rootRoute;
};
'/about': {
id: '/about';
path: '/about';
fullPath: '/about';
preLoaderRoute: typeof AboutImport;
parentRoute: typeof rootRoute;
};
'/_app/bangumi': {
id: '/_app/bangumi';
path: '/bangumi';
fullPath: '/bangumi';
preLoaderRoute: typeof AppBangumiRouteImport;
parentRoute: typeof AppRouteImport;
};
'/_app/playground': {
id: '/_app/playground';
path: '/playground';
fullPath: '/playground';
preLoaderRoute: typeof AppPlaygroundRouteImport;
parentRoute: typeof AppRouteImport;
};
'/_app/settings': {
id: '/_app/settings';
path: '/settings';
fullPath: '/settings';
preLoaderRoute: typeof AppSettingsRouteImport;
parentRoute: typeof AppRouteImport;
};
'/_app/subscriptions': {
id: '/_app/subscriptions';
path: '/subscriptions';
fullPath: '/subscriptions';
preLoaderRoute: typeof AppSubscriptionsRouteImport;
parentRoute: typeof AppRouteImport;
};
'/auth/sign-in': {
id: '/auth/sign-in';
path: '/auth/sign-in';
fullPath: '/auth/sign-in';
preLoaderRoute: typeof AuthSignInImport;
parentRoute: typeof rootRoute;
};
'/auth/sign-up': {
id: '/auth/sign-up';
path: '/auth/sign-up';
fullPath: '/auth/sign-up';
preLoaderRoute: typeof AuthSignUpImport;
parentRoute: typeof rootRoute;
};
'/_app/_explore/explore': {
id: '/_app/_explore/explore';
path: '/explore';
fullPath: '/explore';
preLoaderRoute: typeof AppExploreExploreImport;
parentRoute: typeof AppRouteImport;
};
'/_app/_explore/feed': {
id: '/_app/_explore/feed';
path: '/feed';
fullPath: '/feed';
preLoaderRoute: typeof AppExploreFeedImport;
parentRoute: typeof AppRouteImport;
};
'/_app/bangumi/manage': {
id: '/_app/bangumi/manage';
path: '/manage';
fullPath: '/bangumi/manage';
preLoaderRoute: typeof AppBangumiManageImport;
parentRoute: typeof AppBangumiRouteImport;
};
'/_app/playground/graphql-api': {
id: '/_app/playground/graphql-api';
path: '/graphql-api';
fullPath: '/playground/graphql-api';
preLoaderRoute: typeof AppPlaygroundGraphqlApiImport;
parentRoute: typeof AppPlaygroundRouteImport;
};
'/_app/settings/downloader': {
id: '/_app/settings/downloader';
path: '/downloader';
fullPath: '/settings/downloader';
preLoaderRoute: typeof AppSettingsDownloaderImport;
parentRoute: typeof AppSettingsRouteImport;
};
'/_app/subscriptions/create': {
id: '/_app/subscriptions/create';
path: '/create';
fullPath: '/subscriptions/create';
preLoaderRoute: typeof AppSubscriptionsCreateImport;
parentRoute: typeof AppSubscriptionsRouteImport;
};
'/_app/subscriptions/manage': {
id: '/_app/subscriptions/manage';
path: '/manage';
fullPath: '/subscriptions/manage';
preLoaderRoute: typeof AppSubscriptionsManageImport;
parentRoute: typeof AppSubscriptionsRouteImport;
};
'/auth/oidc/callback': {
id: '/auth/oidc/callback';
path: '/auth/oidc/callback';
fullPath: '/auth/oidc/callback';
preLoaderRoute: typeof AuthOidcCallbackImport;
parentRoute: typeof rootRoute;
};
'/_app/subscriptions/detail/$subscriptionId': {
id: '/_app/subscriptions/detail/$subscriptionId';
path: '/detail/$subscriptionId';
fullPath: '/subscriptions/detail/$subscriptionId';
preLoaderRoute: typeof AppSubscriptionsDetailSubscriptionIdImport;
parentRoute: typeof AppSubscriptionsRouteImport;
};
'/_app/subscriptions/edit/$subscriptionId': {
id: '/_app/subscriptions/edit/$subscriptionId';
path: '/edit/$subscriptionId';
fullPath: '/subscriptions/edit/$subscriptionId';
preLoaderRoute: typeof AppSubscriptionsEditSubscriptionIdImport;
parentRoute: typeof AppSubscriptionsRouteImport;
};
}
}
// Create and export the route tree
interface AppBangumiRouteRouteChildren {
AppBangumiManageRoute: typeof AppBangumiManageRoute;
}
const AppBangumiRouteRouteChildren: AppBangumiRouteRouteChildren = {
AppBangumiManageRoute: AppBangumiManageRoute,
};
const AppBangumiRouteRouteWithChildren = AppBangumiRouteRoute._addFileChildren(
AppBangumiRouteRouteChildren
);
interface AppPlaygroundRouteRouteChildren {
AppPlaygroundGraphqlApiRoute: typeof AppPlaygroundGraphqlApiRoute;
}
const AppPlaygroundRouteRouteChildren: AppPlaygroundRouteRouteChildren = {
AppPlaygroundGraphqlApiRoute: AppPlaygroundGraphqlApiRoute,
};
const AppPlaygroundRouteRouteWithChildren =
AppPlaygroundRouteRoute._addFileChildren(AppPlaygroundRouteRouteChildren);
interface AppSettingsRouteRouteChildren {
AppSettingsDownloaderRoute: typeof AppSettingsDownloaderRoute;
}
const AppSettingsRouteRouteChildren: AppSettingsRouteRouteChildren = {
AppSettingsDownloaderRoute: AppSettingsDownloaderRoute,
};
const AppSettingsRouteRouteWithChildren =
AppSettingsRouteRoute._addFileChildren(AppSettingsRouteRouteChildren);
interface AppSubscriptionsRouteRouteChildren {
AppSubscriptionsCreateRoute: typeof AppSubscriptionsCreateRoute;
AppSubscriptionsManageRoute: typeof AppSubscriptionsManageRoute;
AppSubscriptionsDetailSubscriptionIdRoute: typeof AppSubscriptionsDetailSubscriptionIdRoute;
AppSubscriptionsEditSubscriptionIdRoute: typeof AppSubscriptionsEditSubscriptionIdRoute;
}
const AppSubscriptionsRouteRouteChildren: AppSubscriptionsRouteRouteChildren = {
AppSubscriptionsCreateRoute: AppSubscriptionsCreateRoute,
AppSubscriptionsManageRoute: AppSubscriptionsManageRoute,
AppSubscriptionsDetailSubscriptionIdRoute:
AppSubscriptionsDetailSubscriptionIdRoute,
AppSubscriptionsEditSubscriptionIdRoute:
AppSubscriptionsEditSubscriptionIdRoute,
};
const AppSubscriptionsRouteRouteWithChildren =
AppSubscriptionsRouteRoute._addFileChildren(
AppSubscriptionsRouteRouteChildren
);
interface AppRouteRouteChildren {
AppBangumiRouteRoute: typeof AppBangumiRouteRouteWithChildren;
AppPlaygroundRouteRoute: typeof AppPlaygroundRouteRouteWithChildren;
AppSettingsRouteRoute: typeof AppSettingsRouteRouteWithChildren;
AppSubscriptionsRouteRoute: typeof AppSubscriptionsRouteRouteWithChildren;
AppExploreExploreRoute: typeof AppExploreExploreRoute;
AppExploreFeedRoute: typeof AppExploreFeedRoute;
}
const AppRouteRouteChildren: AppRouteRouteChildren = {
AppBangumiRouteRoute: AppBangumiRouteRouteWithChildren,
AppPlaygroundRouteRoute: AppPlaygroundRouteRouteWithChildren,
AppSettingsRouteRoute: AppSettingsRouteRouteWithChildren,
AppSubscriptionsRouteRoute: AppSubscriptionsRouteRouteWithChildren,
AppExploreExploreRoute: AppExploreExploreRoute,
AppExploreFeedRoute: AppExploreFeedRoute,
};
const AppRouteRouteWithChildren = AppRouteRoute._addFileChildren(
AppRouteRouteChildren
);
export interface FileRoutesByFullPath {
'/': typeof IndexRoute;
'': typeof AppRouteRouteWithChildren;
'/404': typeof R404Route;
'/about': typeof AboutRoute;
'/bangumi': typeof AppBangumiRouteRouteWithChildren;
'/playground': typeof AppPlaygroundRouteRouteWithChildren;
'/settings': typeof AppSettingsRouteRouteWithChildren;
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren;
'/auth/sign-in': typeof AuthSignInRoute;
'/auth/sign-up': typeof AuthSignUpRoute;
'/explore': typeof AppExploreExploreRoute;
'/feed': typeof AppExploreFeedRoute;
'/bangumi/manage': typeof AppBangumiManageRoute;
'/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute;
'/settings/downloader': typeof AppSettingsDownloaderRoute;
'/subscriptions/create': typeof AppSubscriptionsCreateRoute;
'/subscriptions/manage': typeof AppSubscriptionsManageRoute;
'/auth/oidc/callback': typeof AuthOidcCallbackRoute;
'/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute;
'/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute;
}
export interface FileRoutesByTo {
'/': typeof IndexRoute;
'': typeof AppRouteRouteWithChildren;
'/404': typeof R404Route;
'/about': typeof AboutRoute;
'/bangumi': typeof AppBangumiRouteRouteWithChildren;
'/playground': typeof AppPlaygroundRouteRouteWithChildren;
'/settings': typeof AppSettingsRouteRouteWithChildren;
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren;
'/auth/sign-in': typeof AuthSignInRoute;
'/auth/sign-up': typeof AuthSignUpRoute;
'/explore': typeof AppExploreExploreRoute;
'/feed': typeof AppExploreFeedRoute;
'/bangumi/manage': typeof AppBangumiManageRoute;
'/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute;
'/settings/downloader': typeof AppSettingsDownloaderRoute;
'/subscriptions/create': typeof AppSubscriptionsCreateRoute;
'/subscriptions/manage': typeof AppSubscriptionsManageRoute;
'/auth/oidc/callback': typeof AuthOidcCallbackRoute;
'/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute;
'/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute;
}
export interface FileRoutesById {
__root__: typeof rootRoute;
'/': typeof IndexRoute;
'/_app': typeof AppRouteRouteWithChildren;
'/404': typeof R404Route;
'/about': typeof AboutRoute;
'/_app/bangumi': typeof AppBangumiRouteRouteWithChildren;
'/_app/playground': typeof AppPlaygroundRouteRouteWithChildren;
'/_app/settings': typeof AppSettingsRouteRouteWithChildren;
'/_app/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren;
'/auth/sign-in': typeof AuthSignInRoute;
'/auth/sign-up': typeof AuthSignUpRoute;
'/_app/_explore/explore': typeof AppExploreExploreRoute;
'/_app/_explore/feed': typeof AppExploreFeedRoute;
'/_app/bangumi/manage': typeof AppBangumiManageRoute;
'/_app/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute;
'/_app/settings/downloader': typeof AppSettingsDownloaderRoute;
'/_app/subscriptions/create': typeof AppSubscriptionsCreateRoute;
'/_app/subscriptions/manage': typeof AppSubscriptionsManageRoute;
'/auth/oidc/callback': typeof AuthOidcCallbackRoute;
'/_app/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute;
'/_app/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute;
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths:
| '/'
| ''
| '/404'
| '/about'
| '/bangumi'
| '/playground'
| '/settings'
| '/subscriptions'
| '/auth/sign-in'
| '/auth/sign-up'
| '/explore'
| '/feed'
| '/bangumi/manage'
| '/playground/graphql-api'
| '/settings/downloader'
| '/subscriptions/create'
| '/subscriptions/manage'
| '/auth/oidc/callback'
| '/subscriptions/detail/$subscriptionId'
| '/subscriptions/edit/$subscriptionId';
fileRoutesByTo: FileRoutesByTo;
to:
| '/'
| ''
| '/404'
| '/about'
| '/bangumi'
| '/playground'
| '/settings'
| '/subscriptions'
| '/auth/sign-in'
| '/auth/sign-up'
| '/explore'
| '/feed'
| '/bangumi/manage'
| '/playground/graphql-api'
| '/settings/downloader'
| '/subscriptions/create'
| '/subscriptions/manage'
| '/auth/oidc/callback'
| '/subscriptions/detail/$subscriptionId'
| '/subscriptions/edit/$subscriptionId';
id:
| '__root__'
| '/'
| '/_app'
| '/404'
| '/about'
| '/_app/bangumi'
| '/_app/playground'
| '/_app/settings'
| '/_app/subscriptions'
| '/auth/sign-in'
| '/auth/sign-up'
| '/_app/_explore/explore'
| '/_app/_explore/feed'
| '/_app/bangumi/manage'
| '/_app/playground/graphql-api'
| '/_app/settings/downloader'
| '/_app/subscriptions/create'
| '/_app/subscriptions/manage'
| '/auth/oidc/callback'
| '/_app/subscriptions/detail/$subscriptionId'
| '/_app/subscriptions/edit/$subscriptionId';
fileRoutesById: FileRoutesById;
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute;
AppRouteRoute: typeof AppRouteRouteWithChildren;
R404Route: typeof R404Route;
AboutRoute: typeof AboutRoute;
AuthSignInRoute: typeof AuthSignInRoute;
AuthSignUpRoute: typeof AuthSignUpRoute;
AuthOidcCallbackRoute: typeof AuthOidcCallbackRoute;
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AppRouteRoute: AppRouteRouteWithChildren,
R404Route: R404Route,
AboutRoute: AboutRoute,
AuthSignInRoute: AuthSignInRoute,
AuthSignUpRoute: AuthSignUpRoute,
AuthOidcCallbackRoute: AuthOidcCallbackRoute,
};
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>();
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/_app",
"/404",
"/about",
"/auth/sign-in",
"/auth/sign-up",
"/auth/oidc/callback"
]
},
"/": {
"filePath": "index.tsx"
},
"/_app": {
"filePath": "_app/route.tsx",
"children": [
"/_app/bangumi",
"/_app/playground",
"/_app/settings",
"/_app/subscriptions",
"/_app/_explore/explore",
"/_app/_explore/feed"
]
},
"/404": {
"filePath": "404.tsx"
},
"/about": {
"filePath": "about.tsx"
},
"/_app/bangumi": {
"filePath": "_app/bangumi/route.tsx",
"parent": "/_app",
"children": [
"/_app/bangumi/manage"
]
},
"/_app/playground": {
"filePath": "_app/playground/route.tsx",
"parent": "/_app",
"children": [
"/_app/playground/graphql-api"
]
},
"/_app/settings": {
"filePath": "_app/settings/route.tsx",
"parent": "/_app",
"children": [
"/_app/settings/downloader"
]
},
"/_app/subscriptions": {
"filePath": "_app/subscriptions/route.tsx",
"parent": "/_app",
"children": [
"/_app/subscriptions/create",
"/_app/subscriptions/manage",
"/_app/subscriptions/detail/$subscriptionId",
"/_app/subscriptions/edit/$subscriptionId"
]
},
"/auth/sign-in": {
"filePath": "auth/sign-in.tsx"
},
"/auth/sign-up": {
"filePath": "auth/sign-up.tsx"
},
"/_app/_explore/explore": {
"filePath": "_app/_explore/explore.tsx",
"parent": "/_app"
},
"/_app/_explore/feed": {
"filePath": "_app/_explore/feed.tsx",
"parent": "/_app"
},
"/_app/bangumi/manage": {
"filePath": "_app/bangumi/manage.tsx",
"parent": "/_app/bangumi"
},
"/_app/playground/graphql-api": {
"filePath": "_app/playground/graphql-api.tsx",
"parent": "/_app/playground"
},
"/_app/settings/downloader": {
"filePath": "_app/settings/downloader.tsx",
"parent": "/_app/settings"
},
"/_app/subscriptions/create": {
"filePath": "_app/subscriptions/create.tsx",
"parent": "/_app/subscriptions"
},
"/_app/subscriptions/manage": {
"filePath": "_app/subscriptions/manage.tsx",
"parent": "/_app/subscriptions"
},
"/auth/oidc/callback": {
"filePath": "auth/oidc/callback.tsx"
},
"/_app/subscriptions/detail/$subscriptionId": {
"filePath": "_app/subscriptions/detail.$subscriptionId.tsx",
"parent": "/_app/subscriptions"
},
"/_app/subscriptions/edit/$subscriptionId": {
"filePath": "_app/subscriptions/edit.$subscriptionId.tsx",
"parent": "/_app/subscriptions"
}
}
}
ROUTE_MANIFEST_END */

View File

@ -1,4 +1,4 @@
import { AppNotFoundComponent } from '@/views/components/layout/app-not-found';
import { AppNotFoundComponent } from '@/components/layout/app-not-found';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/404')({

View File

@ -4,9 +4,20 @@ import type {
} from '@/infra/routes/traits';
import { Outlet, createRootRouteWithContext } from '@tanstack/react-router';
import { Home } from 'lucide-react';
import { memo } from 'react';
import { Toaster } from 'sonner';
export const RootRouteComponent = memo(() => {
return (
<>
<Outlet />
<Toaster position="top-right" />
</>
);
});
export const Route = createRootRouteWithContext<RouterContext>()({
component: Outlet,
component: RootRouteComponent,
staticData: {
breadcrumb: {
icon: Home,

Some files were not shown because too many files have changed in this diff Show More