summaryrefslogtreecommitdiff
path: root/src/feeds.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-11-23 15:27:58 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-11-23 15:27:58 +0200
commit678ed94bf55bc15592d14aed1ace04863e5483d1 (patch)
tree4301f4ac3d7bc48db4e8c45f6646205418c679ed /src/feeds.c
parente509af0fc8eee4b03766ec5c1f4faf6dae7f41b4 (diff)
Sidebar: Populate with feed entries
Diffstat (limited to 'src/feeds.c')
-rw-r--r--src/feeds.c78
1 files changed, 50 insertions, 28 deletions
diff --git a/src/feeds.c b/src/feeds.c
index cbfc36d0..5034c616 100644
--- a/src/feeds.c
+++ b/src/feeds.c
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
35#include <the_Foundation/stringset.h> 35#include <the_Foundation/stringset.h>
36#include <the_Foundation/thread.h> 36#include <the_Foundation/thread.h>
37#include <SDL_timer.h> 37#include <SDL_timer.h>
38#include <ctype.h>
38 39
39iDeclareType(Feeds) 40iDeclareType(Feeds)
40iDeclareType(FeedJob) 41iDeclareType(FeedJob)
@@ -109,12 +110,24 @@ static void submit_FeedJob_(iFeedJob *d) {
109} 110}
110 111
111static iFeedJob *startNextJob_Feeds_(iFeeds *d) { 112static iFeedJob *startNextJob_Feeds_(iFeeds *d) {
113 if (isEmpty_PtrArray(&d->jobs)) {
114 return NULL;
115 }
112 iFeedJob *job; 116 iFeedJob *job;
113 if (take_PtrArray(&d->jobs, 0, (void **) &job)) { 117 take_PtrArray(&d->jobs, 0, (void **) &job);
114 submit_FeedJob_(job); 118 submit_FeedJob_(job);
115 return job; 119 return job;
120}
121
122static void trimTitle_(iString *title) {
123 const char *start = constBegin_String(title);
124 iConstForEach(String, i, title) {
125 start = i.pos;
126 if (!isSpace_Char(i.value) && !(i.value < 128 && ispunct(i.value))) {
127 break;
128 }
116 } 129 }
117 return NULL; 130 remove_Block(&title->chars, 0, start - constBegin_String(title));
118} 131}
119 132
120static void parseResult_FeedJob_(iFeedJob *d) { 133static void parseResult_FeedJob_(iFeedJob *d) {
@@ -123,7 +136,7 @@ static void parseResult_FeedJob_(iFeedJob *d) {
123 iBeginCollect(); 136 iBeginCollect();
124 iRegExp *linkPattern = 137 iRegExp *linkPattern =
125 new_RegExp("^=>\\s*([^\\s]+)\\s+" 138 new_RegExp("^=>\\s*([^\\s]+)\\s+"
126 "([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])" 139 "([0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9])"
127 "([^0-9].*)", 140 "([^0-9].*)",
128 0); 141 0);
129 iString src; 142 iString src;
@@ -141,8 +154,11 @@ static void parseResult_FeedJob_(iFeedJob *d) {
141 iFeedEntry *entry = new_FeedEntry(); 154 iFeedEntry *entry = new_FeedEntry();
142 entry->bookmarkId = d->bookmarkId; 155 entry->bookmarkId = d->bookmarkId;
143 setRange_String(&entry->url, url); 156 setRange_String(&entry->url, url);
144 set_String(&entry->url, collect_String(lower_String(&entry->url))); 157 set_String(&entry->url,
158 absoluteUrl_String(url_GmRequest(d->request),
159 collect_String(lower_String(&entry->url))));
145 setRange_String(&entry->title, title); 160 setRange_String(&entry->title, title);
161 trimTitle_(&entry->title);
146 int year, month, day; 162 int year, month, day;
147 sscanf(date.start, "%04d-%02d-%02d", &year, &month, &day); 163 sscanf(date.start, "%04d-%02d-%02d", &year, &month, &day);
148 init_Time( 164 init_Time(
@@ -177,7 +193,7 @@ static iBool updateEntries_Feeds_(iFeeds *d, iPtrArray *incoming) {
177 delete_FeedEntry(entry); 193 delete_FeedEntry(entry);
178 if (changed) { 194 if (changed) {
179 /* TODO: better to use a new flag for read feed entries? */ 195 /* TODO: better to use a new flag for read feed entries? */
180 removeUrl_Visited(visited_App(), &entry->url); 196 removeUrl_Visited(visited_App(), &existing->url);
181 gotNew = iTrue; 197 gotNew = iTrue;
182 } 198 }
183 } 199 }
@@ -206,6 +222,7 @@ static iThreadResult fetch_Feeds_(iThread *thread) {
206 } 222 }
207 } 223 }
208 sleep_Thread(0.5); /* TODO: wait on a Condition so we can exit quickly */ 224 sleep_Thread(0.5); /* TODO: wait on a Condition so we can exit quickly */
225 if (d->stopWorker) break;
209 size_t ongoing = 0; 226 size_t ongoing = 0;
210 iForIndices(i, work) { 227 iForIndices(i, work) {
211 if (work[i]) { 228 if (work[i]) {
@@ -241,16 +258,18 @@ static iBool startWorker_Feeds_(iFeeds *d) {
241 if (d->worker) { 258 if (d->worker) {
242 return iFalse; /* Oops? */ 259 return iFalse; /* Oops? */
243 } 260 }
244 d->worker = new_Thread(fetch_Feeds_);
245 /* Queue up all the subscriptions for the worker. */ 261 /* Queue up all the subscriptions for the worker. */
246 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, isSubscribed_, NULL)) { 262 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, isSubscribed_, NULL)) {
247 iFeedJob job; 263 iFeedJob* job = new_FeedJob(i.ptr);
248 init_FeedJob(&job, i.ptr); 264 pushBack_PtrArray(&d->jobs, job);
249 pushBack_Array(&d->jobs, &job); 265 }
266 if (!isEmpty_Array(&d->jobs)) {
267 d->worker = new_Thread(fetch_Feeds_);
268 d->stopWorker = iFalse;
269 start_Thread(d->worker);
270 return iTrue;
250 } 271 }
251 d->stopWorker = iFalse; 272 return iFalse;
252 start_Thread(d->worker);
253 return iTrue;
254} 273}
255 274
256static uint32_t refresh_Feeds_(uint32_t interval, void *data) { 275static uint32_t refresh_Feeds_(uint32_t interval, void *data) {
@@ -265,7 +284,11 @@ static void stopWorker_Feeds_(iFeeds *d) {
265 join_Thread(d->worker); 284 join_Thread(d->worker);
266 iReleasePtr(&d->worker); 285 iReleasePtr(&d->worker);
267 } 286 }
268 /* TODO: Clear jobs */ 287 /* Clear remaining jobs. */
288 iForEach(PtrArray, i, &d->jobs) {
289 delete_FeedJob(i.ptr);
290 }
291 clear_PtrArray(&d->jobs);
269} 292}
270 293
271static int cmp_FeedEntryPtr_(const void *a, const void *b) { 294static int cmp_FeedEntryPtr_(const void *a, const void *b) {
@@ -308,7 +331,7 @@ iDeclareType(FeedHashNode)
308 331
309struct Impl_FeedHashNode { 332struct Impl_FeedHashNode {
310 iHashNode node; 333 iHashNode node;
311 uint32_t bookmarkId; 334 uint32_t bookmarkId;
312}; 335};
313 336
314static void load_Feeds_(iFeeds *d) { 337static void load_Feeds_(iFeeds *d) {
@@ -353,27 +376,26 @@ static void load_Feeds_(iFeeds *d) {
353 } 376 }
354 case 2: { 377 case 2: {
355 const uint32_t feedId = strtoul(line.start, NULL, 16); 378 const uint32_t feedId = strtoul(line.start, NULL, 16);
356 nextSplit_Rangecc(range_Block(src), "\n", &line); 379 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
357 const unsigned long long ts = strtoull(line.start, NULL, 10); 380 const unsigned long long ts = strtoull(line.start, NULL, 10);
358 nextSplit_Rangecc(range_Block(src), "\n", &line); 381 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
359 iString url; 382 const iRangecc urlRange = line;
360 initRange_String(&url, line); 383 if (!nextSplit_Rangecc(range_Block(src), "\n", &line)) break;
361 nextSplit_Rangecc(range_Block(src), "\n", &line); 384 const iRangecc titleRange = line;
362 iString title; 385 iString *url = newRange_String(urlRange);
363 initRange_String(&title, line); 386 iString *title = newRange_String(titleRange);
364 nextSplit_Rangecc(range_Block(src), "\n", &line);
365 /* Look it up in the hash. */ 387 /* Look it up in the hash. */
366 const iFeedHashNode *node = (iFeedHashNode *) value_Hash(feeds, feedId); 388 const iFeedHashNode *node = (iFeedHashNode *) value_Hash(feeds, feedId);
367 if (node) { 389 if (node) {
368 iFeedEntry *entry = new_FeedEntry(); 390 iFeedEntry *entry = new_FeedEntry();
369 entry->bookmarkId = node->bookmarkId; 391 entry->bookmarkId = node->bookmarkId;
370 entry->timestamp.ts.tv_sec = ts; 392 entry->timestamp.ts.tv_sec = ts;
371 set_String(&entry->url, &url); 393 set_String(&entry->url, url);
372 set_String(&entry->title, &title); 394 set_String(&entry->title, title);
373 insert_SortedArray(&d->entries, &entry); 395 insert_SortedArray(&d->entries, &entry);
374 } 396 }
375 deinit_String(&title); 397 delete_String(title);
376 deinit_String(&url); 398 delete_String(url);
377 break; 399 break;
378 } 400 }
379 } 401 }