diff --git a/.eslintrc.json b/.eslintrc.json
index db32bb9..fdca050 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -4,7 +4,7 @@
"import/no-relative-parent-imports": [
"error",
{
- "ignore": ["@/app", "@/components", "@/lib"]
+ "ignore": ["@/app", "@/components", "@/lib", "@/hooks"]
}
]
}
diff --git a/app/(stories)/item/[id]/reply-form.tsx b/app/(stories)/item/[id]/reply-form.tsx
index cc443be..b43be02 100644
--- a/app/(stories)/item/[id]/reply-form.tsx
+++ b/app/(stories)/item/[id]/reply-form.tsx
@@ -6,13 +6,25 @@ import { replyAction, type ReplyActionData } from "./actions";
import { Loader2 } from "lucide-react";
import { useFormStatus, useFormState } from "react-dom";
import Link from "next/link";
+import { useEffect, useState } from 'react';
+import useStoreState from '@/hooks/use-local-store';
export function ReplyForm({ storyId }: { storyId: string }) {
const [state, formAction] = useFormState(replyAction, {});
+ const [storedComment, setStoredComment] = useStoreState(storyId, "");
return (
-
);
}
@@ -20,11 +32,21 @@ export function ReplyForm({ storyId }: { storyId: string }) {
function ReplyFormFields({
error,
commentId,
+ setStoredComment,
+ storedComment,
storyId,
}: ReplyActionData & {
+ setStoredComment: (value: string) => void;
+ storedComment: string;
storyId: string;
}) {
const { pending } = useFormStatus();
+ const [isDraftSaved, setIsDraftSaved] = useState(false);
+
+ // Change state only after mount to prevent hydration errors
+ useEffect(() => {
+ setIsDraftSaved(!!storedComment);
+ }, [storedComment]);
return (
@@ -36,6 +58,10 @@ function ReplyFormFields({
className="w-full text-base bg-white"
placeholder="Write a reply..."
rows={4}
+ value={storedComment}
+ onChange={(e) => {
+ setStoredComment(e.target.value);
+ }}
onKeyDown={(e) => {
if (
(e.ctrlKey || e.metaKey) &&
@@ -59,7 +85,7 @@ function ReplyFormFields({
{pending ? : null}
Submit
- {error &&
+ {error ? (
"message" in error &&
(error.code === "AUTH_ERROR" ? (
@@ -71,7 +97,10 @@ function ReplyFormFields({
) : (
{error.message}
- ))}
+ ))
+ ) : isDraftSaved && !pending ? (
+ Saved to draft.
+ ) : null}
);
diff --git a/hooks/use-local-store.ts b/hooks/use-local-store.ts
new file mode 100644
index 0000000..5faedd7
--- /dev/null
+++ b/hooks/use-local-store.ts
@@ -0,0 +1,45 @@
+import { useState, useEffect, Dispatch, SetStateAction } from 'react';
+
+function useStoreState(
+ _key: string,
+ _initialValue: T | (() => T)
+): [T, Dispatch>];
+function useStoreState(
+ _key: string
+): [T | undefined, Dispatch>];
+
+function useStoreState(
+ key: string,
+ initialValue?: T | (() => T)
+) {
+ const [value, setValue] = useState(() => {
+ if (typeof window !== 'undefined' && window.localStorage) {
+ try {
+ const storedValue = localStorage.getItem(key);
+ return storedValue ? (JSON.parse(storedValue) as T) : initialValue;
+ } catch (error) {}
+ }
+ return initialValue;
+ });
+
+ useEffect(() => {
+ localStorage.setItem(key, JSON.stringify(value));
+ }, [key, value]);
+
+ useEffect(() => {
+ const handleStorageChange = (event: StorageEvent) => {
+ if (event.key === key)
+ setValue(event.newValue ? JSON.parse(event.newValue) : initialValue);
+ };
+
+ window.addEventListener('storage', handleStorageChange);
+
+ return () => {
+ window.removeEventListener('storage', handleStorageChange);
+ };
+ }, [key, initialValue]);
+
+ return [value, setValue] as const;
+}
+
+export default useStoreState;
\ No newline at end of file