feat: hide expiry condition to end user & fix decrement condition in knex

This commit is contained in:
ShubhamPalriwala
2024-05-31 10:58:49 +05:30
parent 862e0437e7
commit e7d32b5f2d
6 changed files with 33 additions and 122 deletions
+1 -1
View File
@@ -128,7 +128,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(db: Kne
} }
if ($decr) { if ($decr) {
Object.entries($decr).forEach(([incrementField, incrementValue]) => { Object.entries($decr).forEach(([incrementField, incrementValue]) => {
void query.increment(incrementField, incrementValue); void query.decrement(incrementField, incrementValue);
}); });
} }
const [docs] = await query; const [docs] = await query;
@@ -62,9 +62,7 @@ export const secretSharingServiceFactory = ({
await secretSharingDAL.deleteById(sharedSecretId); await secretSharingDAL.deleteById(sharedSecretId);
return; return;
} }
await secretSharingDAL.updateById(sharedSecretId, { await secretSharingDAL.updateById(sharedSecretId, { $decr: { expiresAfterViews: 1 } });
expiresAfterViews: sharedSecret.expiresAfterViews - 1
});
} }
return sharedSecret; return sharedSecret;
}; };
@@ -24,8 +24,6 @@ export const useGetActiveSharedSecretByIdAndHashedHex = (id: string, hashedHex:
encryptedValue: data.encryptedValue, encryptedValue: data.encryptedValue,
iv: data.iv, iv: data.iv,
tag: data.tag, tag: data.tag,
expiresAt: data.expiresAt,
expiresAfterViews: data.expiresAfterViews
}; };
} }
}); });
@@ -19,8 +19,6 @@ export type TViewSharedSecretResponse = {
encryptedValue: string; encryptedValue: string;
iv: string; iv: string;
tag: string; tag: string;
expiresAt?: Date;
expiresAfterViews?: number;
}; };
export type TDeleteSharedSecretRequest = { export type TDeleteSharedSecretRequest = {
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo } from "react";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@@ -39,45 +39,10 @@ export const ShareSecretPublicPage = () => {
return ""; return "";
}, [data, publicKey]); }, [data, publicKey]);
const [timeLeft, setTimeLeft] = useState("");
const [isUrlCopied, , setIsUrlCopied] = useTimedReset<boolean>({ const [isUrlCopied, , setIsUrlCopied] = useTimedReset<boolean>({
initialState: false initialState: false
}); });
const millisecondsPerDay = 1000 * 60 * 60 * 24;
const millisecondsPerHour = 1000 * 60 * 60;
const millisecondsPerMinute = 1000 * 60;
useEffect(() => {
const updateTimer = () => {
if (data) {
if (data.expiresAt) {
const expirationTime = new Date(data.expiresAt).getTime();
const currentTime = new Date().getTime();
const timeDifference = expirationTime - currentTime;
if (timeDifference < 0) {
setTimeLeft("Expired");
} else {
const hoursRemaining = Math.floor(
(timeDifference % millisecondsPerDay) / millisecondsPerHour
);
const minutesRemaining = Math.floor(
(timeDifference % millisecondsPerHour) / millisecondsPerMinute
);
const secondsRemaining = Math.floor((timeDifference % millisecondsPerMinute) / 1000);
setTimeLeft(`${hoursRemaining}h ${minutesRemaining}m ${secondsRemaining}s`);
}
} else if (data.expiresAfterViews) {
setTimeLeft(`${data.expiresAfterViews - 1} more views`);
}
}
};
const timer = setInterval(updateTimer, 1000);
return () => clearInterval(timer);
}, [data?.expiresAt]);
useEffect(() => { useEffect(() => {
if (isUrlCopied) { if (isUrlCopied) {
setTimeout(() => setIsUrlCopied(false), 2000); setTimeout(() => setIsUrlCopied(false), 2000);
@@ -106,14 +71,12 @@ export const ShareSecretPublicPage = () => {
<DragonMainImage /> <DragonMainImage />
<div className="m-4 flex flex-1 flex-col items-center justify-start md:m-0"> <div className="m-4 flex flex-1 flex-col items-center justify-start md:m-0">
<p className="mt-8 mb-2 text-xl font-semibold text-mineshaft-100 md:mt-20"> <p className="mt-8 mb-2 text-xl font-semibold text-mineshaft-100 md:mt-20">
Secret Details Shared Secret
</p> </p>
<div className="mb-16 rounded-lg border border-mineshaft-600 bg-mineshaft-900 md:p-8"> <div className="mb-4 rounded-lg md:p-2">
<SecretTable <SecretTable
isLoading={isLoading} isLoading={isLoading}
sharedSecret={data}
decryptedSecret={decryptedSecret} decryptedSecret={decryptedSecret}
timeLeft={timeLeft}
isUrlCopied={isUrlCopied} isUrlCopied={isUrlCopied}
copyUrlToClipboard={copyUrlToClipboard} copyUrlToClipboard={copyUrlToClipboard}
/> />
@@ -1,90 +1,44 @@
import { faCheck, faCopy, faKey } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faCopy, faKey } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import { EmptyState, IconButton, SecretInput, Td, Tr } from "@app/components/v2";
EmptyState,
IconButton,
SecretInput,
Table,
TableContainer,
TBody,
Td,
Th,
THead,
Tr
} from "@app/components/v2";
import { TViewSharedSecretResponse } from "@app/hooks/api/secretSharing";
type Props = { type Props = {
isLoading: boolean; isLoading: boolean;
sharedSecret?: TViewSharedSecretResponse;
decryptedSecret: string; decryptedSecret: string;
timeLeft: string;
isUrlCopied: boolean; isUrlCopied: boolean;
copyUrlToClipboard: () => void; copyUrlToClipboard: () => void;
}; };
export const SecretTable = ({ export const SecretTable = ({
isLoading, isLoading,
sharedSecret,
decryptedSecret, decryptedSecret,
timeLeft,
isUrlCopied, isUrlCopied,
copyUrlToClipboard copyUrlToClipboard
}: Props) => { }: Props) => (
return ( <div className="flex items-center rounded border border-solid border-mineshaft-700 bg-mineshaft-800 p-4">
<TableContainer> {isLoading && <div className="bg-mineshaft-800 text-center text-bunker-400">Loading...</div>}
<Table> {!isLoading && !decryptedSecret && (
<THead> <Tr>
<Tr> <Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-400">
<Th>Secret</Th> <EmptyState title="Secret has either expired or does not exist!" icon={faKey} />
<Th>Valid Until</Th> </Td>
</Tr> </Tr>
</THead> )}
<TBody> {!isLoading && decryptedSecret && (
{!isLoading && sharedSecret && decryptedSecret && ( <>
<Tr key={sharedSecret.encryptedValue}> <div className="max-w-[20rem] flex-1 break-words pr-4">
<Td> <SecretInput isVisible value={decryptedSecret} readOnly />
<div className="flex items-center md:space-x-2"> </div>
<div className="max-w-[20rem] flex-1 break-words"> <IconButton
<SecretInput isVisible value={decryptedSecret} readOnly /> ariaLabel="copy to clipboard"
</div> onClick={copyUrlToClipboard}
<IconButton className="rounded p-2 hover:bg-gray-700"
ariaLabel="copy to clipboard" size="xs"
onClick={copyUrlToClipboard} >
className="rounded p-2 hover:bg-gray-700" <FontAwesomeIcon icon={isUrlCopied ? faCheck : faCopy} />
size="xs" </IconButton>
> </>
<FontAwesomeIcon icon={isUrlCopied ? faCheck : faCopy} /> )}
</IconButton> </div>
</div> );
</Td>
<Td>{timeLeft}</Td>
</Tr>
)}
{isLoading && (
<Tr>
<Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-400">
Loading...
</Td>
</Tr>
)}
{!isLoading && !sharedSecret && (
<Tr>
<Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-400">
<EmptyState title="No such secret is shared yet!" icon={faKey} />
</Td>
</Tr>
)}
{!isLoading && sharedSecret && !decryptedSecret && (
<Tr>
<Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-400">
<EmptyState title="Invalid URL to fetch the Secret!" icon={faKey} />
</Td>
</Tr>
)}
</TBody>
</Table>
</TableContainer>
);
};