diff options
Diffstat (limited to 'app/src/components/bookmark-fetcher.tsx')
-rw-r--r-- | app/src/components/bookmark-fetcher.tsx | 143 |
1 files changed, 143 insertions, 0 deletions
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 |