IKHSAN
ENID Baca dalam Bahasa Indonesia

homeio

Public

Forked from doctor-io/homeio

Branch
main
Latest commit
958d74616 October 2026

useTrashActions.ts

Open raw
"use client";

import { useMutation, useQueryClient } from "@tanstack/react-query";
import type {
  TrashEmptyResponse,
  TrashDeleteRequest,
  TrashMoveRequest,
  TrashMoveResponse,
  TrashRestoreRequest,
  TrashRestoreResponse,
} from "@/lib/shared/contracts/files";
import { queryKeys } from "@/lib/shared/query-keys";

type TrashMoveApiResponse = {
  data: TrashMoveResponse;
};

type TrashRestoreApiResponse = {
  data: TrashRestoreResponse;
};

type TrashDeleteApiResponse = {
  data: {
    deleted: boolean;
    path: string;
  };
};

type TrashEmptyApiResponse = {
  data: TrashEmptyResponse;
};

function mapError(responseStatus: number, errorBody: { error?: string; code?: string }) {
  return (
    errorBody.error ??
    `Request failed (${responseStatus})${errorBody.code ? ` [${errorBody.code}]` : ""}`
  );
}

function getParentPath(filePath: string) {
  const normalized = filePath.trim();
  if (normalized.length === 0) return "";
  const separatorIndex = normalized.lastIndexOf("/");
  if (separatorIndex < 0) return "";
  return normalized.slice(0, separatorIndex);
}

function filesListKeyPrefix(filePath: string) {
  return ["files", "list", filePath] as const;
}

async function moveToTrash(payload: TrashMoveRequest) {
  const response = await fetch("/api/v1/files/trash/move", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    const errorBody = (await response.json().catch(() => ({}))) as {
      error?: string;
      code?: string;
    };
    throw new Error(mapError(response.status, errorBody));
  }

  const json = (await response.json()) as TrashMoveApiResponse;
  return json.data;
}

async function restoreFromTrash(payload: TrashRestoreRequest) {
  const response = await fetch("/api/v1/files/trash/restore", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    const errorBody = (await response.json().catch(() => ({}))) as {
      error?: string;
      code?: string;
    };
    throw new Error(mapError(response.status, errorBody));
  }

  const json = (await response.json()) as TrashRestoreApiResponse;
  return json.data;
}

async function deleteFromTrash(payload: TrashDeleteRequest) {
  const response = await fetch("/api/v1/files/trash/delete", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    const errorBody = (await response.json().catch(() => ({}))) as {
      error?: string;
      code?: string;
    };
    throw new Error(mapError(response.status, errorBody));
  }

  const json = (await response.json()) as TrashDeleteApiResponse;
  return json.data;
}

async function emptyTrashRequest() {
  const response = await fetch("/api/v1/files/trash/empty", {
    method: "POST",
  });

  if (!response.ok) {
    const errorBody = (await response.json().catch(() => ({}))) as {
      error?: string;
      code?: string;
    };
    throw new Error(mapError(response.status, errorBody));
  }

  const json = (await response.json()) as TrashEmptyApiResponse;
  return json.data;
}

export function useMoveToTrash() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: moveToTrash,
    onSuccess: (data, variables) => {
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix(getParentPath(variables.path)),
      });
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix("Trash"),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.fileContent(variables.path),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.trashEntries("Trash"),
      });
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix(getParentPath(data.trashPath)),
      });
    },
  });
}

export function useRestoreFromTrash() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: restoreFromTrash,
    onSuccess: (data, variables) => {
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix("Trash"),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.trashEntries("Trash"),
      });
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix(getParentPath(data.restoredPath)),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.fileContent(variables.path),
      });
    },
  });
}

export function useDeleteFromTrash() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: deleteFromTrash,
    onSuccess: (_data, variables) => {
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix(getParentPath(variables.path)),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.trashEntries("Trash"),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.fileContent(variables.path),
      });
    },
  });
}

export function useEmptyTrash() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: emptyTrashRequest,
    onSuccess: () => {
      void queryClient.invalidateQueries({
        queryKey: filesListKeyPrefix("Trash"),
      });
      void queryClient.invalidateQueries({
        queryKey: queryKeys.trashEntries("Trash"),
      });
    },
  });
}
bytes
5,507
lines
213
Type
ts
Latest commit
16 October 2026