From 6fa13a06411048f3217397f4285b3e64e7b9ee58 Mon Sep 17 00:00:00 2001
From: Adam Mathes <adam@adammathes.com>
Date: Sat, 14 Feb 2026 09:42:14 -0800
Subject: feature: implement full OPML and Text import/export (fixing
 NK-r6nhj0)

---
 frontend/src/components/Settings.tsx | 64 +++++++++++++++++++++++++++++++++---
 1 file changed, 60 insertions(+), 4 deletions(-)

(limited to 'frontend/src/components/Settings.tsx')

diff --git a/frontend/src/components/Settings.tsx b/frontend/src/components/Settings.tsx
index 3f508e9..6b6dab1 100644
--- a/frontend/src/components/Settings.tsx
+++ b/frontend/src/components/Settings.tsx
@@ -9,6 +9,8 @@ export default function Settings() {
   const [loading, setLoading] = useState(false);
   const [error, setError] = useState<string | null>(null);
 
+  const [importFile, setImportFile] = useState<File | null>(null);
+
   const fetchFeeds = () => {
     setLoading(true);
     apiFetch('/api/feed/')
@@ -30,8 +32,6 @@ export default function Settings() {
     fetchFeeds();
   }, []);
 
-
-
   const handleAddFeed = (e: React.FormEvent) => {
     e.preventDefault();
     if (!newFeedUrl) return;
@@ -48,7 +48,7 @@ export default function Settings() {
       })
       .then(() => {
         setNewFeedUrl('');
-        fetchFeeds(); // Refresh list (or we could append if server returns full feed object)
+        fetchFeeds();
       })
       .catch((err) => {
         setError(err.message);
@@ -74,6 +74,34 @@ export default function Settings() {
       });
   };
 
+  const handleImport = (e: React.FormEvent) => {
+    e.preventDefault();
+    if (!importFile) return;
+
+    setLoading(true);
+    const formData = new FormData();
+    formData.append('file', importFile);
+    formData.append('format', 'opml');
+
+    apiFetch('/api/import', {
+      method: 'POST',
+      body: formData,
+    })
+      .then((res) => {
+        if (!res.ok) throw new Error('Failed to import feeds');
+        return res.json();
+      })
+      .then(() => {
+        setImportFile(null);
+        fetchFeeds();
+        alert('Import successful!');
+      })
+      .catch((err) => {
+        setError(err.message);
+        setLoading(false);
+      });
+  };
+
   return (
     <div className="settings-page">
       <h2>Settings</h2>
@@ -94,9 +122,37 @@ export default function Settings() {
             Add Feed
           </button>
         </form>
-        {error && <p className="error-message">{error}</p>}
       </div>
 
+      <div className="import-export-section">
+        <div className="import-section">
+          <h3>Import Feeds (OPML)</h3>
+          <form onSubmit={handleImport} className="import-form">
+            <input
+              type="file"
+              accept=".opml,.xml,.txt"
+              onChange={(e) => setImportFile(e.target.files?.[0] || null)}
+              className="file-input"
+              disabled={loading}
+            />
+            <button type="submit" disabled={!importFile || loading}>
+              Import
+            </button>
+          </form>
+        </div>
+
+        <div className="export-section">
+          <h3>Export Feeds</h3>
+          <div className="export-buttons">
+            <a href="/api/export/opml" className="export-btn">OPML</a>
+            <a href="/api/export/text" className="export-btn">Text</a>
+            <a href="/api/export/json" className="export-btn">JSON</a>
+          </div>
+        </div>
+      </div>
+
+      {error && <p className="error-message">{error}</p>}
+
       <div className="feed-list-section">
         <h3>Manage Feeds</h3>
         {loading && <p>Loading...</p>}
-- 
cgit v1.2.3

