site/feeds.html 8.4 K raw
1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
    <title>Blog Feeds</title>
7
    <link rel="apple-touch-icon" sizes="180x180" href="https://blogfeeds.net/apple-touch-icon.png">
8
    <link rel="icon" type="image/png" sizes="32x32" href="https://blogfeeds.net/favicon-32x32.png">
9
    <link rel="icon" type="image/png" sizes="16x16" href="https://blogfeeds.net/favicon-16x16.png">
10
    <link rel="manifest" href="https://blogfeeds.net/site.webmanifest">
11
    <link rel="stylesheet" href="tailwindcss" />
12
    <link rel="preconnect" href="https://fonts.googleapis.com">
13
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
14
    <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" rel="stylesheet">
15
    <meta name="description" content="Creating organic networks through Blogs, RSS, and Feeds">
16
17
    <!-- Facebook Meta Tags -->
18
    <meta property="og:url" content="https://blogfeeds.net">
19
    <meta property="og:type" content="website">
20
    <meta property="og:title" content="Blog Feeds">
21
    <meta property="og:description" content="Creating organic networks through Blogs, RSS, and Feeds">
22
    <meta property="og:image" content="/og.png">
23
24
    <!-- Twitter Meta Tags -->
25
    <meta name="twitter:card" content="summary_large_image">
26
    <meta property="twitter:domain" content="blogfeeds.net">
27
    <meta property="twitter:url" content="https://blogfeeds.net">
28
    <meta name="twitter:title" content="Blog Feeds">
29
    <meta name="twitter:description" content="Creating organic networks through Blogs, RSS, and Feeds">
30
    <meta name="twitter:image" content="/og.png">
31
    <style>
32
      body {
33
        font-family: 'DM Sans', sans-serif;
34
      }
35
      details[open] summary svg {
36
        transform: rotate(90deg);
37
      }
38
    </style>
39
</head>
40
<body class="min-h-screen sm:py-0 py-8">
41
  <div class="flex flex-col items-center px-4 pt-12 pb-4">
42
    <div class="max-w-xl space-y-4 w-full">
43
      <a href="/" class="inline-flex items-center text-blue-600 hover:text-blue-800 mb-4 transition-colors">
44
        <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
45
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
46
        </svg>
47
        Back
48
      </a>
49
      <img src="https://blogfeeds.net/feeds.svg" alt="Feeds" id="feeds" class="w-5/6 mx-auto py-4" />
50
      <p class="text-gray-700 mb-6">To help foster a community around the concept of Blog Feeds, this page acts as an aggregator of those who have a feeds page on their blog. If that's you and you don't see your blog below, then submit the RSS feed below and we'll review it regularly to add it! Remember, you must have a <a class="underline text-blue-500" target="_blank" href="/#feeds">Feeds page</a> (or something similar like a Blogroll) in order to qualify.</p>
51
52
      <div class="mb-6">
53
        <p class="text-gray-700 mb-2">This collection of feeds is also accessible via API</p>
54
        <div class="bg-gray-100 rounded-lg p-3 !font-mono text-sm">
55
          <div class="mb-4">curl https://api.blogfeeds.net?format=json</div>
56
          <div>curl https://api.blogfeeds.net?format=opml</div>
57
        </div>
58
      </div>
59
60
      <form id="feedForm" class="space-y-4">
61
        <div>
62
          <label for="url" class="block text-sm font-medium text-gray-700 mb-2">Blog URL</label>
63
          <input
64
            type="url"
65
            id="url"
66
            name="url"
67
            required
68
            placeholder="https://example.com"
69
            class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors"
70
          />
71
        </div>
72
73
        <button
74
          type="submit"
75
          class="w-full bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg transition-colors"
76
        >
77
          Submit Feed
78
        </button>
79
      </form>
80
81
      <div id="message" class="hidden my-4 p-4 rounded-lg"></div>
82
    </div>
83
  </div>
84
85
  <section class="flex flex-col items-center px-4 mb-12">
86
    <div class="max-w-xl space-y-4 w-full">
87
      <div id="feedsList" class="space-y-3">
88
        <div class="text-center py-8 text-gray-500">Loading feeds...</div>
89
      </div>
90
    </div>
91
  </section>
92
93
  <script>
94
    // Load feeds on page load
95
    async function loadFeeds() {
96
      const feedsList = document.getElementById('feedsList');
97
98
      try {
99
        const response = await fetch('https://api.blogfeeds.net');
100
        const data = await response.json();
101
102
        // If you're reading this, ignore this section 🙄
103
        if (data.subscriptions && data.subscriptions.length > 0) {
104
          feedsList.innerHTML = '';
105
106
          data.subscriptions.forEach(feed => {
107
            const feedCard = document.createElement('div');
108
            feedCard.className = 'p-4 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors';
109
110
            const titleContainer = document.createElement('h3');
111
            titleContainer.className = 'font-semibold text-lg mb-1';
112
113
            const titleLink = document.createElement('a');
114
            titleLink.href = feed.htmlUrl;
115
            titleLink.target = '_blank';
116
            titleLink.rel = 'noopener noreferrer';
117
            titleLink.className = 'hover:text-blue-600 transition-colors';
118
            titleLink.textContent = feed.title;
119
120
            // Yes I have to do it this way but I'm not going to explain why. You're just gonna have to trust me on this one.
121
            const urlLink = document.createElement('a');
122
            urlLink.href = feed.url;
123
            urlLink.target = '_blank';
124
            urlLink.rel = 'noopener noreferrer';
125
            urlLink.className = 'text-sm text-gray-600 hover:text-blue-600 transition-colors break-all inline-flex items-center gap-2';
126
            urlLink.innerHTML = `
127
              <svg class="w-3 h-3 flex-shrink-0" fill="currentColor" viewBox="0 0 24 24">
128
                <path d="M6.503 20.752c0 1.794-1.456 3.248-3.251 3.248-1.796 0-3.252-1.454-3.252-3.248 0-1.794 1.456-3.248 3.252-3.248 1.795.001 3.251 1.454 3.251 3.248zm-6.503-12.572v4.811c6.05.062 10.96 4.966 11.022 11.009h4.817c-.062-8.71-7.118-15.758-15.839-15.82zm0-3.368c10.58.046 19.152 8.594 19.183 19.188h4.817c-.03-13.231-10.755-23.954-24-24v4.812z"/>
129
              </svg>
130
              ${feed.url}
131
            `;
132
133
            titleContainer.appendChild(titleLink);
134
            feedCard.appendChild(titleContainer);
135
            feedCard.appendChild(urlLink);
136
            feedsList.appendChild(feedCard);
137
          });
138
        } else {
139
          feedsList.innerHTML = '<div class="text-center py-8 text-gray-500">No feeds available yet.</div>';
140
        }
141
      } catch (error) {
142
        feedsList.innerHTML = '<div class="text-center py-8 text-red-600">Failed to load feeds. Please try again later.</div>';
143
      }
144
    }
145
146
    // Load feeds when page loads
147
    loadFeeds();
148
149
    document.getElementById('feedForm').addEventListener('submit', async (e) => {
150
      e.preventDefault();
151
152
      const url = document.getElementById('url').value;
153
      const messageDiv = document.getElementById('message');
154
      const submitBtn = e.target.querySelector('button[type="submit"]');
155
156
      // Show loading state
157
      submitBtn.disabled = true;
158
      submitBtn.textContent = 'Submitting...';
159
      messageDiv.classList.add('hidden');
160
161
      try {
162
        const response = await fetch('https://api.blogfeeds.net/add-feed', {
163
          method: 'POST',
164
          headers: {
165
            'Content-Type': 'application/json',
166
          },
167
          body: JSON.stringify({ url })
168
        });
169
170
        const data = await response.json();
171
172
        if (response.ok) {
173
          messageDiv.className = 'mt-4 p-4 rounded-lg bg-green-100 text-green-800';
174
          messageDiv.textContent = 'Feed submitted for review!';
175
          document.getElementById('url').value = '';
176
        } else {
177
          messageDiv.className = 'mt-4 p-4 rounded-lg bg-red-100 text-red-800';
178
          messageDiv.textContent = data.error || 'Failed to submit feed. Please try again.';
179
        }
180
      } catch (error) {
181
        messageDiv.className = 'mt-4 p-4 rounded-lg bg-red-100 text-red-800';
182
        messageDiv.textContent = 'Network error. Please try again.';
183
      } finally {
184
        submitBtn.disabled = false;
185
        submitBtn.textContent = 'Submit Feed';
186
        messageDiv.classList.remove('hidden');
187
      }
188
    });
189
  </script>
190
</body>
191
</html>