Skip to content

Commit 0de0cf4

Browse files
committed
fix: able to read feeds with newer items listed last
fixes #52
1 parent 1cbcd6d commit 0de0cf4

File tree

4 files changed

+972
-54
lines changed

4 files changed

+972
-54
lines changed

lib/feedsub.js

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ module.exports = class FeedReader extends EventEmitter {
3434
// Create news emitter.
3535
this.news = new NewsEmitter({
3636
maxHistory: this.options.maxHistory,
37+
manageHistory: true,
3738
identifier: (item) => {
3839
item = item[0];
3940
return [
@@ -47,17 +48,7 @@ module.exports = class FeedReader extends EventEmitter {
4748
});
4849

4950
this.news.addHistory('item', this.options.history);
50-
51-
this.newitems = [];
5251
this.first = this.news.history.get('item').size === 0;
53-
54-
// Keep track of and emit new items.
55-
this.news.on('item', (item) => {
56-
if (!this.first || this.options.emitOnStart) {
57-
this.newitems.unshift(item);
58-
}
59-
});
60-
6152
this.getOpts = Object.assign({
6253
headers: {},
6354
acceptEncoding: {
@@ -108,10 +99,12 @@ module.exports = class FeedReader extends EventEmitter {
10899
let ended = false;
109100
let aborted = false;
110101
let req;
102+
let items = [];
103+
let newItems = [];
104+
let sortOrder = 0;
111105

112106
const error = (abort, err) => {
113107
ended = true;
114-
this.newitems = [];
115108
this.first = false;
116109
if (!aborted) {
117110
if (typeof callback === 'function') {
@@ -126,22 +119,21 @@ module.exports = class FeedReader extends EventEmitter {
126119
}
127120
};
128121

129-
const success = (results, abort) => {
122+
const success = (abort) => {
123+
if (ended) { return; }
130124
ended = true;
131-
this.newitems = [];
125+
this.news.addHistory('item', newItems.map((item) => [item]));
126+
if (this.first && !this.options.emitOnStart) {
127+
newItems = [];
128+
}
129+
newItems.forEach(this.emit.bind(this, 'item'));
130+
this.emit('items', newItems);
132131
if (abort && !aborted) {
133132
aborted = true;
134-
if (req) {
135-
req.abort();
136-
}
133+
req.abort();
137134
}
138-
139-
results.forEach((item) => {
140-
this.emit('item', item);
141-
});
142-
this.emit('items', results);
143135
if (typeof callback === 'function') {
144-
callback(null, results);
136+
callback(null, newItems);
145137
}
146138
};
147139

@@ -164,15 +156,15 @@ module.exports = class FeedReader extends EventEmitter {
164156
};
165157

166158
if (shouldSkip()) {
167-
return success([], true);
159+
return success();
168160
}
169161

170162
req = miniget(this.feed, this.getOpts);
171163
req.on('response', (res) => {
172164
// Check if not modified code is sent back
173165
// in this case, the body will be empty.
174166
if (res.statusCode === 304) {
175-
return success([]);
167+
return success();
176168
}
177169

178170
// Check headers for conditional get.
@@ -231,47 +223,78 @@ module.exports = class FeedReader extends EventEmitter {
231223
const firstitem = (item) => {
232224
// If date is the same as last, abort.
233225
if (date && this.options.lastDate === date) {
234-
return success([], true);
226+
return success();
235227
}
236228

237229
if (shouldSkip()) {
238-
return success([], true);
230+
return success();
239231
}
240232

241233
// Continue if dates differ.
242234
if (date) {
243235
this.options.lastDate = date;
244236
}
245-
parser.on('item', getitems);
246-
getitems(item);
237+
parser.on('item', getItem);
238+
getItem(item);
247239
};
248240

249241
parser.once('item', firstitem);
250242

251-
const getitems = (item) => {
252-
if (this.first && this.options.emitOnStart) {
253-
this.newitems.unshift(item);
243+
const getItemDate = (item) => new Date(item.pubdate || item.published || 0);
244+
const getItem = (item) => {
245+
if (sortOrder === 0) {
246+
items.push(item);
247+
sortOrder = getItemDate(item) - getItemDate(items[0]);
248+
if (sortOrder < 0) {
249+
items.forEach(getOlderItem);
250+
} else if (sortOrder > 0) {
251+
items.forEach(getNewerItem);
252+
}
253+
} else if (sortOrder < 0) {
254+
getOlderItem(item);
255+
256+
} else {
257+
getNewerItem(item);
258+
}
259+
};
254260

255-
} else if (!this.news.emit('item', item)) {
256-
// Check if this item has already been read in previous requests
257-
// if it has, then stop parsing the rest of the document.
258-
parser.removeListener('item', getitems);
259-
success(this.newitems);
261+
const getOlderItem = (item) => {
262+
if (this.first) {
263+
newItems.unshift(item);
264+
} else if (!ended) {
265+
let emitted = this.news.emit('item', item);
266+
if (emitted) {
267+
newItems.unshift(item);
268+
} else {
269+
// Check if this item has already been read in previous requests
270+
// if it has, then stop parsing the rest of the document.
271+
parser.removeListener('item', getItem);
272+
success(true);
273+
}
274+
}
275+
};
276+
277+
let foundPrevItem = false;
278+
const getNewerItem = (item) => {
279+
if (this.first) {
280+
newItems.push(item);
281+
} else if (!foundPrevItem && !this.news.emit('item', item)) {
282+
foundPrevItem = true;
283+
} else if (foundPrevItem && this.news.emit('item', item)) {
284+
newItems.push(item);
260285
}
261286
};
262287

263288
req.pipe(parser);
264289

265290
req.on('end', () => {
266-
if (!ended) {
267-
if (this.first && this.options.emitOnStart) {
268-
this.news.addHistory('item', this.newitems.map((item) => {
269-
return [item];
270-
}));
271-
}
272-
success(this.newitems);
273-
this.first = false;
291+
if (ended) { return; }
292+
// Process items in descending order if no order found at end.
293+
if (sortOrder === 0 && newItems.length === 0) {
294+
items.forEach(getOlderItem);
274295
}
296+
success();
297+
this.first = false;
275298
});
276299
});
277300

0 commit comments

Comments
 (0)