adopted-ember-addons / ember-moment

MIT License
399 stars 122 forks source link

Dynamic/automatic interval for live updating #261

Closed Techn1x closed 2 years ago

Techn1x commented 7 years ago

I've been using the moment-from-now helper a lot, and on one of my pages I use it many times with varying times supplied, like so;

{{moment-from-now reportedTime interval=1000}}

This is useful because they will display a few seconds ago then change value accordingly as the supplied times get older. However, once the times get older, it's far less critical to have the interval so precise (for example, if a time is 5 days ago there is no need to update it every second, and having many of these on the same page is a bit of a drag on performance)

For this reason, I implemented the following helper in my app

{{moment-from-now-dynamic reportedTime}}
// app/helpers/moment-from-now-dynamic.js
// Get a live updating 'moment-from-now' with an interval that changes based on how old the given time is
import Ember from 'ember';
import BaseHelper from 'ember-moment/helpers/-base';
import computeFn from 'ember-moment/utils/helper-compute';

export default BaseHelper.extend({
  compute: computeFn(function(params, { hideSuffix, locale, timeZone }) {
    let interval = 1000;

    const moment = Ember.get(this, 'moment');
    // Morph moment is from the base helper
    const morphedMoment = this.morphMoment(moment.moment(...params), { locale, timeZone });
    const nowMorphedMoment = this.morphMoment(moment.moment(), { locale, timeZone });

    // Set the interval based on the difference in times
    let timeDiff = nowMorphedMoment.diff(morphedMoment, 'seconds');
    if(timeDiff < 60) {
      // Diff is less than 1 minute, set 1 second intervals
      interval = 1000;  // Milliseconds
    } else if(timeDiff < 3600) {
      // Diff is less than 1 hour, set 30 second intervals
      interval = 30000;
    } else if(timeDiff < 86400) {
      // Diff is less than 1 day, set 30 minute intervals
      interval = 1800000;
    } else {
      // Diff is greater than 1 day, set 1 hour intervals
      interval = 3600000;
    }

    // Uses the interval & sets timeout, from the base helper
    this._super(...params,{ interval, hideSuffix, locale, timeZone });  

    return morphedMoment.fromNow(hideSuffix);
  })
});

This was my quick and dirty approach, but would it be best if this logic were built into the moment-from-now helper in ember-moment, with something like interval=auto? Or is this something that should be implemented on a case-by-case basis like I have done

jasonmit commented 7 years ago

@Techn1x I like this. Definitely agree with the general idea just unsure about the implementation right now. Perhaps we can look to other projects tackling this for inspiration on how they "back off" on counting within certain windows of time.

Techn1x commented 6 years ago

When we add it, it would be good if we somehow make the backoff logic/values easy to override - that way if the way we implement it isn't perfect, the code can still be used by most but tweaked as needed.

jasonmit commented 6 years ago

@Techn1x I like that idea... allow a strategy to be passed in as a prop or defined somewhere in the app (service?).

Techn1x commented 6 years ago

Had another quick think about this... the best, most accurate backoff strategy that would probably work for everyone would be if we somehow had a list of the defined intervals that fromNow uses, and adjust the update interval to always be when the next change is due to occur

Assuming the following 'fromNow' intervals;

a few seconds ago ( when 0 < time < 60s)
a minute ago (when 60s < time < 2min)
2 minutes ago (etc...)
3 minutes ago
4 minutes ago
...
1 hour ago
2 hours ago
3 hours ago
...
1 day ago
2 days ago
...

etc.

If a time was received that was 3 minutes 12 seconds ago, the next fromNow interval is at the '4 minutes ago' mark, so the next update interval to use should be set to exactly (4 minutes - 3min12sec) == 48 seconds. Once that is reached, the helper is recomputed and the new update interval would be calculated as 5 minutes - 4 minutes == 60 seconds

So the algorithm would be something like nextfromnowinterval - receivedTime = next update interval to use

Following this approach the moment-from-now would only ever update at the exact moment it needs to change

Techn1x commented 6 years ago

This will help to retrieve the existing humanized intervals https://github.com/moment/momentjs.com/blob/master/docs/moment/07-customization/13-relative-time-threshold.md