From c4b85c0dcbaa030655a55e84dd2fd73190cebab0 Mon Sep 17 00:00:00 2001 From: Paul Ccari <46382556+paulclindo@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:55:30 -0500 Subject: [PATCH] feat: implement pagination inboxes (#617) * esllint * refactor * feat: pagination wip * feat: change inbox name updated to v2 and fixes * fixes * updates --- .../use-onboarding-stepper.ts | 9 +- .../components/reset-connection-dialog.tsx | 138 +++++++++++++++++ .../components/tools/components/tool-card.tsx | 9 +- .../src/hooks/use-current-inbox.ts | 10 +- .../src/lib/shinkai-node-overlay.tsx | 2 +- .../shinkai-desktop/src/pages/chat/layout.tsx | 141 +++++++++++------- .../src/pages/galxe-subscriptions.tsx | 8 +- .../src/pages/layout/main-layout.tsx | 141 +----------------- .../edit-inbox-name-dialog.tsx | 11 +- libs/shinkai-message-ts/src/api/jobs/index.ts | 36 +++++ libs/shinkai-message-ts/src/api/jobs/types.ts | 19 +++ .../lib/mutations/updateInboxName/index.ts | 34 ----- .../lib/mutations/updateInboxName/types.ts | 12 -- libs/shinkai-node-state/src/v2/constants.ts | 1 + .../src/v2/mutations/updateInboxName/index.ts | 17 +++ .../src/v2/mutations/updateInboxName/types.ts | 10 ++ .../updateInboxName/useUpdateInboxName.ts | 6 +- .../src/v2/queries/getInboxes/index.ts | 20 ++- .../src/v2/queries/getInboxes/types.ts | 15 +- .../v2/queries/getInboxes/useGetInboxes.ts | 10 +- .../getInboxes/useGetInboxesWithPagination.ts | 44 ++++++ 21 files changed, 410 insertions(+), 283 deletions(-) create mode 100644 apps/shinkai-desktop/src/components/reset-connection-dialog.tsx delete mode 100644 libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts delete mode 100644 libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/updateInboxName/index.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/updateInboxName/types.ts rename libs/shinkai-node-state/src/{lib => v2}/mutations/updateInboxName/useUpdateInboxName.ts (80%) create mode 100644 libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxesWithPagination.ts diff --git a/apps/shinkai-desktop/src/components/onboarding-checklist/use-onboarding-stepper.ts b/apps/shinkai-desktop/src/components/onboarding-checklist/use-onboarding-stepper.ts index 7a5c12471..454ca556a 100644 --- a/apps/shinkai-desktop/src/components/onboarding-checklist/use-onboarding-stepper.ts +++ b/apps/shinkai-desktop/src/components/onboarding-checklist/use-onboarding-stepper.ts @@ -1,7 +1,7 @@ import { flattenDirectoryContents } from '@shinkai_network/shinkai-node-state/lib/utils/files'; import { useGetListDirectoryContents } from '@shinkai_network/shinkai-node-state/v2/queries/getDirectoryContents/useGetListDirectoryContents'; import { useGetHealth } from '@shinkai_network/shinkai-node-state/v2/queries/getHealth/useGetHealth'; -import { useGetInboxes } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxes'; +import { useGetInboxesWithPagination } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxesWithPagination'; import { useGetLLMProviders } from '@shinkai_network/shinkai-node-state/v2/queries/getLLMProviders/useGetLLMProviders'; import { useMap } from '@shinkai_network/shinkai-ui/hooks'; import { useEffect } from 'react'; @@ -27,7 +27,7 @@ export const useOnboardingSteps = () => { token: auth?.api_v2_key ?? '', }); - const { inboxes } = useGetInboxes({ + const { data: inboxesPagination } = useGetInboxesWithPagination({ nodeAddress: auth?.node_address ?? '', token: auth?.api_v2_key ?? '', }); @@ -69,6 +69,9 @@ export const useOnboardingSteps = () => { }, [VRFiles]); useEffect(() => { + const inboxes = + inboxesPagination?.pages.flatMap((page) => page.inboxes) ?? []; + if (inboxes.length > 1) { currentStepsMap.set(GetStartedSteps.CreateAIChat, GetStartedStatus.Done); } @@ -87,7 +90,7 @@ export const useOnboardingSteps = () => { GetStartedStatus.Done, ); } - }, [inboxes]); + }, [inboxesPagination]); // useEffect(() => { // if ((subscriptionFolder ?? [])?.length > 0) { diff --git a/apps/shinkai-desktop/src/components/reset-connection-dialog.tsx b/apps/shinkai-desktop/src/components/reset-connection-dialog.tsx new file mode 100644 index 000000000..411e25aa7 --- /dev/null +++ b/apps/shinkai-desktop/src/components/reset-connection-dialog.tsx @@ -0,0 +1,138 @@ +import { useSubmitRegistrationNoCode } from '@shinkai_network/shinkai-node-state/v2/mutations/submitRegistation/useSubmitRegistrationNoCode'; +import { useGetEncryptionKeys } from '@shinkai_network/shinkai-node-state/v2/queries/getEncryptionKeys/useGetEncryptionKeys'; +import { + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + Button, +} from '@shinkai_network/shinkai-ui'; +import { submitRegistrationNoCodeError } from '@shinkai_network/shinkai-ui/helpers'; +import { XIcon } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; + +import { + useShinkaiNodeKillMutation, + useShinkaiNodeRemoveStorageMutation, + useShinkaiNodeSpawnMutation, +} from '../lib/shinkai-node-manager/shinkai-node-manager-client'; +import { useAuth } from '../store/auth'; +import { useShinkaiNodeManager } from '../store/shinkai-node-manager'; + +export const ResetConnectionDialog = ({ + isOpen, + onOpenChange, + allowClose = false, +}: { + isOpen: boolean; + onOpenChange: (open: boolean) => void; + allowClose?: boolean; +}) => { + const { mutateAsync: shinkaiNodeKill, isPending: isShinkaiNodeKillPending } = + useShinkaiNodeKillMutation(); + const { + mutateAsync: shinkaiNodeSpawn, + isPending: isShinkaiNodeSpawnPending, + } = useShinkaiNodeSpawnMutation({ + onSuccess: async () => { + if (!encryptionKeys) return; + await submitRegistrationNoCode({ + profile: 'main', + registration_name: 'main_device', + node_address: 'http://127.0.0.1:9550', + ...encryptionKeys, + }); + }, + }); + const { + mutateAsync: shinkaiNodeRemoveStorage, + isPending: isShinkaiNodeRemoveStoragePending, + } = useShinkaiNodeRemoveStorageMutation(); + const { setShinkaiNodeOptions } = useShinkaiNodeManager(); + const { encryptionKeys } = useGetEncryptionKeys(); + const setAuth = useAuth((state) => state.setAuth); + const navigate = useNavigate(); + + const isResetLoading = + isShinkaiNodeKillPending || + isShinkaiNodeRemoveStoragePending || + isShinkaiNodeSpawnPending; + + const { mutateAsync: submitRegistrationNoCode } = useSubmitRegistrationNoCode( + { + onSuccess: (response, setupPayload) => { + if (response.status !== 'success') { + shinkaiNodeKill(); + } + if (response.status === 'success' && encryptionKeys) { + const updatedSetupData = { + ...encryptionKeys, + ...setupPayload, + permission_type: '', + shinkai_identity: response.data?.node_name ?? '', + node_signature_pk: response.data?.identity_public_key ?? '', + node_encryption_pk: response.data?.encryption_public_key ?? '', + api_v2_key: response.data?.api_v2_key ?? '', + }; + setAuth(updatedSetupData); + navigate('/ai-model-installation'); + onOpenChange(false); + } else { + submitRegistrationNoCodeError(); + } + }, + }, + ); + + const handleReset = async () => { + await shinkaiNodeKill(); + useAuth.getState().setLogout(); // clean up local storage + await shinkaiNodeRemoveStorage({ preserveKeys: true }); + setShinkaiNodeOptions(null); + await shinkaiNodeSpawn(); + }; + + return ( + + + {allowClose && ( + + + + )} + + App Reset Required + +
+
+ We're currently in beta and we made some significant + updates to improve your experience. To apply these updates, we + need to reset your data. +

+ If you need assistance, please contact our support team. +
+
+
+
+ + + +
+
+ ); +}; diff --git a/apps/shinkai-desktop/src/components/tools/components/tool-card.tsx b/apps/shinkai-desktop/src/components/tools/components/tool-card.tsx index 5f40981b4..e3aada827 100644 --- a/apps/shinkai-desktop/src/components/tools/components/tool-card.tsx +++ b/apps/shinkai-desktop/src/components/tools/components/tool-card.tsx @@ -35,14 +35,7 @@ import { save } from '@tauri-apps/plugin-dialog'; import * as fs from '@tauri-apps/plugin-fs'; import { BaseDirectory } from '@tauri-apps/plugin-fs'; import { open } from '@tauri-apps/plugin-shell'; -import { - CheckCircle, - DownloadIcon, - MoreVertical, - PlayCircle, - Rocket, -} from 'lucide-react'; -import { InfoCircleIcon } from 'primereact/icons/infocircle'; +import { DownloadIcon, MoreVertical, PlayCircle, Rocket } from 'lucide-react'; import { useEffect, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { toast } from 'sonner'; diff --git a/apps/shinkai-desktop/src/hooks/use-current-inbox.ts b/apps/shinkai-desktop/src/hooks/use-current-inbox.ts index f6ebaea39..4cbb0bc6b 100644 --- a/apps/shinkai-desktop/src/hooks/use-current-inbox.ts +++ b/apps/shinkai-desktop/src/hooks/use-current-inbox.ts @@ -1,4 +1,4 @@ -import { useGetInboxes } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxes'; +import { useGetInboxesWithPagination } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxesWithPagination'; import { useMemo } from 'react'; import { useLocation } from 'react-router-dom'; @@ -8,7 +8,7 @@ export const useGetCurrentInbox = (inboxId?: string) => { const auth = useAuth((state) => state.auth); const location = useLocation(); - const { inboxes } = useGetInboxes({ + const { data: inboxesPagination } = useGetInboxesWithPagination({ nodeAddress: auth?.node_address ?? '', token: auth?.api_v2_key ?? '', }); @@ -16,8 +16,10 @@ export const useGetCurrentInbox = (inboxId?: string) => { const currentInboxId = inboxId ?? location.pathname.split('/')?.[2]; const decodedInboxId = decodeURIComponent(currentInboxId); - return inboxes.find((inbox) => decodedInboxId === inbox.inbox_id); - }, [inboxId, inboxes, location.pathname]); + return inboxesPagination?.pages + .flatMap((page) => page.inboxes) + .find((inbox) => decodedInboxId === inbox.inbox_id); + }, [inboxId, inboxesPagination, location.pathname]); return currentInbox; }; diff --git a/apps/shinkai-desktop/src/lib/shinkai-node-overlay.tsx b/apps/shinkai-desktop/src/lib/shinkai-node-overlay.tsx index 408aa15a8..8275ccdca 100644 --- a/apps/shinkai-desktop/src/lib/shinkai-node-overlay.tsx +++ b/apps/shinkai-desktop/src/lib/shinkai-node-overlay.tsx @@ -8,7 +8,7 @@ import { DownloadIcon, Loader2 } from 'lucide-react'; import React, { useState } from 'react'; import { toast } from 'sonner'; -import { ResetConnectionDialog } from '../pages/layout/main-layout'; +import { ResetConnectionDialog } from '../components/reset-connection-dialog'; import { useAuth } from '../store/auth'; import { useShinkaiNodeManager } from '../store/shinkai-node-manager'; import { useRetrieveLogsQuery } from './shinkai-logs/logs-client'; diff --git a/apps/shinkai-desktop/src/pages/chat/layout.tsx b/apps/shinkai-desktop/src/pages/chat/layout.tsx index b9b516dde..73e4896c2 100644 --- a/apps/shinkai-desktop/src/pages/chat/layout.tsx +++ b/apps/shinkai-desktop/src/pages/chat/layout.tsx @@ -9,9 +9,9 @@ import { UpdateInboxNameFormSchema, updateInboxNameFormSchema, } from '@shinkai_network/shinkai-node-state/forms/chat/inbox'; -import { useUpdateInboxName } from '@shinkai_network/shinkai-node-state/lib/mutations/updateInboxName/useUpdateInboxName'; import { useRemoveJob } from '@shinkai_network/shinkai-node-state/v2/mutations/removeJob/useRemoveJob'; -import { useGetInboxes } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxes'; +import { useUpdateInboxName } from '@shinkai_network/shinkai-node-state/v2/mutations/updateInboxName/useUpdateInboxName'; +import { useGetInboxesWithPagination } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxesWithPagination'; import { Button, Dialog, @@ -45,6 +45,7 @@ import { AnimatePresence, motion } from 'framer-motion'; import { Edit3, Trash2Icon } from 'lucide-react'; import { memo, useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; +import { useInView } from 'react-intersection-observer'; import { Link, Outlet, useMatch, useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; @@ -85,14 +86,7 @@ const InboxNameInput = ({ await updateInboxName({ nodeAddress: auth.node_address, - sender: auth.shinkai_identity, - senderSubidentity: auth.profile, - receiver: `${auth.shinkai_identity}`, - my_device_encryption_sk: auth.my_device_encryption_sk, - my_device_identity_sk: auth.my_device_identity_sk, - node_encryption_pk: auth.node_encryption_pk, - profile_encryption_sk: auth.profile_encryption_sk, - profile_identity_sk: auth.profile_identity_sk, + token: auth.api_v2_key, inboxId, inboxName: data.name, }); @@ -434,32 +428,52 @@ const ChatSidebar = () => { (state) => state.setPromptSelected, ); - const { inboxes, isPending, isSuccess } = useGetInboxes( + const { ref, inView } = useInView(); + + const { + data: inboxesPagination, + isPending, + isSuccess, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + } = useGetInboxesWithPagination( { nodeAddress: auth?.node_address ?? '', token: auth?.api_v2_key ?? '' }, - { - refetchIntervalInBackground: true, - refetchInterval: (query) => { - const queryState = query?.state?.data?.filter( - (item) => !!item.last_message, - ); - if (!queryState || !auth) return 0; - - const allInboxesAreCompleted = queryState.every((inbox) => { - return ( - inbox.last_message && - inbox.last_message.sender && - !( - inbox.last_message.sender === auth?.shinkai_identity && - inbox.last_message.sender_subidentity === auth?.profile - ) - ); - }); - - return allInboxesAreCompleted ? 0 : 5000; - }, - }, + // { + // refetchIntervalInBackground: true, + // refetchInterval: (query) => { + // const queryState = query?.state?.data?.filter( + // (item) => !!item.last_message, + // ); + // if (!queryState || !auth) return 0; + + // const allInboxesAreCompleted = queryState.every((inbox) => { + // return ( + // inbox.last_message && + // inbox.last_message.sender && + // !( + // inbox.last_message.sender === auth?.shinkai_identity && + // inbox.last_message.sender_subidentity === auth?.profile + // ) + // ); + // }); + + // return allInboxesAreCompleted ? 0 : 5000; + // }, + // }, ); + console.log(inboxesPagination, 'inboxes'); + + useEffect(() => { + if (inView && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, [inView, fetchNextPage, hasNextPage, isFetchingNextPage]); + + const hasInboxes = + (inboxesPagination?.pages?.at(-1)?.inboxes ?? []).length > 0; + return (
@@ -515,35 +529,46 @@ const ChatSidebar = () => { ))} {isSuccess && - inboxes?.length > 0 && - inboxes.map((inbox) => ( - - ))} - {isSuccess && inboxes?.length === 0 && ( + inboxesPagination?.pages.map((page) => + page.inboxes.map((inbox) => ( + + )), + )} + {isSuccess && !hasInboxes && (

{t('chat.actives.notFound')}{' '}

)} + {hasNextPage && ( + + )}
diff --git a/apps/shinkai-desktop/src/pages/galxe-subscriptions.tsx b/apps/shinkai-desktop/src/pages/galxe-subscriptions.tsx index ee3a5cf72..dfee7c2fb 100644 --- a/apps/shinkai-desktop/src/pages/galxe-subscriptions.tsx +++ b/apps/shinkai-desktop/src/pages/galxe-subscriptions.tsx @@ -3,7 +3,7 @@ import { CheckCircledIcon, CircleIcon } from '@radix-ui/react-icons'; import { useTranslation } from '@shinkai_network/shinkai-i18n'; import { Inbox } from '@shinkai_network/shinkai-message-ts/api/jobs/types'; import { useGetListDirectoryContents } from '@shinkai_network/shinkai-node-state/v2/queries/getDirectoryContents/useGetListDirectoryContents'; -import { useGetInboxes } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxes'; +import { useGetInboxesWithPagination } from '@shinkai_network/shinkai-node-state/v2/queries/getInboxes/useGetInboxesWithPagination'; import { useGetMySubscriptions } from '@shinkai_network/shinkai-node-state/v2/queries/getMySubscriptions/useGetMySubscriptions'; import { Button, @@ -45,7 +45,7 @@ export const GalxeSusbcriptions = () => { const evmAddress = useSettings((store) => store.evmAddress); const setEvmAddress = useSettings((store) => store.setEvmAddress); - const { inboxes } = useGetInboxes({ + const { data: inboxesWithPagination } = useGetInboxesWithPagination({ nodeAddress: auth?.node_address ?? '', token: auth?.api_v2_key ?? '', }); @@ -80,7 +80,9 @@ export const GalxeSusbcriptions = () => { const isUserSubscribedToKnowledge = (subscriptionFolder ?? [])?.length > 0; - const inboxesWithSubscriptions: Inbox[] = inboxes.filter( + const inboxesWithSubscriptions: Inbox[] = ( + inboxesWithPagination?.pages.flatMap((page) => page.inboxes) ?? [] + ).filter( (inbox) => (inbox?.job_scope?.vector_fs_folders ?? []).some((folder) => folder?.includes(SUBSCRIPTION_PATH), diff --git a/apps/shinkai-desktop/src/pages/layout/main-layout.tsx b/apps/shinkai-desktop/src/pages/layout/main-layout.tsx index b29910ac7..871d324e1 100644 --- a/apps/shinkai-desktop/src/pages/layout/main-layout.tsx +++ b/apps/shinkai-desktop/src/pages/layout/main-layout.tsx @@ -1,8 +1,6 @@ import { ExitIcon, GearIcon } from '@radix-ui/react-icons'; import { useTranslation } from '@shinkai_network/shinkai-i18n'; import { useImportTool } from '@shinkai_network/shinkai-node-state/v2/mutations/importTool/useImportTool'; -import { useSubmitRegistrationNoCode } from '@shinkai_network/shinkai-node-state/v2/mutations/submitRegistation/useSubmitRegistrationNoCode'; -import { useGetEncryptionKeys } from '@shinkai_network/shinkai-node-state/v2/queries/getEncryptionKeys/useGetEncryptionKeys'; import { useGetHealth } from '@shinkai_network/shinkai-node-state/v2/queries/getHealth/useGetHealth'; import { AlertDialog, @@ -35,17 +33,11 @@ import { ShinkaiCombinationMarkIcon, ToolsIcon, } from '@shinkai_network/shinkai-ui/assets'; -import { submitRegistrationNoCodeError } from '@shinkai_network/shinkai-ui/helpers'; import { cn } from '@shinkai_network/shinkai-ui/utils'; import { listen } from '@tauri-apps/api/event'; import { getCurrentWindow } from '@tauri-apps/api/window'; -import { AnimatePresence, motion, TargetAndTransition } from 'framer-motion'; -import { - ArrowLeftToLine, - ArrowRightToLine, - BotIcon, - XIcon, -} from 'lucide-react'; +import { AnimatePresence, motion } from 'framer-motion'; +import { ArrowLeftToLine, ArrowRightToLine, BotIcon } from 'lucide-react'; import React, { Fragment, useEffect, useState } from 'react'; import { Link, @@ -59,15 +51,10 @@ import { toast } from 'sonner'; import { ResourcesBanner } from '../../components/hardware-capabilities/resources-banner'; import { UpdateBanner } from '../../components/hardware-capabilities/update-banner'; import OnboardingStepper from '../../components/onboarding-checklist/onboarding'; +import { ResetConnectionDialog } from '../../components/reset-connection-dialog'; import config from '../../config'; -import { - useShinkaiNodeKillMutation, - useShinkaiNodeRemoveStorageMutation, - useShinkaiNodeSpawnMutation, -} from '../../lib/shinkai-node-manager/shinkai-node-manager-client'; import { useAuth } from '../../store/auth'; import { useSettings } from '../../store/settings'; -import { useShinkaiNodeManager } from '../../store/shinkai-node-manager'; type NavigationLink = { title: string; @@ -78,12 +65,6 @@ type NavigationLink = { disabled?: boolean; }; -export const sidebarTransition: TargetAndTransition['transition'] = { - type: 'spring', - stiffness: 260, - damping: 24, -}; - export const showAnimation = { hidden: { width: 0, @@ -100,6 +81,7 @@ export const showAnimation = { }, }, }; + const NavLink = ({ href, external, @@ -185,121 +167,6 @@ const NavLink = ({ ); }; -export const ResetConnectionDialog = ({ - isOpen, - onOpenChange, - allowClose = false, -}: { - isOpen: boolean; - onOpenChange: (open: boolean) => void; - allowClose?: boolean; -}) => { - const { mutateAsync: shinkaiNodeKill, isPending: isShinkaiNodeKillPending } = - useShinkaiNodeKillMutation(); - const { - mutateAsync: shinkaiNodeSpawn, - isPending: isShinkaiNodeSpawnPending, - } = useShinkaiNodeSpawnMutation({ - onSuccess: async () => { - if (!encryptionKeys) return; - await submitRegistrationNoCode({ - profile: 'main', - registration_name: 'main_device', - node_address: 'http://127.0.0.1:9550', - ...encryptionKeys, - }); - }, - }); - const { - mutateAsync: shinkaiNodeRemoveStorage, - isPending: isShinkaiNodeRemoveStoragePending, - } = useShinkaiNodeRemoveStorageMutation(); - const { setShinkaiNodeOptions } = useShinkaiNodeManager(); - const { encryptionKeys } = useGetEncryptionKeys(); - const setAuth = useAuth((state) => state.setAuth); - const navigate = useNavigate(); - - const isResetLoading = - isShinkaiNodeKillPending || - isShinkaiNodeRemoveStoragePending || - isShinkaiNodeSpawnPending; - - const { mutateAsync: submitRegistrationNoCode } = useSubmitRegistrationNoCode( - { - onSuccess: (response, setupPayload) => { - if (response.status !== 'success') { - shinkaiNodeKill(); - } - if (response.status === 'success' && encryptionKeys) { - const updatedSetupData = { - ...encryptionKeys, - ...setupPayload, - permission_type: '', - shinkai_identity: response.data?.node_name ?? '', - node_signature_pk: response.data?.identity_public_key ?? '', - node_encryption_pk: response.data?.encryption_public_key ?? '', - api_v2_key: response.data?.api_v2_key ?? '', - }; - setAuth(updatedSetupData); - navigate('/ai-model-installation'); - onOpenChange(false); - } else { - submitRegistrationNoCodeError(); - } - }, - }, - ); - - const handleReset = async () => { - await shinkaiNodeKill(); - useAuth.getState().setLogout(); // clean up local storage - await shinkaiNodeRemoveStorage({ preserveKeys: true }); - setShinkaiNodeOptions(null); - await shinkaiNodeSpawn(); - }; - - return ( - - - {allowClose && ( - - - - )} - - App Reset Required - -
-
- We're currently in beta and we made some significant - updates to improve your experience. To apply these updates, we - need to reset your data. -

- If you need assistance, please contact our support team. -
-
-
-
- - - -
-
- ); -}; - export function MainNav() { const { t, Trans } = useTranslation(); const optInExperimental = useSettings((state) => state.optInExperimental); diff --git a/apps/shinkai-visor/src/components/edit-inbox-name-dialog/edit-inbox-name-dialog.tsx b/apps/shinkai-visor/src/components/edit-inbox-name-dialog/edit-inbox-name-dialog.tsx index b68c1a0a8..62045905f 100644 --- a/apps/shinkai-visor/src/components/edit-inbox-name-dialog/edit-inbox-name-dialog.tsx +++ b/apps/shinkai-visor/src/components/edit-inbox-name-dialog/edit-inbox-name-dialog.tsx @@ -4,7 +4,7 @@ import { UpdateInboxNameFormSchema, updateInboxNameFormSchema, } from '@shinkai_network/shinkai-node-state/forms/chat/inbox'; -import { useUpdateInboxName } from '@shinkai_network/shinkai-node-state/lib/mutations/updateInboxName/useUpdateInboxName'; +import { useUpdateInboxName } from '@shinkai_network/shinkai-node-state/v2/mutations/updateInboxName/useUpdateInboxName'; import { Button, DialogFooter, @@ -57,14 +57,7 @@ export const EditInboxNameDialog = ({ const submit = (values: UpdateInboxNameFormSchema) => { updateInboxName({ nodeAddress: auth?.node_address ?? '', - sender: auth?.shinkai_identity ?? '', - senderSubidentity: auth?.profile ?? '', - receiver: auth?.shinkai_identity ?? '', - my_device_encryption_sk: auth?.my_device_encryption_sk ?? '', - my_device_identity_sk: auth?.my_device_identity_sk ?? '', - node_encryption_pk: auth?.node_encryption_pk ?? '', - profile_encryption_sk: auth?.profile_encryption_sk ?? '', - profile_identity_sk: auth?.profile_identity_sk ?? '', + token: auth?.api_v2_key ?? '', inboxId: inboxId, inboxName: values.name, }); diff --git a/libs/shinkai-message-ts/src/api/jobs/index.ts b/libs/shinkai-message-ts/src/api/jobs/index.ts index b2f5833c6..9c5646e8f 100644 --- a/libs/shinkai-message-ts/src/api/jobs/index.ts +++ b/libs/shinkai-message-ts/src/api/jobs/index.ts @@ -10,6 +10,8 @@ import { CreateJobRequest, CreateJobResponse, GetAllInboxesResponse, + GetAllInboxesWithPaginationRequest, + GetAllInboxesWithPaginationResponse, GetChatConfigRequest, GetChatConfigResponse, GetDownloadFileRequest, @@ -34,6 +36,8 @@ import { StopGeneratingLLMRequest, UpdateChatConfigRequest, UpdateChatConfigResponse, + UpdateInboxNameRequest, + UpdateInboxNameResponse, UpdateJobScopeRequest, UpdateLLMProviderRequest, UpdateLLMProviderResponse, @@ -347,6 +351,38 @@ export const getAllInboxes = async ( return response.data as GetAllInboxesResponse; }; +export const getAllInboxesWithPagination = async ( + nodeAddress: string, + bearerToken: string, + payload: GetAllInboxesWithPaginationRequest, +) => { + const response = await httpClient.get( + urlJoin(nodeAddress, '/v2/all_inboxes_paginated'), + { + headers: { Authorization: `Bearer ${bearerToken}` }, + responseType: 'json', + params: payload, + }, + ); + return response.data as GetAllInboxesWithPaginationResponse; +}; + +export const updateInboxName = async ( + nodeAddress: string, + bearerToken: string, + payload: UpdateInboxNameRequest, +) => { + const response = await httpClient.post( + urlJoin(nodeAddress, '/v2/update_smart_inbox_name'), + payload, + { + headers: { Authorization: `Bearer ${bearerToken}` }, + responseType: 'json', + }, + ); + return response.data as UpdateInboxNameResponse; +}; + export const getJobConfig = async ( nodeAddress: string, bearerToken: string, diff --git a/libs/shinkai-message-ts/src/api/jobs/types.ts b/libs/shinkai-message-ts/src/api/jobs/types.ts index fa0ef685c..14bfecdda 100644 --- a/libs/shinkai-message-ts/src/api/jobs/types.ts +++ b/libs/shinkai-message-ts/src/api/jobs/types.ts @@ -200,6 +200,25 @@ export type Inbox = { }; export type GetAllInboxesResponse = Inbox[]; +export type GetAllInboxesWithPaginationRequest = { + limit?: number; + offset?: string; + show_hidden?: boolean; +}; +export type GetAllInboxesWithPaginationResponse = { + inboxes: Inbox[]; + hasNextPage: boolean; +}; + +export type UpdateInboxNameRequest = { + inbox_name: string; + custom_name: string; +}; +export type UpdateInboxNameResponse = { + data: null; + status: string; +}; + export type GetFileNamesRequest = { inboxName: string; }; diff --git a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts deleted file mode 100644 index 83b76ea85..000000000 --- a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { updateInboxName as updateInboxNameApi } from '@shinkai_network/shinkai-message-ts/api'; - -import { UpdateInboxNameInput } from './types'; -export const updateInboxName = async ({ - nodeAddress, - senderSubidentity, - sender, - receiver, - my_device_encryption_sk, - my_device_identity_sk, - node_encryption_pk, - profile_encryption_sk, - profile_identity_sk, - inboxName, - inboxId, -}: UpdateInboxNameInput) => { - const response = await updateInboxNameApi( - nodeAddress, - sender, - senderSubidentity, - receiver, - { - my_device_encryption_sk, - my_device_identity_sk, - node_encryption_pk, - profile_encryption_sk, - profile_identity_sk, - }, - inboxName, - inboxId, - ); - - return response; -}; diff --git a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts deleted file mode 100644 index bcf965732..000000000 --- a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { CredentialsPayload } from '@shinkai_network/shinkai-message-ts/models'; - -export type UpdateInboxNameInput = CredentialsPayload & { - nodeAddress: string; - senderSubidentity: string; - sender: string; - receiver: string; - inboxName: string; - inboxId: string; -}; - -export type UpdateInboxNameOutput = { success: string; data: null }; diff --git a/libs/shinkai-node-state/src/v2/constants.ts b/libs/shinkai-node-state/src/v2/constants.ts index 59df7e01f..f4cc9caef 100644 --- a/libs/shinkai-node-state/src/v2/constants.ts +++ b/libs/shinkai-node-state/src/v2/constants.ts @@ -11,6 +11,7 @@ export enum FunctionKeyV2 { GET_AGENTS = 'GET_AGENTS', GET_LLM_PROVIDERS = 'GET_LLM_PROVIDERS', GET_INBOXES = 'GET_INBOXES', + GET_INBOXES_WITH_PAGINATION = 'GET_INBOXES_WITH_PAGINATION', GET_CHAT_CONVERSATION = 'GET_CHAT_CONVERSATION', GET_CHAT_CONVERSATION_PAGINATION = 'GET_CHAT_CONVERSATION_PAGINATION', GET_CHAT_CONVERSATION_BRANCHES = 'GET_CHAT_CONVERSATION_BRANCHES', diff --git a/libs/shinkai-node-state/src/v2/mutations/updateInboxName/index.ts b/libs/shinkai-node-state/src/v2/mutations/updateInboxName/index.ts new file mode 100644 index 000000000..414a8677e --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/updateInboxName/index.ts @@ -0,0 +1,17 @@ +import { updateInboxName as updateInboxNameApi } from '@shinkai_network/shinkai-message-ts/api/jobs/index'; + +import { UpdateInboxNameInput } from './types'; + +export const updateInboxName = async ({ + nodeAddress, + token, + inboxName, + inboxId, +}: UpdateInboxNameInput) => { + const response = await updateInboxNameApi(nodeAddress, token, { + custom_name: inboxName, + inbox_name: inboxId, + }); + + return response; +}; diff --git a/libs/shinkai-node-state/src/v2/mutations/updateInboxName/types.ts b/libs/shinkai-node-state/src/v2/mutations/updateInboxName/types.ts new file mode 100644 index 000000000..c5b44380e --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/updateInboxName/types.ts @@ -0,0 +1,10 @@ +import { Token } from '@shinkai_network/shinkai-message-ts/api/general/types'; +import { UpdateInboxNameResponse } from '@shinkai_network/shinkai-message-ts/api/jobs/types'; + +export type UpdateInboxNameInput = Token & { + nodeAddress: string; + inboxName: string; + inboxId: string; +}; + +export type UpdateInboxNameOutput = UpdateInboxNameResponse; diff --git a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/useUpdateInboxName.ts b/libs/shinkai-node-state/src/v2/mutations/updateInboxName/useUpdateInboxName.ts similarity index 80% rename from libs/shinkai-node-state/src/lib/mutations/updateInboxName/useUpdateInboxName.ts rename to libs/shinkai-node-state/src/v2/mutations/updateInboxName/useUpdateInboxName.ts index d3caa01ae..de5e15d68 100644 --- a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/useUpdateInboxName.ts +++ b/libs/shinkai-node-state/src/v2/mutations/updateInboxName/useUpdateInboxName.ts @@ -1,7 +1,7 @@ import { UseMutationOptions, useQueryClient } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; -import { FunctionKey } from '../../constants'; +import { FunctionKeyV2 } from '../../constants'; import { updateInboxName } from '.'; import type { UpdateInboxNameInput, UpdateInboxNameOutput } from './types'; @@ -16,7 +16,9 @@ export const useUpdateInboxName = (options?: Options) => { return useMutation({ mutationFn: updateInboxName, onSuccess: (...onSuccessParameters) => { - queryClient.invalidateQueries({ queryKey: [FunctionKey.GET_INBOXES] }); + queryClient.invalidateQueries({ + queryKey: [FunctionKeyV2.GET_INBOXES_WITH_PAGINATION], + }); if (options?.onSuccess) { options.onSuccess(...onSuccessParameters); } diff --git a/libs/shinkai-node-state/src/v2/queries/getInboxes/index.ts b/libs/shinkai-node-state/src/v2/queries/getInboxes/index.ts index 9f4b6389a..4cf226b84 100644 --- a/libs/shinkai-node-state/src/v2/queries/getInboxes/index.ts +++ b/libs/shinkai-node-state/src/v2/queries/getInboxes/index.ts @@ -1,4 +1,7 @@ -import { getAllInboxes as getAllInboxesApi } from '@shinkai_network/shinkai-message-ts/api/jobs/index'; +import { + getAllInboxes as getAllInboxesApi, + getAllInboxesWithPagination as getAllInboxesWithPaginationApi, +} from '@shinkai_network/shinkai-message-ts/api/jobs/index'; import type { GetInboxesInput } from './types'; @@ -6,3 +9,18 @@ export const getInboxes = async ({ nodeAddress, token }: GetInboxesInput) => { const inboxes = await getAllInboxesApi(nodeAddress, token); return inboxes; }; + +export const getInboxesWithPagination = async ({ + nodeAddress, + token, + limit, + offset, + show_hidden, +}: GetInboxesInput) => { + const inboxes = await getAllInboxesWithPaginationApi(nodeAddress, token, { + limit, + offset, + show_hidden, + }); + return inboxes; +}; diff --git a/libs/shinkai-node-state/src/v2/queries/getInboxes/types.ts b/libs/shinkai-node-state/src/v2/queries/getInboxes/types.ts index 85ea6f4cf..9111375ae 100644 --- a/libs/shinkai-node-state/src/v2/queries/getInboxes/types.ts +++ b/libs/shinkai-node-state/src/v2/queries/getInboxes/types.ts @@ -1,14 +1,21 @@ import { Token } from '@shinkai_network/shinkai-message-ts/api/general/types'; -import { GetAllInboxesResponse } from '@shinkai_network/shinkai-message-ts/api/jobs/types'; +import { + GetAllInboxesWithPaginationRequest, + GetAllInboxesWithPaginationResponse, +} from '@shinkai_network/shinkai-message-ts/api/jobs/types'; import { QueryObserverOptions } from '@tanstack/react-query'; import { FunctionKeyV2 } from '../../constants'; export type GetInboxesInput = Token & { nodeAddress: string; -}; -export type GetInboxesOutput = GetAllInboxesResponse; -export type UseGetInboxes = [FunctionKeyV2.GET_INBOXES, GetInboxesInput]; +} & GetAllInboxesWithPaginationRequest; +export type GetInboxesOutput = GetAllInboxesWithPaginationResponse; + +export type UseGetInboxes = [ + FunctionKeyV2.GET_INBOXES_WITH_PAGINATION, + GetInboxesInput, +]; export type Options = QueryObserverOptions< GetInboxesOutput, diff --git a/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxes.ts b/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxes.ts index 19df253b9..2b5964b7a 100644 --- a/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxes.ts +++ b/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxes.ts @@ -2,14 +2,11 @@ import { useQuery } from '@tanstack/react-query'; import { FunctionKeyV2 } from '../../constants'; import { getInboxes } from '.'; -import { GetInboxesInput, Options } from './types'; +import { GetInboxesInput } from './types'; -export const useGetInboxes = ( - input: GetInboxesInput, - options?: Omit, -) => { +export const useGetInboxes = (input: GetInboxesInput) => { const response = useQuery({ - queryKey: [FunctionKeyV2.GET_INBOXES, input], + queryKey: [FunctionKeyV2.GET_INBOXES_WITH_PAGINATION, input], queryFn: async () => getInboxes(input), select: (data) => // display only job inboxes @@ -18,7 +15,6 @@ export const useGetInboxes = ( inbox?.inbox_id?.split('::')?.[0] === 'job_inbox' && inbox.is_finished === false, ), - ...options, }); return { ...response, diff --git a/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxesWithPagination.ts b/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxesWithPagination.ts new file mode 100644 index 000000000..2cd5104dd --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getInboxes/useGetInboxesWithPagination.ts @@ -0,0 +1,44 @@ +import { GetAllInboxesWithPaginationRequest } from '@shinkai_network/shinkai-message-ts/api/jobs/types'; +import { InfiniteData, useInfiniteQuery } from '@tanstack/react-query'; + +import { FunctionKeyV2 } from '../../constants'; +import { APIError } from '../../types'; +import { getInboxesWithPagination } from '.'; +import { + GetInboxesInput, + GetInboxesOutput, + Options, + UseGetInboxes, +} from './types'; + +const DEFAULT_LIMIT = 20; + +export const useGetInboxesWithPagination = ( + input: GetInboxesInput, + options?: Omit, +) => { + const response = useInfiniteQuery< + GetInboxesOutput, + APIError, + InfiniteData, + UseGetInboxes, + GetAllInboxesWithPaginationRequest + >({ + queryKey: [FunctionKeyV2.GET_INBOXES_WITH_PAGINATION, input], + queryFn: ({ pageParam }) => + getInboxesWithPagination({ + ...input, + offset: pageParam?.offset ?? undefined, // offset is last inbox id + limit: pageParam?.limit ?? DEFAULT_LIMIT, + show_hidden: false, + }), + getNextPageParam: (lastPage, pages) => { + if (lastPage?.inboxes?.length < DEFAULT_LIMIT) return; + const lastInbox = pages.at(-1)?.inboxes?.at(-1); + if (!lastInbox) return null; + return { offset: lastInbox.inbox_id }; + }, + initialPageParam: { offset: undefined }, + }); + return response; +};