aboutsummaryrefslogtreecommitdiff
path: root/apps/web/hooks
diff options
context:
space:
mode:
authorMahesh Sanikommu <[email protected]>2026-01-13 17:53:28 -0800
committerGitHub <[email protected]>2026-01-13 17:53:28 -0800
commit641db19e35009e22b101f9e673a3af4528de2a30 (patch)
tree698f9c3c22efed5f4061ef7e89b4963da150440a /apps/web/hooks
parentreproduce (diff)
downloadsupermemory-641db19e35009e22b101f9e673a3af4528de2a30.tar.xz
supermemory-641db19e35009e22b101f9e673a3af4528de2a30.zip
chore: quick bugs squash across the elements and added few more changes (#671)
Diffstat (limited to 'apps/web/hooks')
-rw-r--r--apps/web/hooks/use-document-mutations.ts313
-rw-r--r--apps/web/hooks/use-memories-usage.ts35
2 files changed, 348 insertions, 0 deletions
diff --git a/apps/web/hooks/use-document-mutations.ts b/apps/web/hooks/use-document-mutations.ts
new file mode 100644
index 00000000..5abd7b56
--- /dev/null
+++ b/apps/web/hooks/use-document-mutations.ts
@@ -0,0 +1,313 @@
+"use client"
+
+import { useMutation, useQueryClient } from "@tanstack/react-query"
+import { toast } from "sonner"
+import { $fetch } from "@lib/api"
+
+interface DocumentsQueryData {
+ documents: unknown[]
+ totalCount: number
+}
+
+interface UseDocumentMutationsOptions {
+ onClose: () => void
+}
+
+export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions) {
+ const queryClient = useQueryClient()
+
+ const noteMutation = useMutation({
+ mutationFn: async ({
+ content,
+ project,
+ }: {
+ content: string
+ project: string
+ }) => {
+ const response = await $fetch("@post/documents", {
+ body: {
+ content: content,
+ containerTags: [project],
+ metadata: {
+ sm_source: "consumer",
+ },
+ },
+ })
+
+ if (response.error) {
+ throw new Error(response.error?.message || "Failed to add note")
+ }
+
+ return response.data
+ },
+ onMutate: async ({ content, project }) => {
+ await queryClient.cancelQueries({
+ queryKey: ["documents-with-memories", project],
+ })
+
+ const previousMemories = queryClient.getQueryData([
+ "documents-with-memories",
+ project,
+ ])
+
+ const optimisticMemory = {
+ id: `temp-${Date.now()}`,
+ content: content,
+ url: null,
+ title: content.substring(0, 100),
+ description: "Processing content...",
+ containerTags: [project],
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ status: "queued",
+ type: "note",
+ metadata: {
+ processingStage: "queued",
+ processingMessage: "Added to processing queue",
+ },
+ memoryEntries: [],
+ isOptimistic: true,
+ }
+
+ queryClient.setQueryData(
+ ["documents-with-memories", project],
+ (old: DocumentsQueryData | undefined) => {
+ const existingDocs = old?.documents ?? []
+ return {
+ ...old,
+ documents: [optimisticMemory, ...existingDocs],
+ totalCount: (old?.totalCount ?? 0) + 1,
+ }
+ },
+ )
+
+ return { previousMemories }
+ },
+ onError: (_error, variables, context) => {
+ if (context?.previousMemories) {
+ queryClient.setQueryData(
+ ["documents-with-memories", variables.project],
+ context.previousMemories,
+ )
+ }
+ toast.error("Failed to add note", {
+ description: _error instanceof Error ? _error.message : "Unknown error",
+ })
+ },
+ onSuccess: (_data, variables) => {
+ toast.success("Note added successfully!", {
+ description: "Your note is being processed",
+ })
+ queryClient.invalidateQueries({
+ queryKey: ["documents-with-memories", variables.project],
+ })
+ onClose()
+ },
+ })
+
+ const linkMutation = useMutation({
+ mutationFn: async ({ url, project }: { url: string; project: string }) => {
+ const response = await $fetch("@post/documents", {
+ body: {
+ content: url,
+ containerTags: [project],
+ metadata: {
+ sm_source: "consumer",
+ },
+ },
+ })
+
+ if (response.error) {
+ throw new Error(response.error?.message || "Failed to add link")
+ }
+
+ return response.data
+ },
+ onMutate: async ({ url, project }) => {
+ await queryClient.cancelQueries({
+ queryKey: ["documents-with-memories", project],
+ })
+
+ const previousMemories = queryClient.getQueryData([
+ "documents-with-memories",
+ project,
+ ])
+
+ const optimisticMemory = {
+ id: `temp-${Date.now()}`,
+ content: "",
+ url: url,
+ title: "Processing...",
+ description: "Extracting content...",
+ containerTags: [project],
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ status: "queued",
+ type: "link",
+ metadata: {
+ processingStage: "queued",
+ processingMessage: "Added to processing queue",
+ },
+ memoryEntries: [],
+ isOptimistic: true,
+ }
+
+ queryClient.setQueryData(
+ ["documents-with-memories", project],
+ (old: DocumentsQueryData | undefined) => {
+ const existingDocs = old?.documents ?? []
+ return {
+ ...old,
+ documents: [optimisticMemory, ...existingDocs],
+ totalCount: (old?.totalCount ?? 0) + 1,
+ }
+ },
+ )
+
+ return { previousMemories }
+ },
+ onError: (_error, variables, context) => {
+ if (context?.previousMemories) {
+ queryClient.setQueryData(
+ ["documents-with-memories", variables.project],
+ context.previousMemories,
+ )
+ }
+ toast.error("Failed to add link", {
+ description: _error instanceof Error ? _error.message : "Unknown error",
+ })
+ },
+ onSuccess: (_data, variables) => {
+ toast.success("Link added successfully!", {
+ description: "Your link is being processed",
+ })
+ queryClient.invalidateQueries({
+ queryKey: ["documents-with-memories", variables.project],
+ })
+ onClose()
+ },
+ })
+
+ const fileMutation = useMutation({
+ mutationFn: async ({
+ file,
+ title,
+ description,
+ project,
+ }: {
+ file: File
+ title?: string
+ description?: string
+ project: string
+ }) => {
+ const formData = new FormData()
+ formData.append("file", file)
+ formData.append("containerTags", JSON.stringify([project]))
+ formData.append(
+ "metadata",
+ JSON.stringify({
+ sm_source: "consumer",
+ }),
+ )
+
+ const response = await fetch(
+ `${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/documents/file`,
+ {
+ method: "POST",
+ body: formData,
+ credentials: "include",
+ },
+ )
+
+ if (!response.ok) {
+ const error = await response.json()
+ throw new Error(error.error || "Failed to upload file")
+ }
+
+ const data = await response.json()
+
+ if (title || description) {
+ await $fetch(`@patch/documents/${data.id}`, {
+ body: {
+ metadata: {
+ ...(title && { title }),
+ ...(description && { description }),
+ sm_source: "consumer",
+ },
+ },
+ })
+ }
+
+ return data
+ },
+ onMutate: async ({ file, title, description, project }) => {
+ await queryClient.cancelQueries({
+ queryKey: ["documents-with-memories", project],
+ })
+
+ const previousMemories = queryClient.getQueryData([
+ "documents-with-memories",
+ project,
+ ])
+
+ const optimisticMemory = {
+ id: `temp-file-${Date.now()}`,
+ content: "",
+ url: null,
+ title: title || file.name,
+ description: description || `Uploading ${file.name}...`,
+ containerTags: [project],
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ status: "processing",
+ type: "file",
+ metadata: {
+ fileName: file.name,
+ fileSize: file.size,
+ mimeType: file.type,
+ },
+ memoryEntries: [],
+ }
+
+ queryClient.setQueryData(
+ ["documents-with-memories", project],
+ (old: DocumentsQueryData | undefined) => {
+ const existingDocs = old?.documents ?? []
+ return {
+ ...old,
+ documents: [optimisticMemory, ...existingDocs],
+ totalCount: (old?.totalCount ?? 0) + 1,
+ }
+ },
+ )
+
+ return { previousMemories }
+ },
+ onError: (_error, variables, context) => {
+ if (context?.previousMemories) {
+ queryClient.setQueryData(
+ ["documents-with-memories", variables.project],
+ context.previousMemories,
+ )
+ }
+ toast.error("Failed to upload file", {
+ description: _error instanceof Error ? _error.message : "Unknown error",
+ })
+ },
+ onSuccess: (_data, variables) => {
+ toast.success("File uploaded successfully!", {
+ description: "Your file is being processed",
+ })
+ queryClient.invalidateQueries({
+ queryKey: ["documents-with-memories", variables.project],
+ })
+ onClose()
+ },
+ })
+
+ return {
+ noteMutation,
+ linkMutation,
+ fileMutation,
+ }
+}
diff --git a/apps/web/hooks/use-memories-usage.ts b/apps/web/hooks/use-memories-usage.ts
new file mode 100644
index 00000000..fb8a5aea
--- /dev/null
+++ b/apps/web/hooks/use-memories-usage.ts
@@ -0,0 +1,35 @@
+import { fetchMemoriesFeature, fetchSubscriptionStatus } from "@lib/queries"
+import type { useCustomer } from "autumn-js/react"
+
+export function useMemoriesUsage(autumn: ReturnType<typeof useCustomer>) {
+ const {
+ data: status = {
+ consumer_pro: { allowed: false, status: null },
+ },
+ isLoading: isCheckingStatus,
+ } = fetchSubscriptionStatus(autumn, !autumn.isLoading)
+
+ const proStatus = status.consumer_pro
+ const hasProProduct = proStatus?.status !== null
+
+ const { data: memoriesCheck, isLoading: isLoadingMemories } =
+ fetchMemoriesFeature(autumn, !isCheckingStatus && !autumn.isLoading)
+
+ const memoriesUsed = memoriesCheck?.usage ?? 0
+ const memoriesLimit = memoriesCheck?.included_usage ?? 0
+
+ const isLoading = autumn.isLoading || isCheckingStatus || isLoadingMemories
+
+ const usagePercent =
+ memoriesLimit <= 0
+ ? 0
+ : Math.min(Math.max((memoriesUsed / memoriesLimit) * 100, 0), 100)
+
+ return {
+ memoriesUsed,
+ memoriesLimit,
+ hasProProduct,
+ isLoading,
+ usagePercent,
+ }
+}