UNPKG

ng-time-past-pipe

Version:

Reactive textual representation of the time that has been passed between a given date and now in your Angular App.

351 lines (342 loc) 12.3 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, InjectFlags, Pipe, Inject, Injectable } from '@angular/core'; import * as i1 from 'rxjs'; import { interval } from 'rxjs'; import { filter, map } from 'rxjs/operators'; /** * Custom `TimeDiffGenerator` Injection Token * * @public * @api */ const CUSTOM_TIME_DIFF_GENERATOR = new InjectionToken('Custom Time Diff Generator'); /** * Return a respective textual representation of the input, as the input is a timespan that has been passed. * * @param diff The time diff object * @public * @api */ const defaultTimeDiffGenerator = (diff) => { if (diff.seconds === 0) { return 'about now'; } return diff.isFuture ? getFutureDiffString(diff) : getPastDiffString(diff); }; const getPastDiffString = (diff) => { const { seconds, minutes, hours, months, days, years } = diff; if (seconds <= 5) { return 'a few seconds ago'; } else if (seconds <= 59) { return seconds + ' seconds ago'; } else if (seconds <= 90) { return 'about a minute ago'; } if (minutes <= 45) { return minutes + ' minutes ago'; } else if (minutes <= 90) { return 'one hour ago'; } if (hours <= 22) { return hours + ' hours ago'; } else if (hours <= 36) { return 'a day ago'; } if (days <= 25) { return days + ' days ago'; } else if (days <= 45) { return 'a month ago'; } if (days <= 345) { return months + ' months ago'; } else if (days <= 545) { return 'a year ago'; } return years + ' years ago'; }; const getFutureDiffString = (diff) => { const { seconds, minutes, hours, months, days, years } = diff; if (seconds <= 59) { return 'in ' + seconds + ' seconds'; } if (seconds <= 90) { return 'in one minute'; } else if (minutes <= 59) { return 'in ' + minutes + ' minutes'; } if (minutes <= 90) { return 'in one hour'; } else if (hours <= 22) { return 'in ' + hours + ' hours'; } if (hours <= 36) { return 'in one day'; } else if (days <= 25) { return 'in ' + days + ' days'; } if (days <= 45) { return 'in one month'; } else if (days <= 345) { return 'in ' + months + ' months'; } if (days <= 545) { return 'in one year'; } return 'in ' + years + ' years'; }; /** * Provides the TimeDiffGenerator preferring a custom provider for internal usage * * @internal */ const TIME_DIFF_GENERATOR = new InjectionToken('Time Diff Generator', { factory: () => { const customGenerator = inject(CUSTOM_TIME_DIFF_GENERATOR, InjectFlags.Optional); return customGenerator !== null && customGenerator !== void 0 ? customGenerator : defaultTimeDiffGenerator; }, }); /** * TimeDiff Factory * * @param seconds The time difference in seconds. Negative values are considered as a future event * @internal */ const createTimeDiff = (seconds) => { const isFuture = seconds < 0; if (isFuture) { seconds = Math.abs(seconds); } const diff = { seconds, isFuture }; diff.minutes = Math.round(seconds / 60); diff.hours = Math.round(diff.minutes / 60); diff.days = Math.round(diff.hours / 24); diff.months = Math.round(diff.days / 30.416); diff.years = Math.round(diff.days / 365); return diff; }; /** * Custom `UpdateIntervalGenerator` Injection Token * * @public * @api */ const CUSTOM_UPDATE_INTERVAL_GENERATOR = new InjectionToken('Custom Update Interval Generator'); /** * Determinate the point of time on when the output should be checked for a update * * @param diff The time diff object * @return A point of time in future in seconds * @public * @api */ const defaultUpdateIntervalGenerator = (diff) => { if (diff.seconds < 60) { // less than 1 min, update every second return 1; } else if (diff.seconds < 3600) { // less than an hour, update every 30 secs return 30; } else if (diff.seconds < 86400) { // less than a day, update every 5 min return 300; } // update every hour return 3600; }; /** * Provides the `UpdateIntervalGenerator` preferring a custom provider for internal usage * * @internal */ const UPDATE_INTERVAL_GENERATOR = new InjectionToken('Update Interval Generator', { factory: () => { const customGenerator = inject(CUSTOM_UPDATE_INTERVAL_GENERATOR, InjectFlags.Optional); return customGenerator !== null && customGenerator !== void 0 ? customGenerator : defaultUpdateIntervalGenerator; }, }); /** * Optimistic parse a given input to seconds that past between it and now * * @param value A value of type string, number or date * @return The time past in seconds between now and input value * @internal */ const parseInputValue = (value) => { let dateValueTime; if (typeof value === 'number') { if (value <= 0) { // Negative number will always be handled as seconds in the future return value; } const length = Math.ceil(Math.log10(value + 1)); if (length < 10 && length > 0) { return value; // Guessing the input is already the passed seconds } if (length === 10) { value *= 1000; } // Guessing UnixTimestamp dateValueTime = value; // All other lengths are considered intentional and therefore processed } else { // Use Date constructor to determine the microseconds dateValueTime = (value instanceof Date ? value : new Date(value)).getTime(); } return Math.floor((Date.now() - dateValueTime) / 1000); }; /** * Strict TAInput Type Validator * * @param value The optimistic input value to validate * @internal */ const validateTAInputType = (value) => { return (typeof value === 'number' || typeof value === 'string' || value instanceof Date); }; const TIME_PAST_TICKER = new InjectionToken('TimePastTimer', { factory: () => interval(1000), providedIn: 'root' }); class TimePastPipe { /** * TimePastPipe Class Constructor */ constructor(changeDetectorRef, ticker, timeDiffGenerator, updateIntervalGenerator) { this.changeDetectorRef = changeDetectorRef; this.ticker = ticker; this.timeDiffGenerator = timeDiffGenerator; this.updateIntervalGenerator = updateIntervalGenerator; this.currentPeriod = 1; this.intervalTimer = this.ticker.pipe(filter((tick) => tick % this.currentPeriod === 0), map((tick) => tick / this.currentPeriod)); this.intervalSubscription = this.intervalTimer.subscribe(() => { this.changeDetectorRef.markForCheck(); }); } /** * Transform anything that can be parsed to a Date in the past, to a string that represent the relative * time that has been passed between now and this point of time. * * @param value A value that can be parsed to a Date in the past or future * @param overflow Overflow to time in past when initial date was in future * @return The textual representation of the time that has been passed between the given Date * and the current. */ transform(value, overflow = true) { if (this.isValidInput(value) === false) { return value; } const seconds = parseInputValue(value); this.initialSeconds || (this.initialSeconds = seconds); if (this.lastSeconds === seconds || (overflow === false && this.initialSeconds < 0 && seconds > 0)) { return this.lastResult; } // The ChangeDetector should not call transform again while the new value is being resolved this.changeDetectorRef.detach(); this.lastSeconds = seconds; const timeDiff = createTimeDiff(seconds); const result = (this.lastResult = this.timeDiffGenerator(timeDiff)); // Make sure the update interval refreshed as well this.currentPeriod = this.updateIntervalGenerator(timeDiff); // Reattach the ChangeDetector so that further changes are being transformed this.changeDetectorRef.reattach(); return result; } /** * Validate the Input Value and log a warning per value when it fails * * @param value * @private */ isValidInput(value) { const validationResult = validateTAInputType(value); if (validationResult === false && this.lastInput !== value) { console.warn(`[TimePastPipe] Invalid Input of type ${typeof value} (${value}).`); } this.lastInput = value; return validationResult; } /** * Clear interval ticker subscription */ ngOnDestroy() { if (this.intervalSubscription) { this.intervalSubscription.unsubscribe(); } } } TimePastPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: TimePastPipe, deps: [{ token: i0.ChangeDetectorRef }, { token: TIME_PAST_TICKER }, { token: TIME_DIFF_GENERATOR }, { token: UPDATE_INTERVAL_GENERATOR }], target: i0.ɵɵFactoryTarget.Pipe }); TimePastPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.0.3", ngImport: i0, type: TimePastPipe, isStandalone: true, name: "timePast", pure: false }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: TimePastPipe, decorators: [{ type: Pipe, args: [{ standalone: true, name: 'timePast', pure: false, }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i1.Observable, decorators: [{ type: Inject, args: [TIME_PAST_TICKER] }] }, { type: undefined, decorators: [{ type: Inject, args: [TIME_DIFF_GENERATOR] }] }, { type: undefined, decorators: [{ type: Inject, args: [UPDATE_INTERVAL_GENERATOR] }] }]; } }); /** * @deprecated Use TimePastPipe instead */ const NgTimePastPipePipe = TimePastPipe; /** * Public TimePast Service Class * * @public * @api */ class TimePastService { constructor(timeDiffGenerator) { this.timeDiffGenerator = timeDiffGenerator; } /** * Transform anything that can be parsed to a Date in the past, to a string that represent the relative * time that has been passed between now and this point of time. * * @param value A value that can be parsed to a Date in the past * @return The textual representation of the time that has been passed between the given Date * and the current. */ timePast(value) { if (validateTAInputType(value) === false) { return undefined; } const seconds = parseInputValue(value); const timeDiff = createTimeDiff(seconds); return this.timeDiffGenerator(timeDiff); } } TimePastService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: TimePastService, deps: [{ token: TIME_DIFF_GENERATOR }], target: i0.ɵɵFactoryTarget.Injectable }); TimePastService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: TimePastService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: TimePastService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [TIME_DIFF_GENERATOR] }] }]; } }); /** * Generated bundle index. Do not edit. */ export { CUSTOM_TIME_DIFF_GENERATOR, CUSTOM_UPDATE_INTERVAL_GENERATOR, NgTimePastPipePipe, TimePastPipe, TimePastService, defaultTimeDiffGenerator, defaultUpdateIntervalGenerator }; //# sourceMappingURL=ng-time-past-pipe.mjs.map