mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
refactor: remove react-hotkeys-hook and use inertia propos instead of recreating a local store
This commit is contained in:
@@ -3,8 +3,8 @@ import { route } from '@izzyjs/route/client';
|
|||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { AiFillFolderOpen, AiOutlineFolder } from 'react-icons/ai';
|
import { AiFillFolderOpen, AiOutlineFolder } from 'react-icons/ai';
|
||||||
|
import { useActiveCollection } from '~/hooks/collections/use_active_collection';
|
||||||
import { appendCollectionId } from '~/lib/navigation';
|
import { appendCollectionId } from '~/lib/navigation';
|
||||||
import { useActiveCollection } from '~/stores/collection_store';
|
|
||||||
import { CollectionWithLinks } from '~/types/app';
|
import { CollectionWithLinks } from '~/types/app';
|
||||||
import classes from './collection_item.module.css';
|
import classes from './collection_item.module.css';
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ export default function CollectionItem({
|
|||||||
collection: CollectionWithLinks;
|
collection: CollectionWithLinks;
|
||||||
}) {
|
}) {
|
||||||
const itemRef = useRef<HTMLAnchorElement>(null);
|
const itemRef = useRef<HTMLAnchorElement>(null);
|
||||||
const { activeCollection } = useActiveCollection();
|
const activeCollection = useActiveCollection();
|
||||||
const isActiveCollection = collection.id === activeCollection?.id;
|
const isActiveCollection = collection.id === activeCollection?.id;
|
||||||
const FolderIcon = isActiveCollection ? AiFillFolderOpen : AiOutlineFolder;
|
const FolderIcon = isActiveCollection ? AiFillFolderOpen : AiOutlineFolder;
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,12 @@
|
|||||||
import { router } from '@inertiajs/react';
|
|
||||||
import { route } from '@izzyjs/route/client';
|
|
||||||
import { Box, ScrollArea, Text } from '@mantine/core';
|
import { Box, ScrollArea, Text } from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import CollectionItem from '~/components/dashboard/collection/item/collection_item';
|
import CollectionItem from '~/components/dashboard/collection/item/collection_item';
|
||||||
import useShortcut from '~/hooks/use_shortcut';
|
import { useCollections } from '~/hooks/collections/use_collections';
|
||||||
import { appendCollectionId } from '~/lib/navigation';
|
|
||||||
import { useActiveCollection, useCollections } from '~/stores/collection_store';
|
|
||||||
import styles from './collection_list.module.css';
|
import styles from './collection_list.module.css';
|
||||||
|
|
||||||
export default function CollectionList() {
|
export default function CollectionList() {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { collections } = useCollections();
|
const collections = useCollections();
|
||||||
const { activeCollection, setActiveCollection } = useActiveCollection();
|
|
||||||
|
|
||||||
const replaceUrl = (collectionId: number) =>
|
|
||||||
router.get(appendCollectionId(route('dashboard').path, collectionId));
|
|
||||||
|
|
||||||
const goToPreviousCollection = () => {
|
|
||||||
const currentCategoryIndex = collections.findIndex(
|
|
||||||
({ id }) => id === activeCollection?.id
|
|
||||||
);
|
|
||||||
if (currentCategoryIndex === -1 || currentCategoryIndex === 0) return;
|
|
||||||
|
|
||||||
const collection = collections[currentCategoryIndex - 1];
|
|
||||||
replaceUrl(collection.id);
|
|
||||||
setActiveCollection(collection);
|
|
||||||
};
|
|
||||||
|
|
||||||
const goToNextCollection = () => {
|
|
||||||
const currentCategoryIndex = collections.findIndex(
|
|
||||||
({ id }) => id === activeCollection?.id
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
currentCategoryIndex === -1 ||
|
|
||||||
currentCategoryIndex === collections.length - 1
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const collection = collections[currentCategoryIndex + 1];
|
|
||||||
replaceUrl(collection.id);
|
|
||||||
setActiveCollection(collection);
|
|
||||||
};
|
|
||||||
|
|
||||||
useShortcut('ARROW_UP', goToPreviousCollection);
|
|
||||||
useShortcut('ARROW_DOWN', goToNextCollection);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={styles.sideMenu}>
|
<Box className={styles.sideMenu}>
|
||||||
<Box className={styles.listContainer}>
|
<Box className={styles.listContainer}>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import { GoPencil } from 'react-icons/go';
|
|||||||
import { IoIosAddCircleOutline } from 'react-icons/io';
|
import { IoIosAddCircleOutline } from 'react-icons/io';
|
||||||
import { IoTrashOutline } from 'react-icons/io5';
|
import { IoTrashOutline } from 'react-icons/io5';
|
||||||
import { ShareCollection } from '~/components/share/share_collection';
|
import { ShareCollection } from '~/components/share/share_collection';
|
||||||
|
import { useActiveCollection } from '~/hooks/collections/use_active_collection';
|
||||||
import { appendCollectionId } from '~/lib/navigation';
|
import { appendCollectionId } from '~/lib/navigation';
|
||||||
import { useActiveCollection } from '~/stores/collection_store';
|
|
||||||
import { Visibility } from '~/types/app';
|
import { Visibility } from '~/types/app';
|
||||||
|
|
||||||
interface DashboardHeaderProps {
|
interface DashboardHeaderProps {
|
||||||
@@ -32,7 +32,7 @@ interface DashboardHeaderProps {
|
|||||||
}
|
}
|
||||||
export function DashboardHeader({ navbar, aside }: DashboardHeaderProps) {
|
export function DashboardHeader({ navbar, aside }: DashboardHeaderProps) {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { activeCollection } = useActiveCollection();
|
const activeCollection = useActiveCollection();
|
||||||
return (
|
return (
|
||||||
<AppShell.Header style={{ display: 'flex', alignItems: 'center' }}>
|
<AppShell.Header style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<Group justify="space-between" px="md" flex={1} wrap="nowrap">
|
<Group justify="space-between" px="md" flex={1} wrap="nowrap">
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ import { PiGearLight } from 'react-icons/pi';
|
|||||||
import { UserCard } from '~/components/common/user_card';
|
import { UserCard } from '~/components/common/user_card';
|
||||||
import { FavoriteList } from '~/components/dashboard/favorite/favorite_list';
|
import { FavoriteList } from '~/components/dashboard/favorite/favorite_list';
|
||||||
import { SearchSpotlight } from '~/components/search/search';
|
import { SearchSpotlight } from '~/components/search/search';
|
||||||
|
import { useActiveCollection } from '~/hooks/collections/use_active_collection';
|
||||||
import { useAuth } from '~/hooks/use_auth';
|
import { useAuth } from '~/hooks/use_auth';
|
||||||
import useShortcut from '~/hooks/use_shortcut';
|
import useShortcut from '~/hooks/use_shortcut';
|
||||||
import { appendCollectionId } from '~/lib/navigation';
|
import { appendCollectionId } from '~/lib/navigation';
|
||||||
import { useActiveCollection } from '~/stores/collection_store';
|
|
||||||
import { useGlobalHotkeysStore } from '~/stores/global_hotkeys_store';
|
import { useGlobalHotkeysStore } from '~/stores/global_hotkeys_store';
|
||||||
|
|
||||||
interface DashboardNavbarProps {
|
interface DashboardNavbarProps {
|
||||||
@@ -34,7 +34,7 @@ export function DashboardNavbar({ isOpen, toggle }: DashboardNavbarProps) {
|
|||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { isAuthenticated, user } = useAuth();
|
const { isAuthenticated, user } = useAuth();
|
||||||
|
|
||||||
const { activeCollection } = useActiveCollection();
|
const activeCollection = useActiveCollection();
|
||||||
const { globalHotkeysEnabled, setGlobalHotkeysEnabled } =
|
const { globalHotkeysEnabled, setGlobalHotkeysEnabled } =
|
||||||
useGlobalHotkeysStore();
|
useGlobalHotkeysStore();
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Flex, Group, Stack, Text } from '@mantine/core';
|
import { Flex, Group, Stack, Text } from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FavoriteItem } from '~/components/dashboard/favorite/item/favorite_item';
|
import { FavoriteItem } from '~/components/dashboard/favorite/item/favorite_item';
|
||||||
import { useFavorites } from '~/stores/collection_store';
|
import { useFavoriteLinks } from '~/hooks/collections/use_favorite_links';
|
||||||
|
|
||||||
export function FavoriteList() {
|
export function FavoriteList() {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { favorites } = useFavorites();
|
const favoriteLinks = useFavoriteLinks();
|
||||||
|
|
||||||
if (favorites.length === 0) {
|
if (favoriteLinks.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Group justify="center">
|
<Group justify="center">
|
||||||
<Text c="dimmed" size="sm" mt="sm">
|
<Text c="dimmed" size="sm" mt="sm">
|
||||||
@@ -20,10 +20,10 @@ export function FavoriteList() {
|
|||||||
return (
|
return (
|
||||||
<Flex direction="column">
|
<Flex direction="column">
|
||||||
<Text c="dimmed" mt="xs" ml="md" mb={4}>
|
<Text c="dimmed" mt="xs" ml="md" mb={4}>
|
||||||
{t('favorite')} • {favorites.length}
|
{t('favorite')} • {favoriteLinks.length}
|
||||||
</Text>
|
</Text>
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
{favorites.map((link) => (
|
{favoriteLinks.map((link) => (
|
||||||
<FavoriteItem link={link} key={link.id} />
|
<FavoriteItem link={link} key={link.id} />
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Link as InertiaLink } from '@inertiajs/react';
|
import { Link as InertiaLink, router } from '@inertiajs/react';
|
||||||
import { route } from '@izzyjs/route/client';
|
import { route } from '@izzyjs/route/client';
|
||||||
import { ActionIcon, Menu } from '@mantine/core';
|
import { ActionIcon, Menu } from '@mantine/core';
|
||||||
import { MouseEvent } from 'react';
|
import { MouseEvent } from 'react';
|
||||||
@@ -10,7 +10,6 @@ import { IoTrashOutline } from 'react-icons/io5';
|
|||||||
import { MdFavorite, MdFavoriteBorder } from 'react-icons/md';
|
import { MdFavorite, MdFavoriteBorder } from 'react-icons/md';
|
||||||
import { onFavorite } from '~/lib/favorite';
|
import { onFavorite } from '~/lib/favorite';
|
||||||
import { appendCollectionId, appendLinkId } from '~/lib/navigation';
|
import { appendCollectionId, appendLinkId } from '~/lib/navigation';
|
||||||
import { useFavorites } from '~/stores/collection_store';
|
|
||||||
import { Link, PublicLink } from '~/types/app';
|
import { Link, PublicLink } from '~/types/app';
|
||||||
|
|
||||||
interface LinksControlsProps {
|
interface LinksControlsProps {
|
||||||
@@ -21,10 +20,14 @@ export default function LinkControls({
|
|||||||
link,
|
link,
|
||||||
showGoToCollection = false,
|
showGoToCollection = false,
|
||||||
}: LinksControlsProps) {
|
}: LinksControlsProps) {
|
||||||
const { toggleFavorite } = useFavorites();
|
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
|
|
||||||
const onFavoriteCallback = () => toggleFavorite(link.id);
|
const onFavoriteCallback = () => {
|
||||||
|
const path = route('link.toggle-favorite', {
|
||||||
|
params: { id: link.id.toString() },
|
||||||
|
}).path;
|
||||||
|
router.put(path);
|
||||||
|
};
|
||||||
const handleStopPropagation = (event: MouseEvent<HTMLButtonElement>) =>
|
const handleStopPropagation = (event: MouseEvent<HTMLButtonElement>) =>
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import { route } from '@izzyjs/route/client';
|
|||||||
import { Anchor, Box, Text } from '@mantine/core';
|
import { Anchor, Box, Text } from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { LinkListProps } from '~/components/dashboard/link/list/link_list';
|
import type { LinkListProps } from '~/components/dashboard/link/list/link_list';
|
||||||
|
import { useActiveCollection } from '~/hooks/collections/use_active_collection';
|
||||||
import { appendCollectionId } from '~/lib/navigation';
|
import { appendCollectionId } from '~/lib/navigation';
|
||||||
import { useActiveCollection } from '~/stores/collection_store';
|
|
||||||
import styles from './no_link.module.css';
|
import styles from './no_link.module.css';
|
||||||
|
|
||||||
interface NoLinkProps extends LinkListProps {}
|
interface NoLinkProps extends LinkListProps {}
|
||||||
|
|
||||||
export function NoLink({ hideMenu }: NoLinkProps) {
|
export function NoLink({ hideMenu }: NoLinkProps) {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { activeCollection } = useActiveCollection();
|
const activeCollection = useActiveCollection();
|
||||||
return (
|
return (
|
||||||
<Box className={styles.noCollection} p="xl">
|
<Box className={styles.noCollection} p="xl">
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
25
inertia/hooks/collections/use_active_collection.tsx
Normal file
25
inertia/hooks/collections/use_active_collection.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { PageProps } from '@adonisjs/inertia/types';
|
||||||
|
import { usePage } from '@inertiajs/react';
|
||||||
|
import { CollectionWithLinks } from '~/types/app';
|
||||||
|
|
||||||
|
interface UseActiveCollectionProps {
|
||||||
|
activeCollection?: CollectionWithLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useActiveCollection = () => {
|
||||||
|
const { props } = usePage<PageProps & UseActiveCollectionProps>();
|
||||||
|
return props.activeCollection;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WithActiveCollectionProps = {
|
||||||
|
activeCollection?: CollectionWithLinks;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withActiveCollection = (
|
||||||
|
Component: React.ComponentType<WithActiveCollectionProps>
|
||||||
|
) => {
|
||||||
|
return (props: WithActiveCollectionProps) => {
|
||||||
|
const activeCollection = useActiveCollection();
|
||||||
|
return <Component {...props} activeCollection={activeCollection} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
31
inertia/hooks/collections/use_collection_list_selector.tsx
Normal file
31
inertia/hooks/collections/use_collection_list_selector.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { IoListOutline } from 'react-icons/io5';
|
||||||
|
import { TbHash } from 'react-icons/tb';
|
||||||
|
import { ValueWithIcon } from '~/components/common/combo_list/combo_list';
|
||||||
|
import { usePersisted } from '~/hooks/use_persisted';
|
||||||
|
|
||||||
|
const listDisplayOptions: ValueWithIcon[] = [
|
||||||
|
{
|
||||||
|
label: 'Inline',
|
||||||
|
value: 'inline',
|
||||||
|
icon: <TbHash size={20} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'List',
|
||||||
|
value: 'list',
|
||||||
|
icon: <IoListOutline size={20} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
type ListDisplay = (typeof listDisplayOptions)[number]['value'];
|
||||||
|
|
||||||
|
export const useCollectionListSelector = () => {
|
||||||
|
const [listDisplay, setListDisplay] = usePersisted<ListDisplay>(
|
||||||
|
'inline',
|
||||||
|
'list'
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
listDisplay,
|
||||||
|
listDisplayOptions,
|
||||||
|
setListDisplay,
|
||||||
|
};
|
||||||
|
};
|
||||||
25
inertia/hooks/collections/use_collections.tsx
Normal file
25
inertia/hooks/collections/use_collections.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { PageProps } from '@adonisjs/inertia/types';
|
||||||
|
import { usePage } from '@inertiajs/react';
|
||||||
|
import { CollectionWithLinks } from '~/types/app';
|
||||||
|
|
||||||
|
interface UseCollectionsProps {
|
||||||
|
collections: CollectionWithLinks[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCollections = () => {
|
||||||
|
const { props } = usePage<PageProps & UseCollectionsProps>();
|
||||||
|
return props.collections;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WithCollectionsProps = {
|
||||||
|
collections: CollectionWithLinks[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withCollections = <T extends object>(
|
||||||
|
Component: React.ComponentType<T & WithCollectionsProps>
|
||||||
|
): React.ComponentType<Omit<T, 'collections'>> => {
|
||||||
|
return (props: Omit<T, 'collections'>) => {
|
||||||
|
const collections = useCollections();
|
||||||
|
return <Component {...(props as T)} collections={collections} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
12
inertia/hooks/collections/use_favorite_links.tsx
Normal file
12
inertia/hooks/collections/use_favorite_links.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { PageProps } from '@adonisjs/inertia/types';
|
||||||
|
import { usePage } from '@inertiajs/react';
|
||||||
|
import { LinkWithCollection } from '~/types/app';
|
||||||
|
|
||||||
|
interface UseFavoriteLinksProps {
|
||||||
|
favoriteLinks: LinkWithCollection[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFavoriteLinks = () => {
|
||||||
|
const { props } = usePage<PageProps & UseFavoriteLinksProps>();
|
||||||
|
return props.favoriteLinks;
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import KEYS from '#core/constants/keys';
|
import KEYS from '#core/constants/keys';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from '@mantine/hooks';
|
||||||
import { useGlobalHotkeysStore } from '~/stores/global_hotkeys_store';
|
import { useGlobalHotkeysStore } from '~/stores/global_hotkeys_store';
|
||||||
|
|
||||||
type ShortcutOptions = {
|
type ShortcutOptions = {
|
||||||
@@ -16,15 +16,12 @@ export default function useShortcut(
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const { globalHotkeysEnabled } = useGlobalHotkeysStore();
|
const { globalHotkeysEnabled } = useGlobalHotkeysStore();
|
||||||
|
const isEnabled = disableGlobalCheck
|
||||||
|
? enabled
|
||||||
|
: enabled && globalHotkeysEnabled;
|
||||||
return useHotkeys(
|
return useHotkeys(
|
||||||
KEYS[key],
|
[[KEYS[key], () => isEnabled && cb(), { preventDefault: true }]],
|
||||||
(event) => {
|
undefined,
|
||||||
event.preventDefault();
|
true
|
||||||
cb();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: disableGlobalCheck ? enabled : enabled && globalHotkeysEnabled,
|
|
||||||
enableOnFormTags: ['INPUT'],
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { Flex, Text } from '@mantine/core';
|
import { Flex, Text } from '@mantine/core';
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { LinkList } from '~/components/dashboard/link/list/link_list';
|
import { LinkList } from '~/components/dashboard/link/list/link_list';
|
||||||
import { useCollectionsSetter } from '~/stores/collection_store';
|
|
||||||
import type { CollectionWithLinks, PublicUser } from '~/types/app';
|
import type { CollectionWithLinks, PublicUser } from '~/types/app';
|
||||||
|
|
||||||
interface SharedPageProps {
|
interface SharedPageProps {
|
||||||
@@ -11,12 +9,6 @@ interface SharedPageProps {
|
|||||||
|
|
||||||
export default function SharedPage({ collection }: SharedPageProps) {
|
export default function SharedPage({ collection }: SharedPageProps) {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { setActiveCollection } = useCollectionsSetter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setActiveCollection(collection);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex direction="column">
|
<Flex direction="column">
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
import { create } from 'zustand';
|
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
|
||||||
import { CollectionWithLinks, Link, LinkWithCollection } from '~/types/app';
|
|
||||||
|
|
||||||
type Collections = CollectionWithLinks[];
|
|
||||||
|
|
||||||
interface CollectionStore {
|
|
||||||
collections: Collections;
|
|
||||||
_setCollections: (collections: Collections) => void;
|
|
||||||
|
|
||||||
activeCollection: CollectionWithLinks | null;
|
|
||||||
setActiveCollection: (collection: CollectionWithLinks) => void;
|
|
||||||
|
|
||||||
favorites: LinkWithCollection[];
|
|
||||||
toggleFavorite: (link: Link['id']) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useCollectionStore = create<CollectionStore>((set, get) => ({
|
|
||||||
collections: [],
|
|
||||||
_setCollections: (collections) => {
|
|
||||||
const favorites = getFavoriteLinks(collections);
|
|
||||||
set({ collections, favorites });
|
|
||||||
},
|
|
||||||
|
|
||||||
activeCollection: null,
|
|
||||||
setActiveCollection: (collection) => set({ activeCollection: collection }),
|
|
||||||
|
|
||||||
favorites: [],
|
|
||||||
toggleFavorite: (linkId) => {
|
|
||||||
const { collections } = get();
|
|
||||||
let linkIndex = 0;
|
|
||||||
const collectionIndex = collections.findIndex(({ links }) => {
|
|
||||||
const lIndex = links.findIndex((l) => l.id === linkId);
|
|
||||||
if (lIndex !== -1) {
|
|
||||||
linkIndex = lIndex;
|
|
||||||
}
|
|
||||||
return lIndex !== -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
const collectionLink = collections[collectionIndex].links[linkIndex];
|
|
||||||
const collectionsCopy = [...collections];
|
|
||||||
collectionsCopy[collectionIndex].links[linkIndex] = {
|
|
||||||
...collectionLink,
|
|
||||||
favorite: !collectionLink.favorite,
|
|
||||||
};
|
|
||||||
|
|
||||||
set({
|
|
||||||
collections: collectionsCopy,
|
|
||||||
activeCollection: collectionsCopy[collectionIndex],
|
|
||||||
favorites: getFavoriteLinks(collectionsCopy),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const useActiveCollection = () =>
|
|
||||||
useCollectionStore(
|
|
||||||
useShallow((state) => ({
|
|
||||||
activeCollection: state.activeCollection,
|
|
||||||
setActiveCollection: state.setActiveCollection,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
export const useCollections = () =>
|
|
||||||
useCollectionStore(
|
|
||||||
useShallow((state) => ({ collections: state.collections }))
|
|
||||||
);
|
|
||||||
|
|
||||||
export const useFavorites = () =>
|
|
||||||
useCollectionStore(
|
|
||||||
useShallow((state) => ({
|
|
||||||
favorites: state.favorites,
|
|
||||||
toggleFavorite: state.toggleFavorite,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
export function useCollectionsSetter() {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
const { _setCollections, setActiveCollection } = useCollectionStore();
|
|
||||||
return { _setCollections, setActiveCollection };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFavoriteLinks(collections: Collections) {
|
|
||||||
return collections.reduce((acc, collection) => {
|
|
||||||
collection.links.forEach((link) => {
|
|
||||||
if (link.favorite) {
|
|
||||||
const newLink: LinkWithCollection = { ...link, collection };
|
|
||||||
acc.push(newLink);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return acc;
|
|
||||||
}, [] as LinkWithCollection[]);
|
|
||||||
}
|
|
||||||
@@ -100,7 +100,6 @@
|
|||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-hotkeys-hook": "^5.1.0",
|
|
||||||
"react-i18next": "^15.6.1",
|
"react-i18next": "^15.6.1",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
|
|||||||
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@@ -104,9 +104,6 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.1.1
|
specifier: ^19.1.1
|
||||||
version: 19.1.1(react@19.1.1)
|
version: 19.1.1(react@19.1.1)
|
||||||
react-hotkeys-hook:
|
|
||||||
specifier: ^5.1.0
|
|
||||||
version: 5.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
|
||||||
react-i18next:
|
react-i18next:
|
||||||
specifier: ^15.6.1
|
specifier: ^15.6.1
|
||||||
version: 15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)
|
version: 15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)
|
||||||
@@ -4349,12 +4346,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^19.1.1
|
react: ^19.1.1
|
||||||
|
|
||||||
react-hotkeys-hook@5.1.0:
|
|
||||||
resolution: {integrity: sha512-GCNGXjBzV9buOS3REoQFmSmE4WTvBhYQ0YrAeeMZI83bhXg3dRWsLHXDutcVDdEjwJqJCxk5iewWYX5LtFUd7g==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
react-dom: '>=16.8.0'
|
|
||||||
|
|
||||||
react-i18next@15.6.1:
|
react-i18next@15.6.1:
|
||||||
resolution: {integrity: sha512-uGrzSsOUUe2sDBG/+FJq2J1MM+Y4368/QW8OLEKSFvnDflHBbZhSd1u3UkW0Z06rMhZmnB/AQrhCpYfE5/5XNg==}
|
resolution: {integrity: sha512-uGrzSsOUUe2sDBG/+FJq2J1MM+Y4368/QW8OLEKSFvnDflHBbZhSd1u3UkW0Z06rMhZmnB/AQrhCpYfE5/5XNg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -9689,11 +9680,6 @@ snapshots:
|
|||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
scheduler: 0.26.0
|
scheduler: 0.26.0
|
||||||
|
|
||||||
react-hotkeys-hook@5.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
|
||||||
dependencies:
|
|
||||||
react: 19.1.1
|
|
||||||
react-dom: 19.1.1(react@19.1.1)
|
|
||||||
|
|
||||||
react-i18next@15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2):
|
react-i18next@15.6.1(i18next@25.3.2(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.2
|
'@babel/runtime': 7.28.2
|
||||||
|
|||||||
Reference in New Issue
Block a user