From ff3078e93411c3467d797258744a7f17a7dbdf0a Mon Sep 17 00:00:00 2001
From: polwex <polwex@sortug.com>
Date: Wed, 16 Jul 2025 10:07:06 +0700
Subject: m

---
 app/src/components/bookmark-fetcher.tsx | 143 ++++++++++++++++++++++++++++++++
 1 file changed, 143 insertions(+)
 create mode 100644 app/src/components/bookmark-fetcher.tsx

(limited to 'app/src/components/bookmark-fetcher.tsx')

diff --git a/app/src/components/bookmark-fetcher.tsx b/app/src/components/bookmark-fetcher.tsx
new file mode 100644
index 0000000..6522310
--- /dev/null
+++ b/app/src/components/bookmark-fetcher.tsx
@@ -0,0 +1,143 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { TwitterBookmark } from '../lib/bookmark-models';
+import { BookmarkStorageService } from '../lib/bookmark-storage';
+import { BookmarkList } from './bookmark-list';
+
+export function BookmarkFetcher() {
+  const [bookmarks, setBookmarks] = useState<TwitterBookmark[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [error, setError] = useState<string | null>(null);
+  const [username, setUsername] = useState('');
+  const [authToken, setAuthToken] = useState('');
+
+  // Load existing bookmarks on mount
+  useEffect(() => {
+    const existingBookmarks = BookmarkStorageService.getBookmarks();
+    setBookmarks(existingBookmarks);
+  }, []);
+
+  const handleFetchBookmarks = async () => {
+    if (!username || !authToken) {
+      setError('Please enter your Twitter username and auth token');
+      return;
+    }
+
+    setLoading(true);
+    setError(null);
+
+    try {
+      // Simple fetch to our API endpoint
+      const response = await fetch('/api/sync-bookmarks', {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({ username, authToken }),
+      });
+
+      const result = await response.json();
+
+      if (!result.success) {
+        throw new Error(result.error || 'Failed to fetch bookmarks');
+      }
+
+      // Load the newly saved bookmarks
+      const updatedBookmarks = BookmarkStorageService.getBookmarks();
+      setBookmarks(updatedBookmarks);
+      
+    } catch (err) {
+      setError(err instanceof Error ? err.message : 'Failed to fetch bookmarks');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleClearBookmarks = () => {
+    BookmarkStorageService.clearAll();
+    setBookmarks([]);
+  };
+
+  return (
+    <div className="space-y-6">
+      {/* Input fields */}
+      <div className="bg-white border border-gray-200 rounded-lg p-6">
+        <h2 className="text-xl font-semibold mb-4">Twitter Credentials</h2>
+        
+        <div className="space-y-4">
+          <div>
+            <label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
+              Twitter Username
+            </label>
+            <input
+              id="username"
+              type="text"
+              value={username}
+              onChange={(e) => setUsername(e.target.value)}
+              placeholder="your_twitter_username"
+              className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+            />
+          </div>
+          
+          <div>
+            <label htmlFor="authToken" className="block text-sm font-medium text-gray-700 mb-1">
+              Auth Token
+            </label>
+            <input
+              id="authToken"
+              type="password"
+              value={authToken}
+              onChange={(e) => setAuthToken(e.target.value)}
+              placeholder="your_auth_token"
+              className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+            />
+          </div>
+        </div>
+      </div>
+
+      {/* Action buttons */}
+      <div className="flex gap-4">
+        <button
+          onClick={handleFetchBookmarks}
+          disabled={loading}
+          className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-blue-300 disabled:cursor-not-allowed font-medium"
+        >
+          {loading ? (
+            <>
+              <svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
+                <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
+                <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
+              </svg>
+              Fetching...
+            </>
+          ) : (
+            'Fetch Twitter Bookmarks'
+          )}
+        </button>
+        
+        {bookmarks.length > 0 && (
+          <button
+            onClick={handleClearBookmarks}
+            className="px-6 py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 font-medium"
+          >
+            Clear All
+          </button>
+        )}
+      </div>
+
+      {/* Error message */}
+      {error && (
+        <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
+          {error}
+        </div>
+      )}
+
+      {/* Bookmark list */}
+      <div className="bg-white border border-gray-200 rounded-lg p-6">
+        <h2 className="text-xl font-semibold mb-4">Your Bookmarks</h2>
+        <BookmarkList bookmarks={bookmarks} />
+      </div>
+    </div>
+  );
+}
\ No newline at end of file
-- 
cgit v1.2.3

