feat: move to react query

This commit is contained in:
Salman
2024-03-15 17:17:39 +05:30
parent 393c0c9e90
commit 3ae27e088f
3 changed files with 68 additions and 44 deletions
@@ -1,5 +1,5 @@
/* eslint-disable react/no-danger */ /* eslint-disable react/no-danger */
import React, { forwardRef, TextareaHTMLAttributes, useRef, useState } from "react"; import React, { forwardRef, TextareaHTMLAttributes, useEffect, useRef, useState } from "react";
import { faChevronRight, faFolder, faKey, faRecycle } from "@fortawesome/free-solid-svg-icons"; import { faChevronRight, faFolder, faKey, faRecycle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
@@ -7,8 +7,8 @@ import { twMerge } from "tailwind-merge";
import { useWorkspace } from "@app/context"; import { useWorkspace } from "@app/context";
import { useToggle } from "@app/hooks"; import { useToggle } from "@app/hooks";
import { useGetUserWsKey } from "@app/hooks/api"; import { useGetUserWsKey } from "@app/hooks/api";
import { fetchProjectFolders } from "@app/hooks/api/secretFolders/queries"; import { useGetFoldersByEnv } from "@app/hooks/api/secretFolders/queries";
import { decryptSecrets, fetchProjectEncryptedSecrets } from "@app/hooks/api/secrets/queries"; import { useGetProjectSecrets } from "@app/hooks/api/secrets/queries";
const REGEX_REFERENCE = /(\${([^}]*)})/g; const REGEX_REFERENCE = /(\${([^}]*)})/g;
const replaceContentWithDot = (str: string) => { const replaceContentWithDot = (str: string) => {
@@ -26,7 +26,7 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => {
if (!isVisible) return replaceContentWithDot(content); if (!isVisible) return replaceContentWithDot(content);
let skipNext = false; let skipNext = false;
const formatedContent = content.split(REGEX_REFERENCE).flatMap((el, i) => { const formattedContent = content.split(REGEX_REFERENCE).flatMap((el, i) => {
const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}"); const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}");
if (isInterpolationSyntax) { if (isInterpolationSyntax) {
skipNext = true; skipNext = true;
@@ -46,7 +46,7 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => {
// akhilmhdh: Dont remove this br. I am still clueless how this works but weirdly enough // akhilmhdh: Dont remove this br. I am still clueless how this works but weirdly enough
// when break is added a line break works properly // when break is added a line break works properly
return formatedContent.concat(<br />); return formattedContent.concat(<br />);
}; };
type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & { type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
@@ -59,7 +59,7 @@ type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
secretPath?: string; secretPath?: string;
}; };
type VariableType = { type ReferenceType = {
name: string; name: string;
type: "folder" | "secret"; type: "folder" | "secret";
slug?: string; slug?: string;
@@ -77,66 +77,90 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
isDisabled, isDisabled,
isReadOnly, isReadOnly,
onFocus, onFocus,
secretPath, secretPath: propSecretPath,
environment, environment: propEnvironment,
onChange, onChange,
...props ...props
}, },
ref ref
) => { ) => {
const childRef = useRef<HTMLTextAreaElement>(null);
const [isSecretFocused, setIsSecretFocused] = useToggle(); const [isSecretFocused, setIsSecretFocused] = useToggle();
const [showReferencePopup, setShowReferencePopup] = useState<boolean>(false);
const [value, setValue] = useState<string>(propValue || ""); const [value, setValue] = useState<string>(propValue || "");
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const [listVariables, setListVariables] = useState<VariableType[]>([]); const [listReference, setListReference] = useState<ReferenceType[]>([]);
const [showReferencePopup, setShowReferencePopup] = useState<boolean>(false);
const [referenceKey, setReferenceKey] = useState<string>();
const [lastSelectionIndex, setLastSelectionIndex] = useState<number>(0); const [lastSelectionIndex, setLastSelectionIndex] = useState<number>(0);
const childRef = useRef<HTMLTextAreaElement>(null); const [secretPath, setSecretPath] = useState<string>(
propSecretPath || currentWorkspace?.environments?.[0].slug!
);
const [environment, setEnvironment] = useState<string>(
propEnvironment || currentWorkspace?.environments?.[0].slug!
);
const workspaceId = currentWorkspace?.id || ""; const workspaceId = currentWorkspace?.id || "";
const { data: decryptFileKey } = useGetUserWsKey(workspaceId); const { data: decryptFileKey } = useGetUserWsKey(workspaceId);
const { data: secrets } = useGetProjectSecrets({
decryptFileKey: decryptFileKey!,
environment,
secretPath,
workspaceId
});
const { folderNames: folders } = useGetFoldersByEnv({
path: secretPath,
environments: [environment],
projectId: workspaceId
});
async function extractReference(refValue: string) { useEffect(() => {
const isNested = refValue.includes(".");
const currentListVariable: VariableType[] = [];
let currentEnvironment = environment; let currentEnvironment = propEnvironment;
let currentSecretPath = secretPath || "/"; let currentSecretPath = propSecretPath || "/";
if (!referenceKey) {
setSecretPath(currentSecretPath);
setEnvironment(currentEnvironment!);
return;
}
const isNested = referenceKey.includes(".");
const currentListReference: ReferenceType[] = [];
if (isNested) { if (isNested) {
const [envSlug, ...folderPaths] = refValue.split("."); const [envSlug, ...folderPaths] = referenceKey.split(".");
currentEnvironment = envSlug; currentEnvironment = envSlug;
currentSecretPath = `/${folderPaths?.join("/")}` || "/"; currentSecretPath = `/${folderPaths?.join("/")}` || "/";
} }
if (!currentEnvironment || !decryptFileKey || !currentSecretPath || !currentWorkspace) { if (!currentEnvironment || !decryptFileKey || !currentSecretPath || !currentWorkspace || !referenceKey) {
setListVariables(currentListVariable); // this need to clean up?
setListReference(currentListReference);
return; return;
} }
setSecretPath(currentSecretPath);
setEnvironment(currentEnvironment);
setShowReferencePopup(true);
}, [referenceKey])
// Todo: Move to react query useEffect(() => {
const [encryptSecrets, folders] = await Promise.all([ const currentListReference: ReferenceType[] = [];
fetchProjectEncryptedSecrets({ const isNested = referenceKey?.includes(".");
workspaceId,
environment: currentEnvironment,
secretPath: currentSecretPath
}),
// secret reference based on folder only support for nested reference that start with envs
isNested ? fetchProjectFolders(workspaceId, currentEnvironment, currentSecretPath) : []
]);
folders?.forEach((folder) => { if (isNested) {
currentListVariable.unshift({ name: folder.name, type: "folder" }); folders?.forEach((folder) => {
}); currentListReference.unshift({ name: folder, type: "folder" });
});
const secrets = decryptSecrets(encryptSecrets, decryptFileKey); }
secrets?.forEach((secret) => { secrets?.forEach((secret) => {
currentListVariable.unshift({ name: secret.key, type: "secret" }); currentListReference.unshift({ name: secret.key, type: "secret" });
}); });
setListVariables(currentListVariable); setListReference(currentListReference);
setShowReferencePopup(true); }, [secrets, referenceKey]);
}
function findMatch(str: string, start: number) { function findMatch(str: string, start: number) {
const matches = [...str.matchAll(REGEX_REFERENCE)]; const matches = [...str.matchAll(REGEX_REFERENCE)];
@@ -169,7 +193,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
const match = findMatch(text, pos); const match = findMatch(text, pos);
if (match && typeof match.index !== "undefined") { if (match && typeof match.index !== "undefined") {
setLastSelectionIndex(pos); setLastSelectionIndex(pos);
extractReference(match?.[2]); setReferenceKey(match?.[2]);
} }
setShowReferencePopup(!!match); setShowReferencePopup(!!match);
@@ -252,7 +276,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
value.slice(referenceEndIndex) value.slice(referenceEndIndex)
]; ];
let oldReferenceStr = oldReference.slice(2, oldReference.length - 1); let oldReferenceStr = oldReference.slice(2, -1);
let replaceReference = ""; let replaceReference = "";
let offset = 3; let offset = 3;
@@ -277,7 +301,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
// TODO: there should be a better way to do // TODO: there should be a better way to do
onChange?.({ target: { value: newValue } } as any); onChange?.({ target: { value: newValue } } as any);
setCaretPos(start.length + replaceReference.length + offset); setCaretPos(start.length + replaceReference.length + offset);
if (type !== "secret") extractReference(replaceReference); if (type !== "secret") setReferenceKey(replaceReference);
} }
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) { function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
@@ -305,7 +329,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
}} }}
> >
<div className="max-w-60 h-full w-full flex-col items-center justify-center rounded-md py-4 text-white"> <div className="max-w-60 h-full w-full flex-col items-center justify-center rounded-md py-4 text-white">
{listVariables.map((e, i) => { {listReference.map((e, i) => {
return ( return (
<button <button
className="flex items-center justify-between border-b border-mineshaft-600 px-2 py-1 text-left last:border-b-0" className="flex items-center justify-between border-b border-mineshaft-600 px-2 py-1 text-left last:border-b-0"
@@ -24,7 +24,7 @@ export const folderQueryKeys = {
["secret-folders", { projectId, environment, path }] as const ["secret-folders", { projectId, environment, path }] as const
}; };
export const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => { const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => {
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", { const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", {
params: { params: {
workspaceId, workspaceId,
+1 -1
View File
@@ -98,7 +98,7 @@ export const decryptSecrets = (
return secrets; return secrets;
}; };
export const fetchProjectEncryptedSecrets = async ({ const fetchProjectEncryptedSecrets = async ({
workspaceId, workspaceId,
environment, environment,
secretPath secretPath