diff --git a/packages/feeds/src/controller.ts b/packages/feeds/src/controller.ts index 98b419c..d5910a0 100644 --- a/packages/feeds/src/controller.ts +++ b/packages/feeds/src/controller.ts @@ -1,4 +1,4 @@ -import {inc, memoize, omitVals, max, min, now} from "@welshman/lib" +import {inc, defer, Deferred, memoize, omitVals, max, min, now} from "@welshman/lib" import {EPOCH, trimFilters, guessFilterDelta, TrustedEvent, Filter} from "@welshman/util" import {Feed, FeedType, RequestItem} from "./core.js" import {FeedCompiler, FeedCompilerOptions} from "./compiler.js" @@ -87,17 +87,17 @@ export class FeedController { const minSince = sinces.length === filters.length ? min(sinces) : EPOCH const initialDelta = guessFilterDelta(filters) - let loading = false + let promise: Deferred | undefined let delta = initialDelta let since = this.options.useWindowing ? maxUntil - delta : minSince let until = maxUntil return async (limit: number) => { - if (loading) { - return + if (promise) { + return promise } - loading = true + promise = defer() const requestFilters = filters! // Remove filters that don't fit our window @@ -148,32 +148,31 @@ export class FeedController { onExhausted?.() } - loading = false + promise.resolve() + promise = undefined } } - async _getDifferenceLoader(feeds: Feed[]) { + _getDifferenceLoader(feeds: Feed[]) { const exhausted = new Set() const skip = new Set() const events: TrustedEvent[] = [] const seen = new Set() - const controllers = await Promise.all( - feeds.map( - (thisFeed: Feed, i: number) => - new FeedController({ - ...this.options, - feed: thisFeed, - onExhausted: () => exhausted.add(i), - onEvent: (event: TrustedEvent) => { - if (i === 0) { - events.push(event) - } else { - skip.add(event.id) - } - }, - }), - ), + const controllers = feeds.map( + (thisFeed: Feed, i: number) => + new FeedController({ + ...this.options, + feed: thisFeed, + onExhausted: () => exhausted.add(i), + onEvent: (event: TrustedEvent) => { + if (i === 0) { + events.push(event) + } else { + skip.add(event.id) + } + }, + }), ) return async (limit: number) => { @@ -200,25 +199,23 @@ export class FeedController { } } - async _getIntersectionLoader(feeds: Feed[]) { + _getIntersectionLoader(feeds: Feed[]) { const exhausted = new Set() const counts = new Map() const events: TrustedEvent[] = [] const seen = new Set() - const controllers = await Promise.all( - feeds.map( - (thisFeed: Feed, i: number) => - new FeedController({ - ...this.options, - feed: thisFeed, - onExhausted: () => exhausted.add(i), - onEvent: (event: TrustedEvent) => { - events.push(event) - counts.set(event.id, inc(counts.get(event.id))) - }, - }), - ), + const controllers = feeds.map( + (thisFeed: Feed, i: number) => + new FeedController({ + ...this.options, + feed: thisFeed, + onExhausted: () => exhausted.add(i), + onEvent: (event: TrustedEvent) => { + events.push(event) + counts.set(event.id, inc(counts.get(event.id))) + }, + }), ) return async (limit: number) => { @@ -245,25 +242,23 @@ export class FeedController { } } - async _getUnionLoader(feeds: Feed[]) { + _getUnionLoader(feeds: Feed[]) { const exhausted = new Set() const seen = new Set() - const controllers = await Promise.all( - feeds.map( - (thisFeed: Feed, i: number) => - new FeedController({ - ...this.options, - feed: thisFeed, - onExhausted: () => exhausted.add(i), - onEvent: (event: TrustedEvent) => { - if (!seen.has(event.id)) { - this.options.onEvent?.(event) - seen.add(event.id) - } - }, - }), - ), + const controllers = feeds.map( + (thisFeed: Feed, i: number) => + new FeedController({ + ...this.options, + feed: thisFeed, + onExhausted: () => exhausted.add(i), + onEvent: (event: TrustedEvent) => { + if (!seen.has(event.id)) { + this.options.onEvent?.(event) + seen.add(event.id) + } + }, + }), ) return async (limit: number) => { diff --git a/packages/feeds/src/display.ts b/packages/feeds/src/display.ts index 02e9e68..491c71d 100644 --- a/packages/feeds/src/display.ts +++ b/packages/feeds/src/display.ts @@ -1,4 +1,4 @@ -import {now, uniq, displayList} from "@welshman/lib" +import {now, uniq, displayList, formatTimestampAsDate, formatTimestampRelative} from "@welshman/lib" import { FeedType, Feed, @@ -42,13 +42,19 @@ export const displayCreatedAtFeed = (feed: CreatedAtFeed) => { const parts: string[] = [] if (since) { - const timestamp = relative.includes("since") ? now() - since : since - parts.push(`after ${new Date(timestamp * 1000).toLocaleString()}`) + if (relative.includes("since")) { + parts.push(`after ${formatTimestampRelative(now() - since)}`) + } else { + parts.push(`after ${formatTimestampAsDate(since)}`) + } } if (until) { - const timestamp = relative.includes("until") ? now() - until : until - parts.push(`before ${new Date(timestamp * 1000).toLocaleString()}`) + if (relative.includes("until")) { + parts.push(`before ${formatTimestampRelative(now() - until)}`) + } else { + parts.push(`before ${formatTimestampAsDate(until)}`) + } } if (parts.length > 0) { diff --git a/packages/feeds/src/utils.ts b/packages/feeds/src/utils.ts index 02a6380..2290691 100644 --- a/packages/feeds/src/utils.ts +++ b/packages/feeds/src/utils.ts @@ -206,3 +206,61 @@ export const walkFeed = (feed: Feed, visit: (feed: Feed) => void) => { } } } + +export const simplifyFeed = (feed: Feed): Feed => { + if (isUnionFeed(feed)) { + const args = getFeedArgs(feed) + + if (args.length === 1) return simplifyFeed(args[0]) + + const modified: Feed[] = [] + + for (let sub of args.map(simplifyFeed)) { + if (isUnionFeed(sub)) { + modified.push(...getFeedArgs(sub)) + } else { + modified.push(sub) + } + } + + return makeUnionFeed(...modified) + } + + if (isIntersectionFeed(feed)) { + const args = getFeedArgs(feed) + + if (args.length === 1) return simplifyFeed(args[0]) + + const modified: Feed[] = [] + + for (let sub of args.map(simplifyFeed)) { + if (isIntersectionFeed(sub)) { + modified.push(...getFeedArgs(sub)) + } else { + modified.push(sub) + } + } + + return makeIntersectionFeed(...modified) + } + + if (isDifferenceFeed(feed)) { + const args = getFeedArgs(feed) + + if (args.length === 1) return simplifyFeed(args[0]) + + const modified: Feed[] = [] + + for (let sub of args.map(simplifyFeed)) { + if (isDifferenceFeed(sub)) { + modified.push(...getFeedArgs(sub)) + } else { + modified.push(sub) + } + } + + return makeDifferenceFeed(...modified) + } + + return feed +}