refactor: split list store from feed store (#654)
* init Signed-off-by: Innei <i@innei.in> * update Signed-off-by: Innei <i@innei.in> * fix: type Signed-off-by: Innei <i@innei.in> * chore: auto-fix linting and formatting issues * refactor: list Signed-off-by: Innei <i@innei.in> * chore: auto-fix linting and formatting issues * feat: hydrate list data Signed-off-by: Innei <i@innei.in> * chore: auto-fix linting and formatting issues * feat: context feed to list action Signed-off-by: Innei <i@innei.in> * update Signed-off-by: Innei <i@innei.in> * add tooltip Signed-off-by: Innei <i@innei.in> * cetner Signed-off-by: Innei <i@innei.in> --------- Signed-off-by: Innei <i@innei.in> Co-authored-by: Innei <Innei@users.noreply.github.com>
This commit is contained in:
parent
16938f6078
commit
5ef516f0eb
|
|
@ -52,7 +52,6 @@
|
|||
"@types/node": "^22.7.4",
|
||||
"electron": "32.1.2",
|
||||
"electron-devtools-installer": "3.2.0",
|
||||
"hono": "4.6.3",
|
||||
"typescript": "^5.6.2"
|
||||
"hono": "4.6.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@
|
|||
"fake-indexeddb": "6.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"typescript": "^5.6.2",
|
||||
"vitest": "2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ const ContextMenuCheckboxItem = React.forwardRef<
|
|||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<i className="i-mgc-check-filled size-4" />
|
||||
<ContextMenuPrimitive.ItemIndicator asChild>
|
||||
<i className="i-mgc-check-filled size-3" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import type { Transaction } from "dexie"
|
|||
import Dexie from "dexie"
|
||||
|
||||
import { LOCAL_DB_NAME } from "./constants"
|
||||
import { dbSchemaV1, dbSchemaV2, dbSchemaV3, dbSchemaV4, dbSchemaV5 } from "./db_schema"
|
||||
import { dbSchemaV1, dbSchemaV2, dbSchemaV3, dbSchemaV4, dbSchemaV5, dbSchemaV6 } from "./db_schema"
|
||||
import type { DB_Cleaner } from "./schemas/cleaner"
|
||||
import type { DB_Entry, DB_EntryRelated } from "./schemas/entry"
|
||||
import type { DB_Feed, DB_FeedUnread } from "./schemas/feed"
|
||||
import type { DB_List } from "./schemas/list"
|
||||
import type { DB_Subscription } from "./schemas/subscription"
|
||||
|
||||
export interface LocalDBSchemaMap {
|
||||
|
|
@ -15,6 +16,7 @@ export interface LocalDBSchemaMap {
|
|||
entryRelated: DB_EntryRelated
|
||||
feedUnreads: DB_FeedUnread
|
||||
cleaner: DB_Cleaner
|
||||
lists: DB_List
|
||||
}
|
||||
|
||||
// Define a local DB
|
||||
|
|
@ -24,6 +26,7 @@ export class BrowserDB extends Dexie {
|
|||
public subscriptions: BrowserDBTable<"subscriptions">
|
||||
public entryRelated: BrowserDBTable<"entryRelated">
|
||||
public feedUnreads: BrowserDBTable<"feedUnreads">
|
||||
public lists: BrowserDBTable<"lists">
|
||||
public cleaner: BrowserDBTable<"cleaner">
|
||||
|
||||
constructor() {
|
||||
|
|
@ -33,6 +36,7 @@ export class BrowserDB extends Dexie {
|
|||
this.version(3).stores(dbSchemaV3)
|
||||
this.version(4).stores(dbSchemaV4)
|
||||
this.version(5).stores(dbSchemaV5)
|
||||
this.version(6).stores(dbSchemaV6)
|
||||
|
||||
this.entries = this.table("entries")
|
||||
this.feeds = this.table("feeds")
|
||||
|
|
@ -40,6 +44,7 @@ export class BrowserDB extends Dexie {
|
|||
this.entryRelated = this.table("entryRelated")
|
||||
this.feedUnreads = this.table("feedUnreads")
|
||||
this.cleaner = this.table("cleaner")
|
||||
this.lists = this.table("lists")
|
||||
}
|
||||
|
||||
async upgradeToV2(trans: Transaction) {
|
||||
|
|
|
|||
|
|
@ -28,3 +28,8 @@ export const dbSchemaV5 = {
|
|||
...dbSchemaV4,
|
||||
cleaner: "&refId, visitedAt",
|
||||
}
|
||||
|
||||
export const dbSchemaV6 = {
|
||||
...dbSchemaV5,
|
||||
lists: "&id, title",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import type { FeedOrListRespModel } from "~/models"
|
||||
import type { FeedModel } from "~/models"
|
||||
|
||||
export type DB_FeedUnread = {
|
||||
id: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export type DB_Feed = FeedOrListRespModel & { id: string }
|
||||
export type DB_Feed = FeedModel & { id: string }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
export type DB_List = {
|
||||
id: string
|
||||
title: string
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
description: string
|
||||
fee: number
|
||||
image: string
|
||||
ownerUserId: string
|
||||
timelineUpdatedAt: string
|
||||
feedIds: string[]
|
||||
}
|
||||
|
|
@ -4,14 +4,19 @@ import { useTranslation } from "react-i18next"
|
|||
|
||||
import { whoami } from "~/atoms/user"
|
||||
import { useModalStack } from "~/components/ui/modal"
|
||||
import type { FeedViewType } from "~/lib/enum"
|
||||
import type { NativeMenuItem } from "~/lib/native-menu"
|
||||
import { useFeedClaimModal } from "~/modules/claim"
|
||||
import { FeedForm } from "~/modules/discover/feed-form"
|
||||
import { Queries } from "~/queries"
|
||||
import { getFeedById, useAddFeedToFeedList, useFeedById } from "~/store/feed"
|
||||
import {
|
||||
getFeedById,
|
||||
useAddFeedToFeedList,
|
||||
useFeedById,
|
||||
useRemoveFeedFromFeedList,
|
||||
} from "~/store/feed"
|
||||
import { useListById, useListByView } from "~/store/list"
|
||||
import { subscriptionActions, useSubscriptionByFeedId } from "~/store/subscription"
|
||||
|
||||
import { useAuthQuery } from "../common"
|
||||
import { useNavigateEntry } from "./useNavigateEntry"
|
||||
import { getRouteParams } from "./useRouteParams"
|
||||
import { useDeleteSubscription } from "./useSubscriptionActions"
|
||||
|
|
@ -38,25 +43,23 @@ export const useFeedActions = ({
|
|||
const navigateEntry = useNavigateEntry()
|
||||
const isEntryList = type === "entryList"
|
||||
|
||||
const listList = useAuthQuery(Queries.lists.list())
|
||||
const addMutation = useAddFeedToFeedList()
|
||||
const removeMutation = useRemoveFeedFromFeedList()
|
||||
|
||||
const listByView = useListByView(view!)
|
||||
|
||||
const items = useMemo(() => {
|
||||
if (!feed) return []
|
||||
const isList = feed?.type === "list"
|
||||
|
||||
const items: NativeMenuItem[] = [
|
||||
...(!isList
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.mark_all_as_read"),
|
||||
shortcut: "Meta+Shift+A",
|
||||
disabled: isEntryList,
|
||||
click: () => subscriptionActions.markReadByFeedIds({ feedIds: [feedId] }),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(!feed.ownerUserId && !!feed.id && !isList
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.mark_all_as_read"),
|
||||
shortcut: "Meta+Shift+A",
|
||||
disabled: isEntryList,
|
||||
click: () => subscriptionActions.markReadByFeedIds({ feedIds: [feedId] }),
|
||||
},
|
||||
...(!feed.ownerUserId && !!feed.id && !false
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
|
|
@ -74,11 +77,7 @@ export const useFeedActions = ({
|
|||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t(
|
||||
isList
|
||||
? "sidebar.feed_actions.list_owned_by_you"
|
||||
: "sidebar.feed_actions.feed_owned_by_you",
|
||||
),
|
||||
label: t("sidebar.feed_actions.feed_owned_by_you"),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
|
@ -86,41 +85,44 @@ export const useFeedActions = ({
|
|||
type: "separator" as const,
|
||||
disabled: isEntryList,
|
||||
},
|
||||
...(!isList
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_column.context_menu.add_feeds_to_list"),
|
||||
enabled: !!listList.data?.length,
|
||||
submenu: listList.data?.map((list) => ({
|
||||
label: list.title || "",
|
||||
type: "text" as const,
|
||||
click() {
|
||||
return addMutation.mutate({
|
||||
feedId,
|
||||
listId: list.id,
|
||||
})
|
||||
},
|
||||
})),
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_column.context_menu.add_feeds_to_list"),
|
||||
enabled: !!listByView?.length,
|
||||
submenu: listByView?.map((list) => {
|
||||
const isIncluded = list.feedIds.includes(feedId)
|
||||
return {
|
||||
label: list.title || "",
|
||||
type: "text" as const,
|
||||
checked: isIncluded,
|
||||
click() {
|
||||
if (!isIncluded) {
|
||||
addMutation.mutate({
|
||||
feedId,
|
||||
listId: list.id,
|
||||
})
|
||||
} else {
|
||||
removeMutation.mutate({
|
||||
feedId,
|
||||
listId: list.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "separator" as const,
|
||||
disabled: isEntryList,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: "separator" as const,
|
||||
disabled: isEntryList,
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: isEntryList ? t("sidebar.feed_actions.edit_feed") : t("sidebar.feed_actions.edit"),
|
||||
shortcut: "E",
|
||||
click: () => {
|
||||
present({
|
||||
title: isList
|
||||
? t("sidebar.feed_actions.edit_list")
|
||||
: t("sidebar.feed_actions.edit_feed"),
|
||||
content: ({ dismiss }) => (
|
||||
<FeedForm asWidget id={feedId} onSuccess={dismiss} isList={isList} />
|
||||
),
|
||||
title: t("sidebar.feed_actions.edit_feed"),
|
||||
content: ({ dismiss }) => <FeedForm asWidget id={feedId} onSuccess={dismiss} />,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
|
@ -134,11 +136,7 @@ export const useFeedActions = ({
|
|||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t(
|
||||
isList
|
||||
? "sidebar.feed_actions.navigate_to_list"
|
||||
: "sidebar.feed_actions.navigate_to_feed",
|
||||
),
|
||||
label: t("sidebar.feed_actions.navigate_to_feed"),
|
||||
shortcut: "Meta+G",
|
||||
disabled: !isEntryList || getRouteParams().feedId === feedId,
|
||||
click: () => {
|
||||
|
|
@ -151,61 +149,46 @@ export const useFeedActions = ({
|
|||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t(
|
||||
isList
|
||||
? "sidebar.feed_actions.open_list_in_browser"
|
||||
: "sidebar.feed_actions.open_feed_in_browser",
|
||||
{ which: t(window.electron ? "words.browser" : "words.newTab") },
|
||||
),
|
||||
label: t("sidebar.feed_actions.open_feed_in_browser", {
|
||||
which: t(window.electron ? "words.browser" : "words.newTab"),
|
||||
}),
|
||||
disabled: isEntryList,
|
||||
shortcut: "O",
|
||||
click: () =>
|
||||
window.open(
|
||||
isList
|
||||
? `${WEB_URL}/list/${feedId}?view=${view}`
|
||||
: `${WEB_URL}/feed/${feedId}?view=${view}`,
|
||||
"_blank",
|
||||
),
|
||||
click: () => window.open(`${WEB_URL}/feed/${feedId}?view=${view}`, "_blank"),
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.open_site_in_browser", {
|
||||
which: t(window.electron ? "words.browser" : "words.newTab"),
|
||||
}),
|
||||
shortcut: "Meta+O",
|
||||
disabled: isEntryList,
|
||||
click: () => {
|
||||
const feed = getFeedById(feedId)
|
||||
if (feed) {
|
||||
"siteUrl" in feed && feed.siteUrl && window.open(feed.siteUrl, "_blank")
|
||||
}
|
||||
},
|
||||
},
|
||||
...(!isList
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.open_site_in_browser", {
|
||||
which: t(window.electron ? "words.browser" : "words.newTab"),
|
||||
}),
|
||||
shortcut: "Meta+O",
|
||||
disabled: isEntryList,
|
||||
click: () => {
|
||||
const feed = getFeedById(feedId)
|
||||
if (feed) {
|
||||
"siteUrl" in feed && feed.siteUrl && window.open(feed.siteUrl, "_blank")
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
type: "separator",
|
||||
disabled: isEntryList,
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t(
|
||||
isList ? "sidebar.feed_actions.copy_list_url" : "sidebar.feed_actions.copy_feed_url",
|
||||
),
|
||||
label: t("sidebar.feed_actions.copy_feed_url"),
|
||||
disabled: isEntryList,
|
||||
shortcut: "Meta+C",
|
||||
click: () => {
|
||||
const url = isList ? `${WEB_URL}/list/${feedId}?view=${view}` : feed.url
|
||||
// @ts-expect-error
|
||||
const { url } = feed || {}
|
||||
if (!url) return
|
||||
navigator.clipboard.writeText(url)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t(
|
||||
isList ? "sidebar.feed_actions.copy_list_id" : "sidebar.feed_actions.copy_feed_id",
|
||||
),
|
||||
label: t("sidebar.feed_actions.copy_feed_id"),
|
||||
shortcut: "Meta+Shift+C",
|
||||
disabled: isEntryList,
|
||||
click: () => {
|
||||
|
|
@ -216,17 +199,117 @@ export const useFeedActions = ({
|
|||
|
||||
return items
|
||||
}, [
|
||||
t,
|
||||
claimFeed,
|
||||
deleteSubscription,
|
||||
feed,
|
||||
feedId,
|
||||
t,
|
||||
isEntryList,
|
||||
navigateEntry,
|
||||
listByView,
|
||||
feedId,
|
||||
claimFeed,
|
||||
addMutation,
|
||||
present,
|
||||
deleteSubscription,
|
||||
subscription,
|
||||
navigateEntry,
|
||||
view,
|
||||
])
|
||||
|
||||
return { items }
|
||||
}
|
||||
|
||||
export const useListActions = ({ listId, view }: { listId: string; view: FeedViewType }) => {
|
||||
const { t } = useTranslation()
|
||||
const list = useListById(listId)
|
||||
const subscription = useSubscriptionByFeedId(listId)
|
||||
|
||||
const { present } = useModalStack()
|
||||
const deleteSubscription = useDeleteSubscription({})
|
||||
|
||||
const navigateEntry = useNavigateEntry()
|
||||
|
||||
const items = useMemo(() => {
|
||||
if (!list) return []
|
||||
|
||||
const items: NativeMenuItem[] = [
|
||||
...(list.ownerUserId === whoami()?.id
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.list_owned_by_you"),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
type: "separator" as const,
|
||||
disabled: false,
|
||||
},
|
||||
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.edit"),
|
||||
shortcut: "E",
|
||||
click: () => {
|
||||
present({
|
||||
title: t("sidebar.feed_actions.edit_list"),
|
||||
content: ({ dismiss }) => <FeedForm asWidget id={listId} onSuccess={dismiss} isList />,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.unfollow"),
|
||||
shortcut: "Meta+Backspace",
|
||||
click: () => deleteSubscription.mutate(subscription),
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.navigate_to_list"),
|
||||
shortcut: "Meta+G",
|
||||
disabled: getRouteParams().feedId === listId,
|
||||
click: () => {
|
||||
navigateEntry({ feedId: listId })
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "separator" as const,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.open_list_in_browser", {
|
||||
which: t(window.electron ? "words.browser" : "words.newTab"),
|
||||
}),
|
||||
disabled: false,
|
||||
shortcut: "O",
|
||||
click: () => window.open(`${WEB_URL}/list/${listId}?view=${view}`, "_blank"),
|
||||
},
|
||||
|
||||
{
|
||||
type: "separator",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.copy_list_url"),
|
||||
disabled: false,
|
||||
shortcut: "Meta+C",
|
||||
click: () => {
|
||||
const url = `${WEB_URL}/list/${listId}?view=${view}`
|
||||
navigator.clipboard.writeText(url)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
label: t("sidebar.feed_actions.copy_list_id"),
|
||||
shortcut: "Meta+Shift+C",
|
||||
disabled: false,
|
||||
click: () => {
|
||||
navigator.clipboard.writeText(listId)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return items
|
||||
}, [list, t, present, deleteSubscription, subscription, navigateEntry, listId, view])
|
||||
|
||||
return { items }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ import {
|
|||
FeedUnreadService,
|
||||
SubscriptionService,
|
||||
} from "~/services"
|
||||
import { ListService } from "~/services/list"
|
||||
import type { FlatEntryModel } from "~/store/entry"
|
||||
import { listActions } from "~/store/list"
|
||||
|
||||
import { entryActions, useEntryStore } from "../store/entry/store"
|
||||
import { feedActions, useFeedStore } from "../store/feed"
|
||||
|
|
@ -25,7 +27,13 @@ export const setHydrated = (v: boolean) => {
|
|||
export const hydrateDatabaseToStore = async () => {
|
||||
async function hydrate() {
|
||||
const now = Date.now()
|
||||
await Promise.all([hydrateFeed(), hydrateSubscription(), hydrateFeedUnread(), hydrateEntry()])
|
||||
await Promise.all([
|
||||
hydrateFeed(),
|
||||
hydrateSubscription(),
|
||||
hydrateFeedUnread(),
|
||||
hydrateEntry(),
|
||||
hydrateList(),
|
||||
])
|
||||
|
||||
window.__dbIsReady = true
|
||||
const costTime = Date.now() - now
|
||||
|
|
@ -89,6 +97,11 @@ async function hydrateSubscription() {
|
|||
subscriptionActions.upsertMany(subscriptions)
|
||||
}
|
||||
|
||||
async function hydrateList() {
|
||||
const lists = await ListService.findAll()
|
||||
listActions.upsertMany(lists)
|
||||
}
|
||||
|
||||
const logHydrateError = (message: string) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(`Hydrate error: ${message}, maybe local database data is dirty.`)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export type NativeMenuItem =
|
|||
shortcut?: string
|
||||
disabled?: boolean
|
||||
submenu?: NativeMenuItem[]
|
||||
checked?: boolean
|
||||
}
|
||||
| { type: "separator"; disabled?: boolean }
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export type TransactionModel = ExtractBizResponse<
|
|||
|
||||
export type FeedModel = ExtractBizResponse<typeof apiClient.feeds.$get>["data"]["feed"]
|
||||
|
||||
type ListModelPoplutedFeeds = ExtractBizResponse<typeof apiClient.lists.$get>["data"]["list"]
|
||||
export type ListModelPoplutedFeeds = ExtractBizResponse<typeof apiClient.lists.$get>["data"]["list"]
|
||||
export type ListModel = Omit<ListModelPoplutedFeeds, "feeds">
|
||||
export type FeedOrListRespModel = FeedModel | ListModelPoplutedFeeds
|
||||
export type FeedOrListModel = FeedModel | ListModel
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import { LoadingCircle } from "~/components/ui/loading"
|
|||
import { ROUTE_FEED_IN_FOLDER, views } from "~/constants"
|
||||
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
|
||||
import { getRouteParams, useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
|
||||
import { useAnyPointDown, useAuthQuery, useInputComposition } from "~/hooks/common"
|
||||
import { useAnyPointDown, useInputComposition } from "~/hooks/common"
|
||||
import { stopPropagation } from "~/lib/dom"
|
||||
import type { FeedViewType } from "~/lib/enum"
|
||||
import { showNativeMenu } from "~/lib/native-menu"
|
||||
import { cn, sortByAlphabet } from "~/lib/utils"
|
||||
import { Queries } from "~/queries"
|
||||
import { getPreferredTitle, useAddFeedToFeedList, useFeedStore } from "~/store/feed"
|
||||
import { useListByView } from "~/store/list"
|
||||
import { subscriptionActions, useSubscriptionByFeedId } from "~/store/subscription"
|
||||
import { useFeedUnreadStore } from "~/store/unread"
|
||||
|
||||
|
|
@ -46,29 +46,21 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
|
|||
const folderName = subscription?.category || subscription.defaultCategory
|
||||
|
||||
const showCollapse = sortByUnreadFeedList.length > 1 || subscription?.category
|
||||
const open = folderName ? categoryOpenStateData[folderName] : true
|
||||
|
||||
const shouldOpen =
|
||||
useRouteParamsSelector((s) => typeof s.feedId === "string" && ids.includes(s.feedId)) ||
|
||||
ids.length === 1
|
||||
|
||||
const itemsRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const toggleCategoryOpenState = (e) => {
|
||||
e.stopPropagation()
|
||||
if (!isCategoryEditing) {
|
||||
setCategoryActive()
|
||||
const [open, setOpen] = useState(() => {
|
||||
if (!showCollapse) return true
|
||||
if (folderName && typeof categoryOpenStateData[folderName] === "boolean") {
|
||||
return categoryOpenStateData[folderName]
|
||||
}
|
||||
if (view !== undefined && folderName) {
|
||||
subscriptionActions.toggleCategoryOpenState(view, folderName)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const shouldOpen = useRouteParamsSelector(
|
||||
(s) => typeof s.feedId === "string" && ids.includes(s.feedId),
|
||||
)
|
||||
useEffect(() => {
|
||||
if (shouldOpen) {
|
||||
if (!open && view !== undefined && folderName) {
|
||||
subscriptionActions.changeCategoryOpenState(view, folderName, true)
|
||||
}
|
||||
setOpen(true)
|
||||
|
||||
const $items = itemsRef.current
|
||||
|
||||
|
|
@ -78,13 +70,31 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
|
|||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
}, [shouldOpen, open, view, folderName])
|
||||
}, [shouldOpen])
|
||||
const expansion = folderName ? categoryOpenStateData[folderName] : true
|
||||
|
||||
useEffect(() => {
|
||||
if (showCollapse) {
|
||||
setOpen(expansion)
|
||||
}
|
||||
}, [expansion])
|
||||
|
||||
const itemsRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const toggleCategoryOpenState = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation()
|
||||
if (!isCategoryEditing) {
|
||||
setCategoryActive()
|
||||
}
|
||||
if (view !== undefined && folderName) {
|
||||
subscriptionActions.toggleCategoryOpenState(view, folderName)
|
||||
}
|
||||
}
|
||||
|
||||
const setCategoryActive = () => {
|
||||
if (view !== undefined) {
|
||||
navigate({
|
||||
entryId: null,
|
||||
// TODO joint feedId is too long, need to be optimized
|
||||
folderName,
|
||||
view,
|
||||
})
|
||||
|
|
@ -117,9 +127,10 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
|
|||
})
|
||||
const isCategoryIsWaiting = isChangePending
|
||||
|
||||
const listList = useAuthQuery(Queries.lists.list())
|
||||
const addMutation = useAddFeedToFeedList()
|
||||
|
||||
const listList = useListByView(view!)
|
||||
|
||||
return (
|
||||
<div tabIndex={-1} onClick={stopPropagation}>
|
||||
{!!showCollapse && (
|
||||
|
|
@ -151,8 +162,8 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
|
|||
{
|
||||
type: "text",
|
||||
label: t("sidebar.feed_column.context_menu.add_feeds_to_list"),
|
||||
enabled: !!listList.data?.length,
|
||||
submenu: listList.data?.map((list) => ({
|
||||
enabled: !!listList?.length,
|
||||
submenu: listList?.map((list) => ({
|
||||
label: list.title || "",
|
||||
type: "text",
|
||||
click() {
|
||||
|
|
@ -172,7 +183,7 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
|
|||
.filter((v) => v.view !== view)
|
||||
.map((v) => ({
|
||||
label: t(v.name),
|
||||
type: "text",
|
||||
type: "text" as const,
|
||||
shortcut: (v.view + 1).toString(),
|
||||
icon: v.icon,
|
||||
click() {
|
||||
|
|
|
|||
|
|
@ -8,15 +8,18 @@ import { FeedCertification } from "~/components/feed-certification"
|
|||
import { FeedIcon } from "~/components/feed-icon"
|
||||
import { OouiUserAnonymous } from "~/components/icons/OouiUserAnonymous"
|
||||
import { Tooltip, TooltipContent, TooltipPortal, TooltipTrigger } from "~/components/ui/tooltip"
|
||||
import { useFeedActions } from "~/hooks/biz/useFeedActions"
|
||||
import { EllipsisHorizontalTextWithTooltip } from "~/components/ui/typography"
|
||||
import { useFeedActions, useListActions } from "~/hooks/biz/useFeedActions"
|
||||
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
|
||||
import { useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
|
||||
import { useAnyPointDown } from "~/hooks/common"
|
||||
import { nextFrame } from "~/lib/dom"
|
||||
import type { FeedViewType } from "~/lib/enum"
|
||||
import { getNewIssueUrl } from "~/lib/issues"
|
||||
import { showNativeMenu } from "~/lib/native-menu"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { getPreferredTitle, useFeedById } from "~/store/feed"
|
||||
import { useListById } from "~/store/list"
|
||||
import { subscriptionActions, useSubscriptionByFeedId } from "~/store/subscription"
|
||||
import { useFeedUnreadStore } from "~/store/unread"
|
||||
|
||||
|
|
@ -68,7 +71,7 @@ const FeedItemImpl = ({ view, feedId, className }: FeedItemProps) => {
|
|||
if (!feed) return null
|
||||
|
||||
const isFeed = feed.type === "feed" || !feed.type
|
||||
const isList = feed.type === "list"
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -118,7 +121,7 @@ const FeedItemImpl = ({ view, feedId, className }: FeedItemProps) => {
|
|||
isFeed && feed.errorAt && "text-red-900 dark:text-red-500",
|
||||
)}
|
||||
>
|
||||
<FeedIcon fallback feed={feed} size={isList ? 28 : 16} />
|
||||
<FeedIcon fallback feed={feed} size={16} />
|
||||
<div className="truncate">{getPreferredTitle(feed)}</div>
|
||||
{isFeed && <FeedCertification feed={feed} className="text-[15px]" />}
|
||||
{isFeed && feed.errorAt && (
|
||||
|
|
@ -156,10 +159,84 @@ const FeedItemImpl = ({ view, feedId, className }: FeedItemProps) => {
|
|||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<UnreadNumber unread={feedUnread} className="ml-2" isList={isList} />
|
||||
<UnreadNumber unread={feedUnread} className="ml-2" />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const FeedItem = memo(FeedItemImpl)
|
||||
|
||||
const ListItemImpl: Component<{
|
||||
listId: string
|
||||
view: FeedViewType
|
||||
}> = ({ view, listId, className }) => {
|
||||
const list = useListById(listId)
|
||||
|
||||
const isActive = useRouteParamsSelector((routerParams) => routerParams.feedId === listId)
|
||||
const { items } = useListActions({ listId, view })
|
||||
|
||||
const listUnread = useFeedUnreadStore((state) => state.data[listId] || 0)
|
||||
|
||||
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
|
||||
useAnyPointDown(() => {
|
||||
setIsContextMenuOpen(false)
|
||||
})
|
||||
const subscription = useSubscriptionByFeedId(listId)
|
||||
const navigate = useNavigateEntry()
|
||||
const handleNavigate = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
|
||||
navigate({
|
||||
feedId: listId,
|
||||
entryId: null,
|
||||
view,
|
||||
})
|
||||
},
|
||||
[listId, navigate, view],
|
||||
)
|
||||
const { t } = useTranslation()
|
||||
if (!list) return null
|
||||
return (
|
||||
<div
|
||||
data-list-id={listId}
|
||||
className={cn(
|
||||
"flex w-full cursor-menu items-center justify-between rounded-md pr-2.5 text-sm font-medium leading-loose",
|
||||
(isActive || isContextMenuOpen) && "bg-native-active",
|
||||
"py-1.5 pl-2.5",
|
||||
className,
|
||||
)}
|
||||
onClick={handleNavigate}
|
||||
onDoubleClick={() => {
|
||||
window.open(`${WEB_URL}/list/${listId}?view=${view}`, "_blank")
|
||||
}}
|
||||
onContextMenu={(e) => {
|
||||
setIsContextMenuOpen(true)
|
||||
|
||||
showNativeMenu(items, e)
|
||||
}}
|
||||
>
|
||||
<div className={"flex min-w-0 items-center"}>
|
||||
<FeedIcon fallback feed={list} size={28} />
|
||||
<EllipsisHorizontalTextWithTooltip className="truncate">
|
||||
{list.title}
|
||||
</EllipsisHorizontalTextWithTooltip>
|
||||
|
||||
{subscription.isPrivate && (
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger>
|
||||
<OouiUserAnonymous className="ml-1 shrink-0 text-base" />
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>{t("feed_item.not_publicly_visible")}</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<UnreadNumber unread={listUnread} isList className="ml-2" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const ListItem = memo(ListItemImpl)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as HoverCard from "@radix-ui/react-hover-card"
|
||||
import { AnimatePresence, m } from "framer-motion"
|
||||
import { Fragment, memo, useMemo, useState } from "react"
|
||||
import { memo, useMemo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link } from "react-router-dom"
|
||||
|
||||
|
|
@ -12,25 +12,17 @@ import { useRouteFeedId } from "~/hooks/biz/useRouteParams"
|
|||
import { useAuthQuery } from "~/hooks/common"
|
||||
import { stopPropagation } from "~/lib/dom"
|
||||
import type { FeedViewType } from "~/lib/enum"
|
||||
import { cn, sortByAlphabet } from "~/lib/utils"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { Queries } from "~/queries"
|
||||
import { getPreferredTitle, useFeedStore } from "~/store/feed"
|
||||
import {
|
||||
getSubscriptionByFeedId,
|
||||
subscriptionActions,
|
||||
useCategoryOpenStateByView,
|
||||
useSubscriptionByView,
|
||||
} from "~/store/subscription"
|
||||
import { useFeedUnreadStore } from "~/store/unread"
|
||||
|
||||
import {
|
||||
getFeedListSort,
|
||||
setFeedListSortBy,
|
||||
setFeedListSortOrder,
|
||||
useFeedListSort,
|
||||
useFeedListSortSelector,
|
||||
} from "./atom"
|
||||
import { FeedCategory } from "./category"
|
||||
import { getFeedListSort, setFeedListSortBy, setFeedListSortOrder, useFeedListSort } from "./atom"
|
||||
import { SortableFeedList, SortByAlphabeticalList } from "./sort-by"
|
||||
import { UnreadNumber } from "./unread-number"
|
||||
|
||||
const useFeedsGroupedData = (view: FeedViewType) => {
|
||||
|
|
@ -108,6 +100,10 @@ function FeedListImpl({ className, view }: { className?: string; view: number })
|
|||
|
||||
const { t } = useTranslation()
|
||||
|
||||
// Data prefetch
|
||||
useAuthQuery(Queries.lists.list())
|
||||
|
||||
const hasListData = Object.keys(listsData).length > 0
|
||||
return (
|
||||
<div className={cn(className, "font-medium")}>
|
||||
<div onClick={stopPropagation} className="mx-3 flex items-center justify-between px-2.5 py-1">
|
||||
|
|
@ -163,29 +159,27 @@ function FeedListImpl({ className, view }: { className?: string; view: number })
|
|||
<i className="i-mgc-star-cute-fi size-4 -translate-y-px text-amber-500" />
|
||||
{t("words.starred")}
|
||||
</div>
|
||||
{Object.keys(listsData).length > 0 && (
|
||||
{hasListData && (
|
||||
<>
|
||||
<div className="mt-1 flex h-6 w-full shrink-0 items-center rounded-md px-2.5 text-xs font-semibold text-theme-vibrancyFg transition-colors">
|
||||
{t("words.lists")}
|
||||
</div>
|
||||
<SortableList
|
||||
view={view}
|
||||
data={listsData}
|
||||
categoryOpenStateData={categoryOpenStateData}
|
||||
by="alphabetical"
|
||||
/>
|
||||
|
||||
<SortByAlphabeticalList view={view} />
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-6 w-full shrink-0 items-center rounded-md px-2.5 text-xs font-semibold text-theme-vibrancyFg transition-colors",
|
||||
Object.keys(feedsData).length === 0 ? "mt-0" : "mt-1",
|
||||
)}
|
||||
>
|
||||
{t("words.feeds")}
|
||||
</div>
|
||||
{hasListData && hasData && (
|
||||
<div
|
||||
className={cn(
|
||||
"mb-1 flex h-6 w-full shrink-0 items-center rounded-md px-2.5 text-xs font-semibold text-theme-vibrancyFg transition-colors",
|
||||
Object.keys(feedsData).length === 0 ? "mt-0" : "mt-1",
|
||||
)}
|
||||
>
|
||||
{t("words.feeds")}
|
||||
</div>
|
||||
)}
|
||||
{hasData ? (
|
||||
<SortableList
|
||||
<SortableFeedList
|
||||
view={view}
|
||||
data={feedsData}
|
||||
categoryOpenStateData={categoryOpenStateData}
|
||||
|
|
@ -297,109 +291,4 @@ const SortButton = () => {
|
|||
)
|
||||
}
|
||||
|
||||
type FeedListProps = {
|
||||
view: number
|
||||
data: Record<string, string[]>
|
||||
categoryOpenStateData: Record<string, boolean>
|
||||
}
|
||||
const SortByUnreadList = ({ view, data, categoryOpenStateData }: FeedListProps) => {
|
||||
const isDesc = useFeedListSortSelector((s) => s.order === "desc")
|
||||
|
||||
const sortedByUnread = useFeedUnreadStore((state) => {
|
||||
const sortedList = [] as [string, string[]][]
|
||||
const folderUnread = {} as Record<string, number>
|
||||
// Calc total unread count for each folder
|
||||
for (const category in data) {
|
||||
folderUnread[category] = data[category].reduce((acc, cur) => (state.data[cur] || 0) + acc, 0)
|
||||
}
|
||||
|
||||
// Sort by unread count
|
||||
Object.keys(folderUnread)
|
||||
.sort((a, b) => folderUnread[b] - folderUnread[a])
|
||||
.forEach((key) => {
|
||||
sortedList.push([key, data[key]])
|
||||
})
|
||||
|
||||
if (!isDesc) {
|
||||
sortedList.reverse()
|
||||
}
|
||||
return sortedList
|
||||
})
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{sortedByUnread?.map(([category, ids]) => (
|
||||
<FeedCategory
|
||||
key={category}
|
||||
data={ids}
|
||||
view={view}
|
||||
categoryOpenStateData={categoryOpenStateData}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const SortByAlphabeticalList = ({ view, data, categoryOpenStateData }: FeedListProps) => {
|
||||
const categoryName2RealDisplayNameMap = useFeedStore((state) => {
|
||||
const map = {} as Record<string, string>
|
||||
for (const categoryName in data) {
|
||||
const feedId = data[categoryName][0]
|
||||
|
||||
if (!feedId) {
|
||||
continue
|
||||
}
|
||||
const feed = state.feeds[feedId]
|
||||
if (!feed) {
|
||||
continue
|
||||
}
|
||||
const hascategoryNameNotDefault = !!getSubscriptionByFeedId(feedId)?.category
|
||||
const isSingle = data[categoryName].length === 1
|
||||
if (!isSingle || hascategoryNameNotDefault) {
|
||||
map[categoryName] = categoryName
|
||||
} else {
|
||||
map[categoryName] = getPreferredTitle(feed)!
|
||||
}
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const isDesc = useFeedListSortSelector((s) => s.order === "desc")
|
||||
|
||||
let sortedByAlphabetical = Object.keys(data).sort((a, b) => {
|
||||
const nameA = categoryName2RealDisplayNameMap[a]
|
||||
const nameB = categoryName2RealDisplayNameMap[b]
|
||||
return sortByAlphabet(nameA, nameB)
|
||||
})
|
||||
if (!isDesc) {
|
||||
sortedByAlphabetical = sortedByAlphabetical.reverse()
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{sortedByAlphabetical.map((category) => (
|
||||
<FeedCategory
|
||||
key={category}
|
||||
data={data[category]}
|
||||
view={view}
|
||||
categoryOpenStateData={categoryOpenStateData}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const SortableList = (props: FeedListProps & { by?: "count" | "alphabetical" }) => {
|
||||
const userBy = useFeedListSortSelector((s) => s.by)
|
||||
|
||||
switch (props.by || userBy) {
|
||||
case "count": {
|
||||
return <SortByUnreadList {...props} />
|
||||
}
|
||||
case "alphabetical": {
|
||||
return <SortByAlphabeticalList {...props} />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const FeedList = memo(FeedListImpl)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
import { Fragment, useMemo } from "react"
|
||||
|
||||
import { sortByAlphabet } from "~/lib/utils"
|
||||
import { getPreferredTitle, useFeedStore } from "~/store/feed"
|
||||
import { useListByView } from "~/store/list"
|
||||
import { getSubscriptionByFeedId } from "~/store/subscription"
|
||||
|
||||
import { useFeedListSortSelector } from "../atom"
|
||||
import { FeedCategory } from "../category"
|
||||
import { ListItem } from "../item"
|
||||
import type { FeedListProps, ListListProps } from "./types"
|
||||
|
||||
export const SortByAlphabeticalFeedList = ({
|
||||
view,
|
||||
data,
|
||||
categoryOpenStateData,
|
||||
}: FeedListProps) => {
|
||||
const categoryName2RealDisplayNameMap = useFeedStore((state) => {
|
||||
const map = {} as Record<string, string>
|
||||
for (const categoryName in data) {
|
||||
const feedId = data[categoryName][0]
|
||||
|
||||
if (!feedId) {
|
||||
continue
|
||||
}
|
||||
const feed = state.feeds[feedId]
|
||||
if (!feed) {
|
||||
continue
|
||||
}
|
||||
const hascategoryNameNotDefault = !!getSubscriptionByFeedId(feedId)?.category
|
||||
const isSingle = data[categoryName].length === 1
|
||||
if (!isSingle || hascategoryNameNotDefault) {
|
||||
map[categoryName] = categoryName
|
||||
} else {
|
||||
map[categoryName] = getPreferredTitle(feed)!
|
||||
}
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const isDesc = useFeedListSortSelector((s) => s.order === "desc")
|
||||
|
||||
let sortedByAlphabetical = Object.keys(data).sort((a, b) => {
|
||||
const nameA = categoryName2RealDisplayNameMap[a]
|
||||
const nameB = categoryName2RealDisplayNameMap[b]
|
||||
return sortByAlphabet(nameA, nameB)
|
||||
})
|
||||
if (!isDesc) {
|
||||
sortedByAlphabetical = sortedByAlphabetical.reverse()
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{sortedByAlphabetical.map((category) => (
|
||||
<FeedCategory
|
||||
key={category}
|
||||
data={data[category]}
|
||||
view={view}
|
||||
categoryOpenStateData={categoryOpenStateData}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export const SortByAlphabeticalListList = ({ view }: ListListProps) => {
|
||||
const isDesc = useFeedListSortSelector((s) => s.order === "desc")
|
||||
|
||||
const lists = useListByView(view)
|
||||
|
||||
const sortedLists = useMemo(() => {
|
||||
const res = lists.sort((a, b) => {
|
||||
return sortByAlphabet(a.title ?? "", b.title ?? "")
|
||||
})
|
||||
|
||||
return isDesc ? res.reverse() : res
|
||||
}, [isDesc, lists])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{sortedLists.map((list) => (
|
||||
<ListItem key={list.id} listId={list.id} view={view} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { Fragment } from "react"
|
||||
|
||||
import { useFeedUnreadStore } from "~/store/unread"
|
||||
|
||||
import { useFeedListSortSelector } from "../atom"
|
||||
import { FeedCategory } from "../category"
|
||||
import type { FeedListProps } from "./types"
|
||||
|
||||
export const SortByUnreadFeedList = ({ view, data, categoryOpenStateData }: FeedListProps) => {
|
||||
const isDesc = useFeedListSortSelector((s) => s.order === "desc")
|
||||
|
||||
const sortedByUnread = useFeedUnreadStore((state) => {
|
||||
const sortedList = [] as [string, string[]][]
|
||||
const folderUnread = {} as Record<string, number>
|
||||
// Calc total unread count for each folder
|
||||
for (const category in data) {
|
||||
folderUnread[category] = data[category].reduce((acc, cur) => (state.data[cur] || 0) + acc, 0)
|
||||
}
|
||||
|
||||
// Sort by unread count
|
||||
Object.keys(folderUnread)
|
||||
.sort((a, b) => folderUnread[b] - folderUnread[a])
|
||||
.forEach((key) => {
|
||||
sortedList.push([key, data[key]])
|
||||
})
|
||||
|
||||
if (!isDesc) {
|
||||
sortedList.reverse()
|
||||
}
|
||||
return sortedList
|
||||
})
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{sortedByUnread?.map(([category, ids]) => (
|
||||
<FeedCategory
|
||||
key={category}
|
||||
data={ids}
|
||||
view={view}
|
||||
categoryOpenStateData={categoryOpenStateData}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { useFeedListSortSelector } from "../atom"
|
||||
import { SortByAlphabeticalFeedList, SortByAlphabeticalListList } from "./SortByAlphabeticalList"
|
||||
import { SortByUnreadFeedList } from "./SortByUnreadList"
|
||||
import type { FeedListProps, ListListProps } from "./types"
|
||||
|
||||
export const SortableFeedList = (props: FeedListProps) => {
|
||||
const by = useFeedListSortSelector((s) => s.by)
|
||||
|
||||
switch (by) {
|
||||
case "count": {
|
||||
return <SortByUnreadFeedList {...props} />
|
||||
}
|
||||
case "alphabetical": {
|
||||
return <SortByAlphabeticalFeedList {...props} />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SortByAlphabeticalList = (props: ListListProps) => {
|
||||
const by = useFeedListSortSelector((s) => s.by)
|
||||
|
||||
switch (by) {
|
||||
default: {
|
||||
return <SortByAlphabeticalListList {...props} />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import type { FeedViewType } from "~/lib/enum"
|
||||
|
||||
export type FeedListProps = {
|
||||
view: number
|
||||
data: Record<string, string[]>
|
||||
categoryOpenStateData: Record<string, boolean>
|
||||
}
|
||||
export type SortBy = "count" | "alphabetical"
|
||||
|
||||
export type ListListProps = {
|
||||
view: FeedViewType
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ import { views } from "~/constants"
|
|||
import { useAuthQuery, useI18n } from "~/hooks/common"
|
||||
import { apiClient } from "~/lib/api-fetch"
|
||||
import { cn, isBizId } from "~/lib/utils"
|
||||
import type { FeedModel, ListModel } from "~/models"
|
||||
import type { FeedModel } from "~/models"
|
||||
import { ViewSelectorRadioGroup } from "~/modules/shared/ViewSelectorRadioGroup"
|
||||
import { Balance } from "~/modules/wallet/balance"
|
||||
import { Queries } from "~/queries"
|
||||
|
|
@ -50,6 +50,7 @@ import {
|
|||
useFeedById,
|
||||
useRemoveFeedFromFeedList,
|
||||
} from "~/store/feed"
|
||||
import { useListById } from "~/store/list"
|
||||
import { subscriptionActions, useSubscriptionStore } from "~/store/subscription"
|
||||
|
||||
export const SettingLists = () => {
|
||||
|
|
@ -86,7 +87,9 @@ export const SettingLists = () => {
|
|||
<TableHead size="sm">{t.settings("lists.fee.label")}</TableHead>
|
||||
<TableHead size="sm">{t.settings("lists.subscriptions")}</TableHead>
|
||||
<TableHead size="sm">{t.settings("lists.earnings")}</TableHead>
|
||||
<TableHead size="sm">{t.common("words.actions")}</TableHead>
|
||||
<TableHead size="sm" className="center">
|
||||
{t.common("words.actions")}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody className="border-t-[12px] border-transparent [&_td]:!px-3">
|
||||
|
|
@ -107,9 +110,18 @@ export const SettingLists = () => {
|
|||
</a>
|
||||
</TableCell>
|
||||
<TableCell size="sm">
|
||||
<span className={cn("inline-flex items-center", views[row.view].className)}>
|
||||
{views[row.view].icon}
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className={cn("inline-flex items-center", views[row.view].className)}
|
||||
>
|
||||
{views[row.view].icon}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>{t(views[row.view].name)}</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell size="sm">
|
||||
<div className="flex items-center gap-1">
|
||||
|
|
@ -121,7 +133,7 @@ export const SettingLists = () => {
|
|||
<TableCell size="sm">
|
||||
<Balance>{BigInt(row.purchaseAmount || 0n)}</Balance>
|
||||
</TableCell>
|
||||
<TableCell size="sm">
|
||||
<TableCell size="sm" className="center">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
|
|
@ -189,7 +201,7 @@ const formSchema = z.object({
|
|||
const ListCreationModalContent = ({ dismiss, id }: { dismiss: () => void; id?: string }) => {
|
||||
const { t } = useTranslation(["settings", "common"])
|
||||
|
||||
const list = useFeedById(id) as ListModel
|
||||
const list = useListById(id)
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
|
|
@ -226,6 +238,7 @@ const ListCreationModalContent = ({ dismiss, id }: { dismiss: () => void; id?: s
|
|||
Queries.lists.list().invalidate()
|
||||
dismiss()
|
||||
|
||||
if (!list) return
|
||||
if (id) subscriptionActions.changeListView(id, views[list.view].view, views[values.view].view)
|
||||
},
|
||||
async onError() {
|
||||
|
|
@ -341,7 +354,7 @@ const ListCreationModalContent = ({ dismiss, id }: { dismiss: () => void; id?: s
|
|||
}
|
||||
|
||||
export const ListFeedsModalContent = ({ id }: { id: string }) => {
|
||||
const list = useFeedById(id) as ListModel
|
||||
const list = useListById(id)
|
||||
const { t } = useTranslation("settings")
|
||||
|
||||
const [feedSearchFor, setFeedSearchFor] = useState("")
|
||||
|
|
@ -368,14 +381,15 @@ export const ListFeedsModalContent = ({ id }: { id: string }) => {
|
|||
|
||||
const autocompleteSuggestions: Suggestion[] = useMemo(() => {
|
||||
return allFeeds
|
||||
.filter((feed) => !list.feedIds?.includes(feed.id))
|
||||
.filter((feed) => !list?.feedIds?.includes(feed.id))
|
||||
.map((feed) => ({
|
||||
name: feed.title,
|
||||
value: feed.id,
|
||||
}))
|
||||
}, [allFeeds, list.feedIds])
|
||||
}, [allFeeds, list?.feedIds])
|
||||
|
||||
const selectedFeedIdRef = useRef<string | null>()
|
||||
if (!list) return null
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -11,47 +11,46 @@ import { LoadingCircle } from "~/components/ui/loading"
|
|||
import { usePresentFeedFormModal } from "~/hooks/biz/useFeedFormModal"
|
||||
import { useTitle } from "~/hooks/common"
|
||||
import type { ListModel } from "~/models"
|
||||
import { useFeed } from "~/queries/feed"
|
||||
import { useList } from "~/queries/lists"
|
||||
import { useFeedById } from "~/store/feed/hooks"
|
||||
|
||||
export function Component() {
|
||||
const { id } = useParams()
|
||||
|
||||
const feed = useFeed({
|
||||
id,
|
||||
isList: true,
|
||||
const list = useList({
|
||||
id: id!,
|
||||
})
|
||||
const listData = feed.data?.feed as ListModel
|
||||
const isSubscribed = !!feed.data?.subscription
|
||||
const listData = list.data?.list as ListModel
|
||||
const isSubscribed = !!list.data?.subscription
|
||||
|
||||
const { t } = useTranslation("external")
|
||||
useTitle(feed.data?.feed.title)
|
||||
useTitle(list.data?.list.title)
|
||||
const presentFeedFormModal = usePresentFeedFormModal()
|
||||
return (
|
||||
<>
|
||||
{feed.isLoading ? (
|
||||
{list.isLoading ? (
|
||||
<LoadingCircle size="large" className="center fixed inset-0" />
|
||||
) : (
|
||||
feed.data?.feed && (
|
||||
list.data?.list && (
|
||||
<div className="mx-auto mt-12 flex max-w-5xl flex-col items-center justify-center p-4 lg:p-0">
|
||||
<FeedIcon
|
||||
fallback
|
||||
feed={feed.data.feed}
|
||||
feed={list.data.list}
|
||||
className="mask-squircle mask shrink-0"
|
||||
size={64}
|
||||
/>
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="mb-2 mt-4 flex items-center text-2xl font-bold">
|
||||
<h1>{feed.data.feed.title}</h1>
|
||||
<FeedCertification feed={feed.data.feed} />
|
||||
<h1>{list.data.list.title}</h1>
|
||||
<FeedCertification feed={list.data.list} />
|
||||
</div>
|
||||
<div className="mb-8 text-sm text-zinc-500">{feed.data.feed.description}</div>
|
||||
<div className="mb-8 text-sm text-zinc-500">{list.data.list.description}</div>
|
||||
</div>
|
||||
<div className="mb-4 text-sm">
|
||||
{t("feed.followsAndFeeds", {
|
||||
subscriptionCount: feed.data.subscriptionCount,
|
||||
subscriptionNoun: t("feed.follower", { count: feed.data.subscriptionCount }),
|
||||
feedsCount: "feedCount" in feed.data ? feed.data.feedCount : 0,
|
||||
subscriptionCount: list.data?.subscriptionCount,
|
||||
subscriptionNoun: t("feed.follower", { count: list.data?.subscriptionCount }),
|
||||
feedsCount: "feedCount" in listData ? listData.feedCount : 0,
|
||||
feedsNoun: t("feed.feeds", { count: listData?.feedIds?.length }),
|
||||
appName: APP_NAME,
|
||||
})}
|
||||
|
|
@ -82,7 +81,7 @@ export function Component() {
|
|||
{listData.feedIds
|
||||
?.slice(0, 5)
|
||||
.map((feedId) => <FeedRow feedId={feedId} key={feedId} />)}
|
||||
{"feedCount" in feed.data && (
|
||||
{"feedCount" in list.data && (
|
||||
<div
|
||||
onClick={() => {
|
||||
presentFeedFormModal({
|
||||
|
|
@ -92,7 +91,7 @@ export function Component() {
|
|||
className="text-sm text-zinc-500"
|
||||
>
|
||||
{t("feed.follow_to_view_all", {
|
||||
count: feed.data.feedCount,
|
||||
count: list.data.feedCount || 0,
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useHotkeys } from "react-hotkeys-hook"
|
|||
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuPortal,
|
||||
|
|
@ -112,7 +113,11 @@ const Item = memo(({ item }: { item: NativeMenuItem }) => {
|
|||
return <ContextMenuSeparator />
|
||||
}
|
||||
case "text": {
|
||||
const Wrapper = item.submenu ? ContextMenuSubTrigger : ContextMenuItem
|
||||
const Wrapper = item.submenu
|
||||
? ContextMenuSubTrigger
|
||||
: typeof item.checked === "boolean"
|
||||
? ContextMenuCheckboxItem
|
||||
: ContextMenuItem
|
||||
|
||||
const Sub = item.submenu ? ContextMenuSub : Fragment
|
||||
return (
|
||||
|
|
@ -122,6 +127,7 @@ const Item = memo(({ item }: { item: NativeMenuItem }) => {
|
|||
disabled={item.enabled === false || (item.click === undefined && !item.submenu)}
|
||||
onClick={onClick}
|
||||
className="flex items-center gap-2"
|
||||
checked={item.checked}
|
||||
>
|
||||
{item.icon}
|
||||
{item.label}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
import { useAuthQuery } from "~/hooks/common"
|
||||
import { defineQuery } from "~/lib/defineQuery"
|
||||
import { feedActions } from "~/store/feed"
|
||||
import { listActions } from "~/store/list"
|
||||
|
||||
export const lists = {
|
||||
list: () =>
|
||||
defineQuery(["lists"], async () => feedActions.fetchOwnedLists(), {
|
||||
defineQuery(["lists"], async () => listActions.fetchOwnedLists(), {
|
||||
rootKey: ["lists"],
|
||||
}),
|
||||
byId: ({ id }: { id: string }) =>
|
||||
defineQuery(["lists", id], async () => listActions.fetchListById(id), {
|
||||
rootKey: ["lists"],
|
||||
}),
|
||||
}
|
||||
|
||||
export const useList = ({ id }: { id: string }) =>
|
||||
useAuthQuery(lists.byId({ id }), {
|
||||
enabled: !!id,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { browserDB } from "~/database"
|
||||
import type { FeedOrListModel } from "~/models/types"
|
||||
import type { FeedModel, FeedOrListModel } from "~/models/types"
|
||||
|
||||
import { BaseService } from "./base"
|
||||
import { CleanerService } from "./cleaner"
|
||||
|
||||
type TargetModelWithId = FeedOrListModel & { id: string }
|
||||
class ServiceStatic extends BaseService<TargetModelWithId> {
|
||||
type FeedModelWithId = FeedModel & { id: string }
|
||||
class ServiceStatic extends BaseService<FeedModelWithId> {
|
||||
constructor() {
|
||||
super(browserDB.feeds)
|
||||
}
|
||||
|
|
@ -15,13 +15,13 @@ class ServiceStatic extends BaseService<TargetModelWithId> {
|
|||
|
||||
CleanerService.reset(filterData.map((d) => ({ type: "feed", id: d.id! })))
|
||||
|
||||
return this.table.bulkPut(filterData as TargetModelWithId[])
|
||||
return this.table.bulkPut(filterData as FeedModelWithId[])
|
||||
}
|
||||
|
||||
override async upsert(data: FeedOrListModel): Promise<string | null> {
|
||||
if (!data.id) return null
|
||||
CleanerService.reset([{ type: "feed", id: data.id }])
|
||||
return this.table.put(data as TargetModelWithId)
|
||||
return this.table.put(data as FeedModelWithId)
|
||||
}
|
||||
|
||||
async bulkDelete(ids: string[]) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { omit } from "lodash-es"
|
||||
|
||||
import { browserDB } from "~/database"
|
||||
import type { ListModel } from "~/models/types"
|
||||
|
||||
import { BaseService } from "./base"
|
||||
import { CleanerService } from "./cleaner"
|
||||
|
||||
class ServiceStatic extends BaseService<{ id: string }> {
|
||||
constructor() {
|
||||
super(browserDB.lists)
|
||||
}
|
||||
|
||||
override async upsertMany(data: ListModel[]) {
|
||||
CleanerService.reset(data.map((d) => ({ type: "feed", id: d.id! })))
|
||||
|
||||
// FIXME The backend should not pass these computed attributes, and these need to be removed here.
|
||||
// Subsequent refactoring of the backend data flow should not nest computed attributes
|
||||
return this.table.bulkPut(data.map((d) => omit(d, "owner")))
|
||||
}
|
||||
|
||||
override async findAll() {
|
||||
return super.findAll() as unknown as ListModel[]
|
||||
}
|
||||
|
||||
override async upsert(data: ListModel): Promise<string | null> {
|
||||
if (!data.id) return null
|
||||
CleanerService.reset([{ type: "feed", id: data.id }])
|
||||
return this.table.put(omit(data, "owner"))
|
||||
}
|
||||
|
||||
async bulkDelete(ids: string[]) {
|
||||
return this.table.bulkDelete(ids)
|
||||
}
|
||||
}
|
||||
|
||||
export const ListService = new ServiceStatic()
|
||||
|
|
@ -5,7 +5,7 @@ import { isNil, merge, omit } from "lodash-es"
|
|||
import { runTransactionInScope } from "~/database"
|
||||
import { apiClient } from "~/lib/api-fetch"
|
||||
import { getEntriesParams, omitObjectUndefinedValue } from "~/lib/utils"
|
||||
import type { CombinedEntryModel, EntryModel, FeedOrListRespModel } from "~/models"
|
||||
import type { CombinedEntryModel, EntryModel, FeedModel, FeedOrListRespModel } from "~/models"
|
||||
import { EntryService } from "~/services"
|
||||
|
||||
import { feedActions } from "../feed"
|
||||
|
|
@ -249,7 +249,7 @@ class EntryActions {
|
|||
}),
|
||||
)
|
||||
// Insert to feed store
|
||||
feedActions.upsertMany(feeds)
|
||||
feedActions.upsertMany(feeds as FeedModel[])
|
||||
const newStarIds = new Set(get().starIds)
|
||||
for (const entryId in entryCollection) {
|
||||
newStarIds.add(entryId)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ import { useRouteParams } from "~/hooks/biz/useRouteParams"
|
|||
import { apiClient } from "~/lib/api-fetch"
|
||||
import type { FeedOrListRespModel } from "~/models"
|
||||
|
||||
import { listActions } from "../list"
|
||||
import { getSubscriptionByFeedId } from "../subscription"
|
||||
import { feedActions, useFeedStore } from "./store"
|
||||
import { useFeedStore } from "./store"
|
||||
import type { FeedQueryParams } from "./types"
|
||||
|
||||
export const useFeedById = (feedId: Nullable<string>): FeedOrListRespModel | null =>
|
||||
|
|
@ -75,7 +76,7 @@ export const useAddFeedToFeedList = (options?: {
|
|||
json: payload,
|
||||
})
|
||||
|
||||
feeds.data.forEach((feed) => feedActions.addFeedToFeedList(payload.listId, feed))
|
||||
feeds.data.forEach((feed) => listActions.addFeedToFeedList(payload.listId, feed))
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success(t("lists.feeds.add.success"))
|
||||
|
|
@ -96,7 +97,7 @@ export const useRemoveFeedFromFeedList = (options?: {
|
|||
const { t } = useTranslation("settings")
|
||||
return useMutation({
|
||||
mutationFn: async (payload: { feedId: string; listId: string }) => {
|
||||
feedActions.removeFeedFromFeedList(payload.listId, payload.feedId)
|
||||
listActions.removeFeedFromFeedList(payload.listId, payload.feedId)
|
||||
await apiClient.lists.feeds.$delete({
|
||||
json: {
|
||||
listId: payload.listId,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,7 @@ import { nanoid } from "nanoid"
|
|||
import { whoami } from "~/atoms/user"
|
||||
import { runTransactionInScope } from "~/database"
|
||||
import { apiClient } from "~/lib/api-fetch"
|
||||
import type {
|
||||
FeedModel,
|
||||
FeedOrListModel,
|
||||
FeedOrListRespModel,
|
||||
ListModel,
|
||||
UserModel,
|
||||
} from "~/models"
|
||||
import type { FeedModel, FeedOrListModel, FeedOrListRespModel, UserModel } from "~/models"
|
||||
import { FeedService } from "~/services"
|
||||
|
||||
import { getSubscriptionByFeedId } from "../subscription"
|
||||
|
|
@ -30,16 +24,9 @@ class FeedActions {
|
|||
set({ feeds: {} })
|
||||
}
|
||||
|
||||
upsertMany(feeds: FeedOrListRespModel[]) {
|
||||
upsertMany(feeds: FeedModel[]) {
|
||||
runTransactionInScope(() => {
|
||||
FeedService.upsertMany(
|
||||
feeds.map((feed) => {
|
||||
if (feed.type === "list") {
|
||||
return omit(feed, ["feeds"])
|
||||
}
|
||||
return feed
|
||||
}),
|
||||
)
|
||||
FeedService.upsertMany(feeds)
|
||||
})
|
||||
set((state) =>
|
||||
produce(state, (state) => {
|
||||
|
|
@ -73,9 +60,6 @@ class FeedActions {
|
|||
const nonce = feed["nonce"] || nanoid(8)
|
||||
state.feeds[nonce] = { ...feed, id: nonce }
|
||||
}
|
||||
if ("feeds" in feed && feed.feeds) {
|
||||
this.upsertMany(feed.feeds)
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
|
@ -114,50 +98,20 @@ class FeedActions {
|
|||
})
|
||||
}
|
||||
|
||||
async addFeedToFeedList(listId: string, feed: FeedModel) {
|
||||
const list = get().feeds[listId] as ListModel
|
||||
if (!list) return
|
||||
feedActions.upsertMany([feed])
|
||||
|
||||
this.patch(listId, {
|
||||
feedIds: [feed.id, ...list.feedIds],
|
||||
})
|
||||
feedActions.upsertMany([get().feeds[listId]])
|
||||
}
|
||||
|
||||
async removeFeedFromFeedList(listId: string, feedId: string) {
|
||||
const list = get().feeds[listId] as ListModel
|
||||
if (!list) return
|
||||
|
||||
this.patch(listId, {
|
||||
feedIds: list.feedIds.filter((id) => id !== feedId),
|
||||
})
|
||||
feedActions.upsertMany([get().feeds[listId]])
|
||||
}
|
||||
|
||||
// API Fetcher
|
||||
//
|
||||
|
||||
async fetchFeedById({ id, url, isList }: FeedQueryParams) {
|
||||
const res = isList
|
||||
? await apiClient.lists.$get({
|
||||
query: {
|
||||
listId: id!,
|
||||
},
|
||||
})
|
||||
: await apiClient.feeds.$get({
|
||||
query: {
|
||||
id,
|
||||
url,
|
||||
},
|
||||
})
|
||||
async fetchFeedById({ id, url }: FeedQueryParams) {
|
||||
const res = await apiClient.feeds.$get({
|
||||
query: {
|
||||
id,
|
||||
url,
|
||||
},
|
||||
})
|
||||
|
||||
const nonce = nanoid(8)
|
||||
|
||||
const finalData = {
|
||||
...("list" in res.data ? res.data.list : res.data.feed),
|
||||
}
|
||||
|
||||
const finalData = res.data.feed
|
||||
if (!finalData.id) {
|
||||
finalData["nonce"] = nonce
|
||||
}
|
||||
|
|
@ -168,13 +122,6 @@ class FeedActions {
|
|||
feed: !finalData.id ? { ...finalData, id: nonce } : finalData,
|
||||
}
|
||||
}
|
||||
|
||||
async fetchOwnedLists() {
|
||||
const res = await apiClient.lists.list.$get()
|
||||
this.upsertMany(res.data)
|
||||
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
export const feedActions = new FeedActions()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,8 @@
|
|||
import type { FeedOrListModel, FeedOrListRespModel } from "~/models"
|
||||
import type { FeedOrListModel } from "~/models"
|
||||
|
||||
type FeedId = string
|
||||
|
||||
export interface FeedState {
|
||||
feeds: Record<FeedId, FeedOrListModel>
|
||||
}
|
||||
|
||||
export interface FeedActions {
|
||||
upsertMany: (feeds: FeedOrListRespModel[]) => void
|
||||
clear: () => void
|
||||
patch: (feedId: FeedId, patch: Partial<FeedOrListModel>) => void
|
||||
}
|
||||
|
||||
export type FeedQueryParams = { id?: string; url?: string; isList?: boolean }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import type { FeedViewType } from "~/lib/enum"
|
||||
import type { ListModel } from "~/models"
|
||||
|
||||
import { useListStore } from "./store"
|
||||
|
||||
export const useListById = (listId: Nullable<string>): ListModel | null =>
|
||||
useListStore((state) => (listId ? state.lists[listId] : null))
|
||||
|
||||
export const useListByView = (view: FeedViewType) => {
|
||||
return useListStore((state) => Object.values(state.lists).filter((list) => list.view === view))
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./hooks"
|
||||
export * from "./store"
|
||||
export * from "./types"
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import { runTransactionInScope } from "~/database"
|
||||
import { apiClient } from "~/lib/api-fetch"
|
||||
import type { FeedModel, ListModel, ListModelPoplutedFeeds } from "~/models"
|
||||
import { ListService } from "~/services/list"
|
||||
|
||||
import { feedActions } from "../feed"
|
||||
import { createImmerSetter, createZustandStore } from "../utils/helper"
|
||||
import type { ListState } from "./types"
|
||||
|
||||
export const useListStore = createZustandStore<ListState>("list")(() => ({
|
||||
lists: {},
|
||||
}))
|
||||
|
||||
const set = createImmerSetter(useListStore)
|
||||
const get = useListStore.getState
|
||||
|
||||
class ListActionStatic {
|
||||
upsertMany(lists: ListModelPoplutedFeeds[]) {
|
||||
if (lists.length === 0) return
|
||||
const feeds = [] as FeedModel[]
|
||||
set((state) => {
|
||||
for (const list of lists) {
|
||||
state.lists[list.id] = list
|
||||
|
||||
if (list.feeds)
|
||||
for (const feed of list.feeds) {
|
||||
feeds.push(feed)
|
||||
}
|
||||
}
|
||||
|
||||
feedActions.upsertMany(feeds)
|
||||
return state
|
||||
})
|
||||
|
||||
runTransactionInScope(() => ListService.upsertMany(lists))
|
||||
}
|
||||
|
||||
async fetchOwnedLists() {
|
||||
const res = await apiClient.lists.list.$get()
|
||||
this.upsertMany(res.data)
|
||||
|
||||
return res.data
|
||||
}
|
||||
|
||||
private patch(listId: string, data: Partial<ListModel>) {
|
||||
set((state) => {
|
||||
state.lists[listId] = { ...state.lists[listId], ...data }
|
||||
return state
|
||||
})
|
||||
|
||||
runTransactionInScope(async () => {
|
||||
const patchedData = get().lists[listId]
|
||||
if (!patchedData) return
|
||||
return ListService.upsert(patchedData as ListModel)
|
||||
})
|
||||
}
|
||||
async addFeedToFeedList(listId: string, feed: FeedModel) {
|
||||
const list = get().lists[listId]
|
||||
if (!list) return
|
||||
feedActions.upsertMany([feed])
|
||||
|
||||
this.patch(listId, {
|
||||
feedIds: [feed.id, ...list.feedIds],
|
||||
})
|
||||
}
|
||||
|
||||
async removeFeedFromFeedList(listId: string, feedId: string) {
|
||||
const list = get().lists[listId] as ListModel
|
||||
if (!list) return
|
||||
|
||||
this.patch(listId, {
|
||||
feedIds: list.feedIds.filter((id) => id !== feedId),
|
||||
})
|
||||
}
|
||||
|
||||
async fetchListById(id: string) {
|
||||
const res = await apiClient.lists.$get({ query: { listId: id } })
|
||||
|
||||
this.upsertMany([res.data.list])
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
export const listActions = new ListActionStatic()
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import type { ListModel } from "~/models"
|
||||
|
||||
type FeedId = string
|
||||
|
||||
export interface ListState {
|
||||
lists: Record<FeedId, ListModel>
|
||||
}
|
||||
|
|
@ -53,7 +53,6 @@ class SearchActions {
|
|||
const feedsMap = new Map(feeds.map((feed) => [feed.id, feed]))
|
||||
|
||||
const entriesFuse = this.createFuse(entries, ["title", "content", "description", "id"])
|
||||
// @ts-expect-error
|
||||
const feedsFuse = this.createFuse(feeds, ["title", "description", "id", "siteUrl", "url"])
|
||||
const subscriptionsFuse = this.createFuse(subscriptions, ["title", "category"])
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import { runTransactionInScope } from "~/database"
|
|||
import { apiClient } from "~/lib/api-fetch"
|
||||
import { FeedViewType } from "~/lib/enum"
|
||||
import { capitalizeFirstLetter } from "~/lib/utils"
|
||||
import type { SubscriptionModel } from "~/models"
|
||||
import type { FeedModel, ListModelPoplutedFeeds, SubscriptionModel } from "~/models"
|
||||
import { SubscriptionService } from "~/services"
|
||||
|
||||
import { entryActions } from "../entry"
|
||||
import { feedActions, getFeedById } from "../feed"
|
||||
import { listActions } from "../list/store"
|
||||
import { feedUnreadActions } from "../unread"
|
||||
import { createZustandStore, doMutationAndTransaction } from "../utils/helper"
|
||||
|
||||
|
|
@ -116,8 +117,19 @@ class SubscriptionActions {
|
|||
const transformedData = morphResponseData(res.data)
|
||||
|
||||
this.upsertMany(transformedData)
|
||||
|
||||
const feeds = [] as FeedModel[]
|
||||
const lists = [] as ListModelPoplutedFeeds[]
|
||||
for (const subscription of res.data) {
|
||||
if ("feeds" in subscription) {
|
||||
feeds.push(subscription.feeds)
|
||||
} else {
|
||||
lists.push(subscription.lists)
|
||||
}
|
||||
}
|
||||
this.updateCategoryOpenState(transformedData.filter((s) => s.category || s.defaultCategory))
|
||||
feedActions.upsertMany(res.data.map((s) => ("feeds" in s ? s.feeds : s.lists)))
|
||||
feedActions.upsertMany(feeds)
|
||||
listActions.upsertMany(lists)
|
||||
|
||||
return res.data
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable no-unsafe-finally */
|
||||
import { produce } from "immer"
|
||||
import { unstable_batchedUpdates } from "react-dom"
|
||||
import type { StateCreator } from "zustand"
|
||||
import type { StateCreator, StoreApi, UseBoundStore } from "zustand"
|
||||
import type { PersistStorage } from "zustand/middleware"
|
||||
import { devtools } from "zustand/middleware"
|
||||
import { shallow } from "zustand/shallow"
|
||||
|
|
@ -127,3 +128,12 @@ export const doMutationAndTransaction = async <M, T>(
|
|||
return await Promise.all([wrappedMutation(), wrappedTransaction()])
|
||||
}
|
||||
}
|
||||
|
||||
export function createImmerSetter<T>(useStore: UseBoundStore<StoreApi<T>>) {
|
||||
return (updater: (state: T) => void) =>
|
||||
useStore.setState((state) =>
|
||||
produce(state, (draft) => {
|
||||
updater(draft as T)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
"tailwindcss-animate": "1.0.7",
|
||||
"tsup": "8.3.0",
|
||||
"tsx": "4.19.1",
|
||||
"typescript": "^5.6.2",
|
||||
"typescript": "5.4.5",
|
||||
"vite": "^5.4.8",
|
||||
"vite-bundle-analyzer": "0.12.1",
|
||||
"vite-plugin-mkcert": "1.17.6",
|
||||
|
|
|
|||
337
pnpm-lock.yaml
337
pnpm-lock.yaml
|
|
@ -97,7 +97,7 @@ importers:
|
|||
version: 2.22.4(encoding@0.1.13)
|
||||
'@t3-oss/env-core':
|
||||
specifier: ^0.11.1
|
||||
version: 0.11.1(typescript@5.6.2)(zod@3.23.8)
|
||||
version: 0.11.1(typescript@5.4.5)(zod@3.23.8)
|
||||
'@tailwindcss/container-queries':
|
||||
specifier: 0.1.1
|
||||
version: 0.1.1(tailwindcss@3.4.13)
|
||||
|
|
@ -151,7 +151,7 @@ importers:
|
|||
version: 9.11.1(jiti@2.0.0)
|
||||
eslint-config-hyoban:
|
||||
specifier: ^3.1.8
|
||||
version: 3.1.8(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(tailwindcss@3.4.13)(typescript@5.6.2)
|
||||
version: 3.1.8(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(tailwindcss@3.4.13)(typescript@5.4.5)
|
||||
fake-indexeddb:
|
||||
specifier: 6.0.0
|
||||
version: 6.0.0
|
||||
|
|
@ -196,13 +196,13 @@ importers:
|
|||
version: 1.0.7(tailwindcss@3.4.13)
|
||||
tsup:
|
||||
specifier: 8.3.0
|
||||
version: 8.3.0(jiti@2.0.0)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.2)(yaml@2.5.1)
|
||||
version: 8.3.0(jiti@2.0.0)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.4.5)(yaml@2.5.1)
|
||||
tsx:
|
||||
specifier: 4.19.1
|
||||
version: 4.19.1
|
||||
typescript:
|
||||
specifier: ^5.6.2
|
||||
version: 5.6.2
|
||||
specifier: 5.4.5
|
||||
version: 5.4.5
|
||||
vite:
|
||||
specifier: ^5.4.8
|
||||
version: 5.4.8(@types/node@22.7.4)(terser@5.34.1)
|
||||
|
|
@ -214,7 +214,7 @@ importers:
|
|||
version: 1.17.6(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1))
|
||||
vite-tsconfig-paths:
|
||||
specifier: 5.0.1
|
||||
version: 5.0.1(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1))
|
||||
version: 5.0.1(typescript@5.4.5)(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1))
|
||||
vitest:
|
||||
specifier: '2.0'
|
||||
version: 2.0.5(@types/node@22.7.4)(terser@5.34.1)
|
||||
|
|
@ -312,9 +312,6 @@ importers:
|
|||
hono:
|
||||
specifier: 4.6.3
|
||||
version: 4.6.3(patch_hash=iej2g43ojhll5opwbnvyorjq4e)
|
||||
typescript:
|
||||
specifier: ^5.6.2
|
||||
version: 5.6.2
|
||||
|
||||
apps/renderer:
|
||||
dependencies:
|
||||
|
|
@ -646,9 +643,6 @@ importers:
|
|||
react-dom:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1(react@18.3.1)
|
||||
typescript:
|
||||
specifier: ^5.6.2
|
||||
version: 5.6.2
|
||||
vitest:
|
||||
specifier: '2.0'
|
||||
version: 2.0.5(@types/node@22.7.4)(terser@5.34.1)
|
||||
|
|
@ -8392,6 +8386,11 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
typescript@5.4.5:
|
||||
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
typescript@5.6.2:
|
||||
resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
|
||||
engines: {node: '>=14.17'}
|
||||
|
|
@ -10307,13 +10306,13 @@ snapshots:
|
|||
|
||||
'@eslint-community/regexpp@4.11.1': {}
|
||||
|
||||
'@eslint-react/ast@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/ast@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
birecord: 0.1.1
|
||||
string-ts: 2.2.0
|
||||
ts-pattern: 5.4.0
|
||||
|
|
@ -10322,18 +10321,18 @@ snapshots:
|
|||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@eslint-react/core@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/core@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
birecord: 0.1.1
|
||||
short-unique-id: 5.2.0
|
||||
ts-pattern: 5.4.0
|
||||
|
|
@ -10342,46 +10341,46 @@ snapshots:
|
|||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@eslint-react/eslint-plugin@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/eslint-plugin@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
eslint-plugin-react-debug: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-react-dom: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-react-hooks-extra: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-react-naming-convention: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-react-web-api: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-react-x: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-react-debug: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint-plugin-react-dom: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint-plugin-react-hooks-extra: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint-plugin-react-naming-convention: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint-plugin-react-web-api: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint-plugin-react-x: 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint-react/jsx@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/jsx@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
ts-pattern: 5.4.0
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@eslint-react/shared@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/shared@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
picomatch: 4.0.2
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
|
|
@ -10390,24 +10389,24 @@ snapshots:
|
|||
|
||||
'@eslint-react/tools@1.14.2': {}
|
||||
|
||||
'@eslint-react/types@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/types@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@eslint-react/var@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@eslint-react/var@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
ts-pattern: 5.4.0
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
|
|
@ -12217,9 +12216,9 @@ snapshots:
|
|||
|
||||
'@sindresorhus/is@4.6.0': {}
|
||||
|
||||
'@stylistic/eslint-plugin@2.8.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@stylistic/eslint-plugin@2.8.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
eslint-visitor-keys: 4.1.0
|
||||
espree: 10.2.0
|
||||
|
|
@ -12237,6 +12236,12 @@ snapshots:
|
|||
dependencies:
|
||||
defer-to-connect: 2.0.1
|
||||
|
||||
'@t3-oss/env-core@0.11.1(typescript@5.4.5)(zod@3.23.8)':
|
||||
dependencies:
|
||||
zod: 3.23.8
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
|
||||
'@t3-oss/env-core@0.11.1(typescript@5.6.2)(zod@3.23.8)':
|
||||
dependencies:
|
||||
zod: 3.23.8
|
||||
|
|
@ -12462,34 +12467,34 @@ snapshots:
|
|||
'@types/node': 22.7.4
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.11.1
|
||||
'@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 8.7.0
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.2
|
||||
natural-compare: 1.4.0
|
||||
ts-api-utils: 1.3.0(typescript@5.6.2)
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 8.7.0
|
||||
debug: 4.3.7
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -12498,21 +12503,21 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/visitor-keys': 8.7.0
|
||||
|
||||
'@typescript-eslint/type-utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@typescript-eslint/type-utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
debug: 4.3.7
|
||||
ts-api-utils: 1.3.0(typescript@5.6.2)
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/types@8.7.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.7.0(typescript@5.6.2)':
|
||||
'@typescript-eslint/typescript-estree@8.7.0(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/visitor-keys': 8.7.0
|
||||
|
|
@ -12521,18 +12526,18 @@ snapshots:
|
|||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.6.3
|
||||
ts-api-utils: 1.3.0(typescript@5.6.2)
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@2.0.0))
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.6.2)
|
||||
'@typescript-eslint/typescript-estree': 8.7.0(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -12554,17 +12559,17 @@ snapshots:
|
|||
|
||||
'@unocss/core@0.62.4': {}
|
||||
|
||||
'@unocss/eslint-config@0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@unocss/eslint-config@0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@unocss/eslint-plugin': 0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@unocss/eslint-plugin': 0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@unocss/eslint-plugin@0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)':
|
||||
'@unocss/eslint-plugin@0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@unocss/config': 0.62.4
|
||||
'@unocss/core': 0.62.4
|
||||
magic-string: 0.30.11
|
||||
|
|
@ -13435,7 +13440,7 @@ snapshots:
|
|||
config-file-ts@0.2.8-rc1:
|
||||
dependencies:
|
||||
glob: 10.4.5
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
|
||||
consola@3.2.3: {}
|
||||
|
||||
|
|
@ -14134,19 +14139,19 @@ snapshots:
|
|||
eslint: 9.11.1(jiti@2.0.0)
|
||||
find-up-simple: 1.0.0
|
||||
|
||||
eslint-config-hyoban@3.1.8(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(tailwindcss@3.4.13)(typescript@5.6.2):
|
||||
eslint-config-hyoban@3.1.8(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(tailwindcss@3.4.13)(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/eslint-plugin': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/eslint-plugin': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint/js': 9.11.1
|
||||
'@stylistic/eslint-plugin': 2.8.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@unocss/eslint-config': 0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@stylistic/eslint-plugin': 2.8.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@unocss/eslint-config': 0.62.4(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
defu: 6.1.4
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
eslint-config-flat-gitignore: 0.3.0(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-antfu: 2.7.0(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-command: 0.2.6(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-hyoban: 0.6.1(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-import-x: 4.3.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
eslint-plugin-import-x: 4.3.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint-plugin-jsonc: 2.16.0(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-package-json: 0.15.3(eslint@9.11.1(jiti@2.0.0))(jsonc-eslint-parser@2.4.0)
|
||||
eslint-plugin-react-compiler: 0.0.0-experimental-f8a5409-20240829(eslint@9.11.1(jiti@2.0.0))
|
||||
|
|
@ -14156,14 +14161,14 @@ snapshots:
|
|||
eslint-plugin-simple-import-sort: 12.1.1(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-tailwindcss: 3.17.4(tailwindcss@3.4.13)
|
||||
eslint-plugin-unicorn: 55.0.0(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))
|
||||
eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))
|
||||
globals: 15.9.0
|
||||
jsonc-eslint-parser: 2.4.0
|
||||
local-pkg: 0.5.0
|
||||
read-package-up: 11.0.0
|
||||
typescript-eslint: 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
typescript-eslint: 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/eslint-plugin'
|
||||
- supports-color
|
||||
|
|
@ -14191,9 +14196,9 @@ snapshots:
|
|||
dependencies:
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
|
||||
eslint-plugin-import-x@4.3.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-import-x@4.3.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
debug: 4.3.7
|
||||
doctrine: 3.0.0
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
|
|
@ -14244,63 +14249,63 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-debug@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-react-debug@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
string-ts: 2.2.0
|
||||
ts-pattern: 5.4.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-dom@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-react-dom@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
ts-pattern: 5.4.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-hooks-extra@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-react-hooks-extra@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
ts-pattern: 5.4.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -14308,22 +14313,22 @@ snapshots:
|
|||
dependencies:
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
|
||||
eslint-plugin-react-naming-convention@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-react-naming-convention@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
ts-pattern: 5.4.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -14331,44 +14336,44 @@ snapshots:
|
|||
dependencies:
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
|
||||
eslint-plugin-react-web-api@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-react-web-api@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
birecord: 0.1.1
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
ts-pattern: 5.4.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-x@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
eslint-plugin-react-x@1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/ast': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/core': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/jsx': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/shared': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/tools': 1.14.2
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@eslint-react/types': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@eslint-react/var': 1.14.2(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 8.7.0
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/types': 8.7.0
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
is-immutable-type: 5.0.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
is-immutable-type: 5.0.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
ts-pattern: 5.4.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -14413,11 +14418,11 @@ snapshots:
|
|||
semver: 7.6.3
|
||||
strip-indent: 3.0.0
|
||||
|
||||
eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0)):
|
||||
eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0)):
|
||||
dependencies:
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
|
||||
eslint-scope@8.1.0:
|
||||
dependencies:
|
||||
|
|
@ -15389,13 +15394,13 @@ snapshots:
|
|||
|
||||
is-hexadecimal@2.0.1: {}
|
||||
|
||||
is-immutable-type@5.0.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
is-immutable-type@5.0.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
eslint: 9.11.1(jiti@2.0.0)
|
||||
ts-api-utils: 1.3.0(typescript@5.6.2)
|
||||
ts-declaration-location: 1.0.4(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
ts-declaration-location: 1.0.4(typescript@5.4.5)
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -18065,26 +18070,26 @@ snapshots:
|
|||
dependencies:
|
||||
utf8-byte-length: 1.0.5
|
||||
|
||||
ts-api-utils@1.3.0(typescript@5.6.2):
|
||||
ts-api-utils@1.3.0(typescript@5.4.5):
|
||||
dependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
|
||||
ts-declaration-location@1.0.4(typescript@5.6.2):
|
||||
ts-declaration-location@1.0.4(typescript@5.4.5):
|
||||
dependencies:
|
||||
minimatch: 10.0.1
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
ts-pattern@5.4.0: {}
|
||||
|
||||
tsconfck@3.1.1(typescript@5.6.2):
|
||||
tsconfck@3.1.1(typescript@5.4.5):
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
|
||||
tslib@2.7.0: {}
|
||||
|
||||
tsup@8.3.0(jiti@2.0.0)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.2)(yaml@2.5.1):
|
||||
tsup@8.3.0(jiti@2.0.0)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.4.5)(yaml@2.5.1):
|
||||
dependencies:
|
||||
bundle-require: 5.0.0(esbuild@0.23.1)
|
||||
cac: 6.7.14
|
||||
|
|
@ -18104,7 +18109,7 @@ snapshots:
|
|||
tree-kill: 1.2.2
|
||||
optionalDependencies:
|
||||
postcss: 8.4.47
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- supports-color
|
||||
|
|
@ -18133,17 +18138,19 @@ snapshots:
|
|||
|
||||
type-fest@4.26.1: {}
|
||||
|
||||
typescript-eslint@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2):
|
||||
typescript-eslint@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.6.2)
|
||||
'@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5))(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@2.0.0))(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
|
||||
typescript@5.4.5: {}
|
||||
|
||||
typescript@5.6.2: {}
|
||||
|
||||
ufo@1.5.4: {}
|
||||
|
|
@ -18374,11 +18381,11 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-tsconfig-paths@5.0.1(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1)):
|
||||
vite-tsconfig-paths@5.0.1(typescript@5.4.5)(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1)):
|
||||
dependencies:
|
||||
debug: 4.3.7
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.1(typescript@5.6.2)
|
||||
tsconfck: 3.1.1(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
vite: 5.4.8(@types/node@22.7.4)(terser@5.34.1)
|
||||
transitivePeerDependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue