Skip to content

Commit

Permalink
adds draft support
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotBraem committed Jan 9, 2025
1 parent a336be6 commit 97e7987
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 9 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@near-wallet-selector/modal-ui": "^8.9.13",
"@near-wallet-selector/my-near-wallet": "^8.9.13",
"cookie": "^1.0.2",
"date-fns": "^4.1.0",
"framer-motion": "^11.16.1",
"lucide-react": "^0.469.0",
"near-api-js": "^4.0.3",
Expand Down
37 changes: 28 additions & 9 deletions src/components/compose-post.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useState } from "react";
import { useDraftsStore } from "../store/drafts-store";
import { DraftsModal } from "./drafts-modal";

// This "widget" handles all of the editing for post content
// Calls "onSubmit" with an array of post objects
Expand All @@ -7,6 +9,7 @@ export function ComposePost({ onSubmit }) {
const [isThreadMode, setIsThreadMode] = useState(false);
const [posts, setPosts] = useState([{ text: "", image: null }]);
const [error, setError] = useState("");
const { setModalOpen, saveDraft } = useDraftsStore();

const handleTextChange = (index, value) => {
const newPosts = [...posts];
Expand Down Expand Up @@ -69,7 +72,13 @@ export function ComposePost({ onSubmit }) {

return (
<div className="space-y-3">
<div className="flex justify-end mb-2">
<div className="flex justify-end gap-2 mb-2">
<button
onClick={() => setModalOpen(true)}
className="text-sm px-3 py-1 border-2 border-gray-800 hover:bg-gray-100 shadow-[2px_2px_0_rgba(0,0,0,1)]"
>
Drafts
</button>
<button
onClick={toggleMode}
className="text-sm px-3 py-1 border-2 border-gray-800 hover:bg-gray-100 shadow-[2px_2px_0_rgba(0,0,0,1)]"
Expand Down Expand Up @@ -125,22 +134,32 @@ export function ComposePost({ onSubmit }) {
</div>
)}

<div className="flex justify-between items-center">
<div className="flex justify-between items-center gap-2">
<span className="text-sm text-gray-500">
{isThreadMode
? `${posts.length} parts`
: `${posts[0].text.length} characters`}
</span>
<button
onClick={handleSubmit}
disabled={posts.every((p) => !p.text.trim())}
className="flex items-center gap-2 px-6 py-2 border-2 border-gray-800 hover:bg-gray-100 shadow-[2px_2px_0_rgba(0,0,0,1)] disabled:opacity-50 disabled:cursor-not-allowed"
>
Post
</button>
<div className="flex gap-2">
<button
onClick={() => saveDraft(posts)}
disabled={posts.every((p) => !p.text.trim())}
className="px-4 py-2 border-2 border-gray-800 hover:bg-gray-100 shadow-[2px_2px_0_rgba(0,0,0,1)] disabled:opacity-50 disabled:cursor-not-allowed"
>
Save Draft
</button>
<button
onClick={handleSubmit}
disabled={posts.every((p) => !p.text.trim())}
className="flex items-center gap-2 px-6 py-2 border-2 border-gray-800 hover:bg-gray-100 shadow-[2px_2px_0_rgba(0,0,0,1)] disabled:opacity-50 disabled:cursor-not-allowed"
>
Post
</button>
</div>
</div>

{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
<DraftsModal onSelect={setPosts} />
</div>
);
}
78 changes: 78 additions & 0 deletions src/components/drafts-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { motion, AnimatePresence } from "framer-motion";
import { useDraftsStore } from "../store/drafts-store";
import { formatDistanceToNow } from "date-fns";

export function DraftsModal({ onSelect }) {
const { drafts, isModalOpen, setModalOpen, deleteDraft } = useDraftsStore();

if (!isModalOpen) return null;

return (
<AnimatePresence>
<div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
<motion.div
initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.95, opacity: 0 }}
className="w-full max-w-2xl border-2 border-gray-800 bg-white shadow-[4px_4px_0_rgba(0,0,0,1)] p-6"
>
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-bold">Drafts</h2>
<button
onClick={() => setModalOpen(false)}
className="text-gray-600 hover:text-gray-800"
>
</button>
</div>

{drafts.length === 0 ? (
<p className="text-gray-600 text-center py-8">No drafts saved yet</p>
) : (
<div className="space-y-4 max-h-[60vh] overflow-y-auto">
{drafts.map((draft) => (
<div
key={draft.id}
className="border-2 border-gray-800 p-4 shadow-[2px_2px_0_rgba(0,0,0,1)] hover:bg-gray-50"
>
<div className="flex justify-between items-start mb-2">
<span className="text-sm text-gray-600">
{formatDistanceToNow(new Date(draft.createdAt), {
addSuffix: true,
})}
</span>
<div className="flex gap-2">
<button
onClick={() => {
onSelect(draft.posts);
setModalOpen(false);
}}
className="text-sm px-3 py-1 border-2 border-gray-800 hover:bg-gray-100 shadow-[2px_2px_0_rgba(0,0,0,1)]"
>
Load
</button>
<button
onClick={() => deleteDraft(draft.id)}
className="text-sm px-3 py-1 border-2 border-red-600 text-red-600 hover:bg-red-50 shadow-[2px_2px_0_rgba(0,0,0,1)]"
>
Delete
</button>
</div>
</div>
<div className="text-sm text-gray-800">
{draft.posts.map((post, i) => (
<div key={i} className="mb-2">
{post.text.substring(0, 100)}
{post.text.length > 100 ? "..." : ""}
</div>
))}
</div>
</div>
))}
</div>
)}
</motion.div>
</div>
</AnimatePresence>
);
}
51 changes: 51 additions & 0 deletions src/store/drafts-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { create } from "zustand";

const STORAGE_KEY = "crosspost_drafts";

const loadDrafts = () => {
if (typeof window === "undefined") return [];
try {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
} catch (err) {
console.error("Failed to load drafts:", err);
return [];
}
};

const saveDrafts = (drafts) => {
if (typeof window === "undefined") return;
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(drafts));
} catch (err) {
console.error("Failed to save drafts:", err);
}
};

export const useDraftsStore = create((set, get) => ({
drafts: loadDrafts(),
isModalOpen: false,

setModalOpen: (isOpen) => set({ isModalOpen: isOpen }),

saveDraft: (posts) => {
const draft = {
id: Date.now(),
posts,
createdAt: new Date().toISOString(),
};
set((state) => {
const newDrafts = [draft, ...state.drafts];
saveDrafts(newDrafts);
return { drafts: newDrafts };
});
},

deleteDraft: (id) => {
set((state) => {
const newDrafts = state.drafts.filter((d) => d.id !== id);
saveDrafts(newDrafts);
return { drafts: newDrafts };
});
},
}));

0 comments on commit 97e7987

Please sign in to comment.