Problem/Motivation

As it turned out in #2500525: Time ago/hence date/time formatting breaks caching; needs appropriate max age and #2686409: Time Ago summary does not render on Manage Display for Timestamp and Datetime fields the TimestampAgoFormatter formatter outputs a string that is very hard to cache. A time difference interval string changes very often making it uncacheable and then the whole page become uncacheable. The big problem here is that we return a string from the server that is refreshes very often. Using a max-age strategy works but it has the drawback of being very bad for the user experience.

Proposed resolution

  1. Use the default TimestampFormatter formatter to show also a time difference formatted dates and deprecate TimestampAgoFormatter EDIT: The deprecation is discussed in #2938705: Consider deprecating TimestampAgoFormatter.
  2. Add a new "Display as a time difference" option (as checkbox) in TimestampFormatter settings. When this setting is On, a Javascript snippet replaces the current output of the formatter, which is a human readable formatted date/time, with a Display as a time difference string expression. Also the "Display as a time difference" settings will be visible and configurable.
  3. The time difference replacement is formatted using a jQuery snippet that converts the actual PHP time difference functionality.

Pros of this solution:

  • Assures a dynamic time difference that can be refreshed by JS with an interval that should be configured in the formatter.
  • Is cacheable.
  • Degrades nice to a normal formatted date/time for non-JS browsers.

Additional improvements:

  • Use the <time ...> HTML5 element.
  • Add a title (tooltip) to <time ...> so that while displaying a time difference string, the user still can see the exact time by hovering with the mouse. The format of the tooltip should be configurable apart from the main date format because we may want a more detailed format than the main one.
  • Allow configuration of time difference string pattern as the actual TimeAgoFormatter does.

Remaining tasks

  • Add tests.
  • Add a post update script to fix configurations of existing entity view displays that are using the TimestampFormatter.
  • Add update path test.
  • Open a followup for the datetime.module corresponding formatter.

User interface changes

  • For site builders: New options in TimestampFormatter settings.
  • For users: "Time difference" dates will be up-to-date (no cached) and will refresh in the client browser on a specific interval.

API changes

Deprecated TimestampAgoFormatter formatter. EDIT: Moved to #2938705: Consider deprecating TimestampAgoFormatter.

Data model changes

New settings for TimestampFormatter formatter.

Manual testing

  • Create a Timestamp field
  • The formatter is set to "Default". Edit the formatter settings
  • Check the "Display as time difference"
  • Configure
  • Set the refresh interval to a lower value so that you can observe later, in node view, how the field is refreshing
  • Save the formatter settings.
  • Check the formatter settings summary.
  • Create a new node and save.
  • See that the field correctly shows the field value as time difference.
  • Hover with the mouse and check that a detailed time tooltip pops-up
  • Without reloading the page, check that the field is refreshing client side after the refresh interval expires.
  • Disable Javascript and reload the page.
  • Check that the field degrades to a normal time representation.

Formatter settings form


Formatter settings summary

Javascript shows the timestamp as time difference

The time difference refreshes after the refresh interval expires

Javascript disabled degradation

Release notes snippet

In order to show the date/time as time difference (e.g. '2 hours 23 minutes ago') the timestamp default formatter exposes a setting "Display as a time difference". When enabled, the date/time is showed as time difference using Javascript, so the page remains cacheable. A refresh interval can be configured so that the time difference is refreshed without any page reload.

CommentFileSizeAuthor
#241 2921810-241.conflict-1.diff.txt2.5 KBdww
#239 2921810-nr-bot.txt89 bytesneeds-review-queue-bot
#201 timediff_summary.png28.25 KBclaudiu.cristea
#201 tmediff_diff.png92.12 KBclaudiu.cristea
#201 timediff_normal.png232 KBclaudiu.cristea
#195 interdiff_193-195.txt988 bytesravi.shankar
#195 2921810-195.patch76.04 KBravi.shankar
#193 reroll_diff_2921810_190-193.txt5.57 KBankithashetty
#193 2921810-193.patch76.03 KBankithashetty
#190 2921810-190-9.3.x.patch75.98 KBseanb
#189 2921810-189-9.3.x.patch72.46 KBseanb
#184 2921810-184-9.1.x.patch72.42 KBseanb
#183 2921810-183-9.1.x.patch76.1 KBseanb
#182 2921810-182-9.1.x.patch76.05 KBseanb
#167 2921810-167.patch72.34 KBseanb
#164 2921810-164.patch73.95 KBseanb
#162 2921810-162.patch73.89 KBwilfred waltman
#159 time_diff-unchecked.png165.97 KBclaudiu.cristea
#157 date-time-old.png158.22 KBwebchick
#153 time_diff_no_js.png99.81 KBclaudiu.cristea
#153 time_diff2.png101.96 KBclaudiu.cristea
#153 time_diff1.png63 KBclaudiu.cristea
#153 time_diff_formatter_summary.png66.48 KBclaudiu.cristea
#153 time_diff_formatter_form.png131.19 KBclaudiu.cristea
#152 2921810-152.patch73.8 KBclaudiu.cristea
#152 2921810-152.interdiff.txt4.59 KBclaudiu.cristea
#149 2921810-149.patch69.91 KBclaudiu.cristea
#149 2921810-149.interdiff.txt697 bytesclaudiu.cristea
#148 2921810-148.patch69.92 KBclaudiu.cristea
#148 2921810-148.interdiff.txt4.49 KBclaudiu.cristea
#146 2921810-146.patch67.17 KBkostyashupenko
#142 2921810-142.patch67.34 KBclaudiu.cristea
#142 2921810-142.interdiff.txt2.87 KBclaudiu.cristea
#142 formatter-time-diff.png131.19 KBclaudiu.cristea
#138 2921810-2-138.patch67.04 KBalexpott
#137 2921810-133.patch67.03 KBjonathanshaw
#134 interdiff-2921810-133-134.txt1.73 KByogeshmpawar
#134 2921810-134.patch67.09 KByogeshmpawar
#133 interdiff-2921810-129-133.txt1.42 KByogeshmpawar
#133 2921810-133.patch67.03 KByogeshmpawar
#129 interdiff-2921810-127-129.txt2.24 KByogeshmpawar
#129 2921810-129.patch67.03 KByogeshmpawar
#127 interdiff-2921810-119-127.txt1.34 KByogeshmpawar
#127 2921810-127.patch67.09 KByogeshmpawar
#119 2921810-119.patch67.23 KBclaudiu.cristea
#119 2921810-119.interdiff.txt1.62 KBclaudiu.cristea
#108 2921810-108.patch67.32 KBclaudiu.cristea
#108 2921810-108.interdiff.txt1.45 KBclaudiu.cristea
#107 2921810-107.interdiff.txt794 bytesclaudiu.cristea
#107 2921810-107.patch67.35 KBclaudiu.cristea
#104 2921810-104.patch67.39 KBclaudiu.cristea
#104 2921810-104.interdiff.txt2.32 KBclaudiu.cristea
#103 2921810-103.interdiff.txt8.38 KBclaudiu.cristea
#103 2921810-103.patch67.39 KBclaudiu.cristea
#102 2921810-102.patch61.23 KBclaudiu.cristea
#102 2921810-102.interdiff.txt3.67 KBclaudiu.cristea
#101 2921810-101.patch61.36 KBwilfred waltman
#86 2921810-86.patch61.46 KBclaudiu.cristea
#86 2921810-86.interdiff.txt1.37 KBclaudiu.cristea
#84 2921810-84.interdiff.txt38.95 KBclaudiu.cristea
#84 2921810-84.patch61.46 KBclaudiu.cristea
#81 2921810-80.interdiff.txt2.23 KBclaudiu.cristea
#81 2921810-80.patch60.52 KBclaudiu.cristea
#78 2921810-78.patch60.12 KBclaudiu.cristea
#78 2921810-78.interdiff.txt9.96 KBclaudiu.cristea
#76 2921810-76.interdiff.txt1.38 KBclaudiu.cristea
#76 2921810-76.patch53.63 KBclaudiu.cristea
#75 2921810-75.interdiff.txt22.16 KBclaudiu.cristea
#75 2921810-75.patch53.58 KBclaudiu.cristea
#74 2921810-74.interdiff.txt5.66 KBclaudiu.cristea
#74 2921810-74.patch47.41 KBclaudiu.cristea
#68 2921810-68.interdiff.txt1.03 KBaskibinski
#68 2921810-68.patch47.57 KBaskibinski
#62 2921810-62.patch47.47 KBclaudiu.cristea
#62 2921810-62.interdiff.txt2.79 KBclaudiu.cristea
#61 2921810-61.interdiff.txt9.13 KBclaudiu.cristea
#61 2921810-61.patch44.69 KBclaudiu.cristea
#59 2921810-59.interdiff.txt9.01 KBclaudiu.cristea
#59 2921810-59.patch43.26 KBclaudiu.cristea
#57 2921810-57.interdiff.txt1.47 KBclaudiu.cristea
#57 2921810-57.patch40.07 KBclaudiu.cristea
#56 2921810-56.interdiff.txt12.27 KBclaudiu.cristea
#56 2921810-56.patch40.11 KBclaudiu.cristea
#52 interdiff.txt8.48 KBjoelpittet
#52 2921810-52.patch38.75 KBjoelpittet
#50 2921810-50.interdiff.txt7.06 KBclaudiu.cristea
#50 2921810-50.patch39.99 KBclaudiu.cristea
#41 2921810-41.interdiff.txt1.17 KBclaudiu.cristea
#41 2921810-41.patch38.39 KBclaudiu.cristea
#40 2921810-40.interdiff.txt1.71 KBclaudiu.cristea
#40 2921810-40.patch38.44 KBclaudiu.cristea
#30 2921810-30.interdiff.txt5.67 KBclaudiu.cristea
#30 2921810-30.patch40.79 KBclaudiu.cristea
#25 2921810-25.patch41.03 KBclaudiu.cristea
#25 2921810-25.interdiff.txt9.96 KBclaudiu.cristea
#24 2921810-24.interdiff.txt794 bytesclaudiu.cristea
#24 2921810-24.patch40.09 KBclaudiu.cristea
#23 2921810-23.interdiff.txt2.5 KBclaudiu.cristea
#23 2921810-23.patch40.14 KBclaudiu.cristea
#21 2921810-21.interdiff.txt3.47 KBclaudiu.cristea
#21 2921810-21.patch40.4 KBclaudiu.cristea
#19 2921810-19.interdiff.txt6.98 KBclaudiu.cristea
#19 2921810-19.patch39.53 KBclaudiu.cristea
#17 2921810-17.interdiff.txt1.12 KBclaudiu.cristea
#17 2921810-17.patch32.54 KBclaudiu.cristea
#16 2921810-16.patch32.56 KBclaudiu.cristea
#16 2921810-16.interdiff.txt576 bytesclaudiu.cristea
#13 2921810-13.interdiff.txt1.18 KBclaudiu.cristea
#13 2921810-13.patch32.72 KBclaudiu.cristea
#12 2921810-12.patch32.71 KBclaudiu.cristea
#12 2921810-12.interdiff.txt12.18 KBclaudiu.cristea
#10 2921810-10.patch21.17 KBclaudiu.cristea
#10 2921810-10.interdiff.txt1.27 KBclaudiu.cristea
#8 2921810-8.patch21.2 KBclaudiu.cristea
#8 2921810-8.interdiff.txt68.79 KBclaudiu.cristea
#2 2921810-2.patch62.12 KBclaudiu.cristea

Issue fork drupal-2921810

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

claudiu.cristea created an issue. See original summary.

claudiu.cristea’s picture

Here's a fist patch based on my experience in a project where I created a custom formatter, extending TimestampFormatter, for this purpose.

Unfortunately I don't have time to work on this. I just created this first approach just to open the party.

claudiu.cristea’s picture

Issue tags: +Needs change record

.

Status: Needs review » Needs work

The last submitted patch, 2: 2921810-2.patch, failed testing. View results

wim leers’s picture

Issue tags: +D8 cacheability
claudiu.cristea’s picture

Issue summary: View changes

.

droplet’s picture

moment.js is too heavy. Since the timezone doesn't matter, I suggested making a custom one and yeah, to sync with PHP

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Datetime%...

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new68.79 KB
new21.2 KB

Removing Moment.js as per #7.

Status: Needs review » Needs work

The last submitted patch, 8: 2921810-8.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new1.27 KB
new21.17 KB

Wrong schema.

Status: Needs review » Needs work

The last submitted patch, 10: 2921810-10.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
Issue tags: -Needs upgrade path, -Needs upgrade path tests
StatusFileSize
new12.18 KB
new32.71 KB

Added upgrade path & test for upgrade path. Now, I'm looking into the failures.

claudiu.cristea’s picture

StatusFileSize
new32.72 KB
new1.18 KB

Temporary disabling the deprecation.

The last submitted patch, 12: 2921810-12.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 13: 2921810-13.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new576 bytes
new32.56 KB
claudiu.cristea’s picture

StatusFileSize
new32.54 KB
new1.12 KB

The last submitted patch, 16: 2921810-16.patch, failed testing. View results

claudiu.cristea’s picture

StatusFileSize
new39.53 KB
new6.98 KB

The last submitted patch, 17: 2921810-17.patch, failed testing. View results

claudiu.cristea’s picture

Issue summary: View changes
Issue tags: -Needs change record
Related issues: +#2922451: [policy no patch] Make it possible to mark plugins as deprecated
StatusFileSize
new40.4 KB
new3.47 KB

Added CR. Updating IS. Fixing the deprecation of plugin. Note that we don't have right now a plugin deprecation policy/mechanism. This is handled in #2922451: [policy no patch] Make it possible to mark plugins as deprecated.

Status: Needs review » Needs work

The last submitted patch, 21: 2921810-21.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new40.14 KB
new2.5 KB

Seems that putting the deprecation error triggering in constructor, as it was suggested in #2922451-6: [policy no patch] Make it possible to mark plugins as deprecated, doesn't help too much. Moving into the main method.

claudiu.cristea’s picture

StatusFileSize
new40.09 KB
new794 bytes

Oops!

claudiu.cristea’s picture

StatusFileSize
new9.96 KB
new41.03 KB

The deprecation of the plugin should wait for #2922451: [policy no patch] Make it possible to mark plugins as deprecated. Added only a @todo for now.

The last submitted patch, 23: 2921810-23.patch, failed testing. View results

The last submitted patch, 24: 2921810-24.patch, failed testing. View results

mpdonadio’s picture

Can we pause this for a little bit to talk about the solution? Don't want to get too far down the road and then realize that we have problems.

I like the JS part, especially since it looks like it matches the current PHP behavior, and the time element part (see also #2793143: The datetime and daterange formatters (except plain) should use the HTML time element).

Few initial concerns.

Even if we deprecate the timeago formatter, I think we have a problem w/ non-JS uses. Besides non-JS browsers, formatters get used in other places (Views outputting something other than HTML, REST, etc). I don't think we can deprecate w/ plans to remove.

There would also be the issue of what to do with #2686409: Time Ago summary does not render on Manage Display for Timestamp and Datetime fields and the Datetime timeago formatter, which would have the same problem.

I'm wondering if instead of deprecating the timeago formatter and moving this functionality to the default formatter, we make this an option on the timeago formatter.

- Default behavior would be the current, with the cache quirk
- Optional JS-enhanced, cache friendly, version would be taken from this patch.

That would mean no deprecations, no non-JS problems, no update path, just an optional enhanced experience.

?

claudiu.cristea’s picture

@mpdonadio, I'm not insisting for deprecation but next question comes into my mind: Why would a consumer (for REST or non-HTML views) get outdated data like a max-age cached 'time ago' interval? That has no value for a service consumer as it has no value for a user that reads a HTML in the browser. Deprecated doesn't mean it will not continue to work. The formatter will be available for the entire life cycle of Drupal 8.x. However, deprecating it will tell the consumers to update their client software and compute themselves the interval. But I agree that keeping timestamp_ago can be an option.

For the datetime.module equivalent we should open a followup blocked on this as I think that we can extend from this one. Also included also other bug fixes here and changing to <time... > HTML5 as I need that for the JS part.

claudiu.cristea’s picture

StatusFileSize
new40.79 KB
new5.67 KB

This diff has been worked before #28 and is fixing usage of the formatter in Views where, there's now wrapper to store the settings. I'm holding the developement for a decision.

alexpott’s picture

@claudiu.cristea you can't deprecate and then still have usages. That's not deprecating. The constructor deprecation would work but, for example, views.view.user_admin_people is still using the plugin so of course the deprecation message is firing.

claudiu.cristea’s picture

@alexpott

you can't deprecate and then still have usages

You mean core usages? If yes, those should be replaced in this patch.

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
@@ -16,9 +16,17 @@
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use the
+ *   \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampFormatter formatter
+ *   instead and configure it with "Display as 'time ago'" option. For details,
+ *   see https://www.drupal.org/node/2926275.
+ *
+ * @todo Add the deprecation error triggering and proper mark the plugin as
+ *   deprecated once https://www.drupal.org/project/drupal/issues/2922451 is in.

Unless you remove all usages in core then this is not deprecated - it is just hopeful. Perhaps to keep the issue smaller we should deal with the deprecation in a separate issue. Since that issue will need to add the @trigger_error, remove all the usages and add a legacy test.

alexpott’s picture

You mean core usages? If yes, those should be replaced in this patch.

Well then if you are really replacing all the usages then you should be able to add the @trigger_error() to the constructor but the patch in #30 is not. For example there are a few views which are using it:

grep -R "type: timestamp_ago" ./core
./core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml:    type: timestamp_ago
./core/modules/comment/config/optional/views.view.comments_recent.yml:          type: timestamp_ago
./core/modules/node/config/optional/views.view.content_recent.yml:          type: timestamp_ago
./core/modules/user/config/optional/views.view.user_admin_people.yml:          type: timestamp_ago
./core/modules/user/config/optional/views.view.user_admin_people.yml:          type: timestamp_ago

And maybe other things too. This is the whole point of the @trigger_error() - to prove that the deprecation is real in core and also tell contrib / custom that in order to be ready for the future they need to change something too.

claudiu.cristea’s picture

@alexpott, Got it, agree. While this patch is already ~40KB, I agree that the deprecation should go in a followup. However, there's no agreement yet on scope of this issue, see #28 and #29. I will touch this patch as soon as there will be an agreement on the direction this goes.

wim leers’s picture

Impressive work!

Just a very high-level review. Definitely needs JS tests.

  1. +++ b/core/config/schema/core.data_types.schema.yml
    @@ -743,6 +743,16 @@ field.value.float:
    +field.value.timestamp:
    

    I think this can be done in a separate issue?

  2. +++ b/core/core.libraries.yml
    @@ -937,3 +937,12 @@ drupal.dialog.off_canvas:
    +    misc/timeago.js: {}
    

    This seems like it could be defer: true.

  3. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -94,6 +96,16 @@ public static function defaultSettings() {
    +        'future_format' => '@interval hence',
    

    😯 Woah, never seen this before!

  4. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +113,92 @@ public static function defaultSettings() {
    -    $elements = parent::settingsForm($form, $form_state);
    +    $form = parent::settingsForm($form, $form_state);
    ...
    -
    ...
    -    $elements['date_format'] = [
    +    $form['date_format'] = [
    

    Can you avoid renames and whitespace changes, to make this patch easier to review?

  5. +++ b/core/modules/system/templates/time.html.twig
    @@ -19,4 +19,6 @@
    +{% spaceless %}
     <time{{ attributes }}>{{ text }}</time>
    +{% endspaceless %}
    

    Can also be done in a separate issue.

claudiu.cristea’s picture

@Wim Leers, thank you. Unfortunately without #36.1 and #36.5 there will be test failures. If I create new issues, I'll have to block this ticket.

#36.4: OK with the white lines but the renames are mandatory because right now the names are buggy.

claudiu.cristea’s picture

Added the timestamp schema fix in a new issue, as @Wim Leers suggested in #36.1: #2931294: Timestamp field type misses schema for value.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

claudiu.cristea’s picture

Status: Needs work » Needs review
Related issues: +#2938705: Consider deprecating TimestampAgoFormatter
StatusFileSize
new38.44 KB
new1.71 KB

#2931294: Timestamp field type misses schema for value is in. Let's continue here.

Moved the deprecation of TimestampAgoFormatter in a followup #2938705: Consider deprecating TimestampAgoFormatter. @mpdonadio this allows us to unblock and move on with this ticket and postpone the decision whether to deprecate or not the TimestampAgoFormatter plugin. So let's push with this.

claudiu.cristea’s picture

StatusFileSize
new38.39 KB
new1.17 KB

Forgot to address #36.

The last submitted patch, 40: 2921810-40.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 41: 2921810-41.patch, failed testing. View results

joelpittet’s picture

@claudiu.cristea I don't mind helping with the test issues to avoid adding spaceless, ping me on IRC and I'll jump on that.

claudiu.cristea’s picture

Status: Needs work » Postponed

@joelpittet, thank you. I'll contact you on IRC.

So, the failure is caused by #2938799: Provide the timestamp scalar data type. Postponing on that.

claudiu.cristea’s picture

Status: Postponed » Needs review

#2938799: Provide the timestamp scalar data type has been committed, re-activating this.

claudiu.cristea’s picture

+++ b/core/misc/timeago.es6.js
@@ -0,0 +1,133 @@
+            output.push(Drupal.formatPlural(units, '1 months', '@count months'));

The singular is wrong: s/1 months/1 month. Fix it on the next iteration.

Status: Needs review » Needs work

The last submitted patch, 41: 2921810-41.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review

That was a random failure

claudiu.cristea’s picture

Issue summary: View changes
StatusFileSize
new39.99 KB
new7.06 KB

Added refresh interval. Fixed #41.

jonathanshaw’s picture

Exciting to see this coming along. Anything outstanding except #36.5 / #44?

joelpittet’s picture

StatusFileSize
new38.75 KB
new8.48 KB

Addressing my comment in #44. If possible we try to avoid making markup changes to stable/classy.

Spaceless can be a potenially slow(because of the preg_replace) compile change, so we've avoided it:
vendor/twig/twig/lib/Twig/Node/Spaceless.php:29

            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));\n")

We have a note here: https://www.drupal.org/node/1823416#whitespace in the caveat that suggest changing the test first. So that is what I did, by adding trim() around the tests that already had strip_tags() added.

And last, incase there is pushback, the alternative to spaceless is just add a twig comment after the tag. Like PHP, Twig will remove linebreaks after control statements and also comments.
Like this will work: <time{{ attributes }}>{{ text }}</time>{# This comment removes the linebreak after. #}

claudiu.cristea’s picture

+++ b/core/misc/timeago.es6.js
@@ -0,0 +1,141 @@
+            setInterval(showTimeAgo, timeagoSettings.refresh * 1000);

Well, this is tricky. The refresh interval should be "smart" and should adapt after the timeago string is displayed for the first time. Let's take this example: The refresh interval is set to 15 sec. When the displayed value is something like 1 minute 17 seconds everything is OK but after some time when the time is showed as 1 hour 24 minutes, refreshing every 15 seconds is a waste of CPU. As the time ago interval increases, the refresh interval should be adjusted to the minimum effort.

jonathanshaw’s picture

Yeah that is tricky. Perhaps the whole business of refreshing is best left to a follow-up.

The needed refresh interval depends on the date format, but we don't have easy way to understand that. A solution might be to try adding various time increments to the current time, 1 second, 1 minute, 1 hour, 1 day, 1 week, 1 month, 1 year. Then try to format these new times and see if they are different from the formatted current time. The first one to be different tells us what granularity we need to operate at.

The bigger pain is that the needed granularity changes. It might only be 1 second for the first 60 seconds, only 1 minute for the first hour etc. These possibilities can be handled in a similar way, but it's a bit complex and might need some javascript unit tests to ensure it's working.

droplet’s picture

+++ b/core/misc/timeago.es6.js
@@ -0,0 +1,141 @@
+  Drupal.behaviors.timestampAsTimeAgo = {
...
+          var showTimeAgo = function () {

We could export it as a global helper `Drupal.showTimeAgo` and `detach` to cancel the updates (e.g. when the element is removed from DOM).

I also preferred a recursive setTimeOut.

IMO, 5 ~ 10 mins updates is more acceptable.

+++ b/core/misc/timeago.es6.js
@@ -0,0 +1,141 @@
+          var timeagoSettings = JSON.parse(atob($(this).attr('data-drupal-timeago')));

why base64?

** Needs to fix the ES6 code style.

Thanks!

claudiu.cristea’s picture

StatusFileSize
new40.11 KB
new12.27 KB

Fixed #55.

Now the time is rendered (with JS) as:

<time
  datetime="2018-01-28T13:58:00+0200"
  title="Sunday, January 28, 2018 - 13:58"
  class="js-timeago datetime"
  data-drupal-timeago="{&quot;future_format&quot;:&quot;@interval hence&quot;,&quot;past_format&quot;:&quot;@interval ago&quot;,&quot;granularity&quot;:2,&quot;refresh&quot;:60}"
>44 minutes 59 seconds ago</time>

Still to be fixed:

  • Tests. This is not easy with time intervals because there's a latency between the moment the widget has been rendered and the moment you're making the assertion. Also the refreshing is hard to be tested. I see there is some Clock Mocking support in PHPUnit Bridge and that library have already integrated in core but I have no idea on how that could be used with our testing.
  • #53 is still not fixed. We have to come with some idea or just postpone the whole refresh thing. However, in order to address that, I've improved Drupal.dateFormatter.formatDiff() return, which now returns something like:
    {
      formatted: '2 days 1 hour 34 minutes',
      value: {
        day: 2,
        hour: 1,
        minute: 34,
      }
    }
    
claudiu.cristea’s picture

StatusFileSize
new40.07 KB
new1.47 KB

More es6 code style.

jonathanshaw’s picture

I had same idea to use setTimeout not setInterval.

Now instead of

            if (timeagoSettings.refresh > 0) {
              element.timer = setTimeout(Drupal.showTimeAgo, timeagoSettings.refresh * 1000, element);
            }

if we had something along the lines of

            if (timeagoSettings.refresh > 0) {
              refresh = nextTimeAgoRefresh(timeago.value, timeagoSettings.refresh, options);
              element.timer = setTimeout(Drupal.showTimeAgo, refresh, element);
            }

          var nextTimeAgoRefresh = function (current, interval, options) {
             // a function that returns the number of milliseconds until we should next refresh the timeAgo
             now = new Date();
             if (current.second) { return interval};
             if (current.minute) {
               remaining = (60 - now.getSeconds()); 
               return  min(remaining, interval) * 1000;
             };
             // etc for hours, days, weeks, years
          };

Could we set it up in such a way that unit test nextTimeAgoRefresh without having to worry about clock mocking?

claudiu.cristea’s picture

StatusFileSize
new43.26 KB
new9.01 KB

This is fixing the refresh issue. Have no idea how this can be tested, except manually.

droplet’s picture

Have no idea how this can be tested

It works I think:
(I have written in JS but should use PHPUnit instead. Or simply attach a JS test file)

// mock function only, have to use the right PHPUnit function in our REAL code.
function assertHTML() {
  return updated
 }
 
 var timerA;
 
 function countDown() {
   timerA = setTimeout(() => {
     console.log('working')
     countDown(); // #1
   }, 1000);
 }
 countDown();
 
 var updated;
 var timerB = timerA;
 console.log(`Initial: ${timerB === timerA}`);
 intialHTML = assertHTML() // from PHPUNIT function.
 
 console.log(`Immedate assert (FALSE): ${timerB !== timerA}`); // In PHPUNIT, we need not this step to validate the data. Only JS has blocking single-thread)

 // PHPUNIT: waitForElement / wait (for JS conditional)
 var setIntervalA = setInterval(function name(params) {
   const condition = timerB !== timerA;
   if(condition) {
    console.log(`Update 1: ${condition}`);
    timerB = timerA; // waiting for another changes
    updated = true; // at least one update after intial
    clearInterval(setIntervalA) // PHPUNIT will stop automatically
   }
 }, 1000)
 

  // PHPUNIT: waitForElement / wait (for JS conditional)
  var setIntervalB = setInterval(function name(params) {
    updatedHTML = assertHTML()    
    const condition = timerB !== timerA && updated && updatedHTML !== intialHTML;
    if(condition) {
      console.log(`Update 2: ${condition}`);
     clearInterval(setIntervalB)
    }
  }, 1000)


If you take #1 away (broken code), "Update 2" will be failed.

claudiu.cristea’s picture

StatusFileSize
new44.69 KB
new9.13 KB

Fixing docs.

@droplet, sorry I don't understand #60 :)

claudiu.cristea’s picture

Status: Needs review » Postponed
Issue tags: -Needs tests
Related issues: +#2775653: JavascriptTests with webDriver
StatusFileSize
new2.79 KB
new47.47 KB

This is the first attempt to add a JS test. Unfortunately we have to block this on #2775653: JavascriptTests with webDriver because it seems that PhantomJS doesn't work with setTimeout() & Co. At least it didn't work on my local machine but it worked nice with Selenium.

droplet’s picture

I may miss some point here. Why 5 sec but not 3 or 4?

If we expecting that counting up from 1, 2, 3, 4, 5. Bingo! Then, I think the test failure rate will be high.

#60 is trying to catch 2 continue changes. Now, I think we able to simplify it by set 2 assertJsCondition to assign to the value. If both values are greater than previous, `Drupal.showTimeAgo` is working

joelpittet’s picture

Status: Postponed » Needs review

#2775653: JavascriptTests with webDriver is committed, unpostponing.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

seanb’s picture

This patch seems to work fine! I think we need some more test coverage though. Here are some ideas.

+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeagoTest.php
@@ -0,0 +1,89 @@
+  public function testTimestampFormatterWithTimeago() {
...
+    $this->assertJsCondition("jQuery('time:contains(\"5 seconds\")').length > 0");

Since the interval is 1 sec asserting the exact value doesn't seem to be a stable option. Maybe we could improve the asserts a bit:

  • Assert the element has the right classes and attributes
  • Assert the '.js-timeago' element contains the word seconds.
  • Create some more content with the created dates in the past and assert for days/weeks/years etc.
  • Assert for other granularities (why not just all of them) and check the '.js-timeago' element contains the words weeks, months, years etc.
  • Find a way to assert the refresh (this might be the hardest one)
  • Assert the future / past formats
askibinski’s picture

Status: Needs review » Needs work

We are using the (locked) core html_datetime pattern for creating a RFC 3339 compliant datetime, which breaks the JS in Safari on iOS/OSX because Safari expects a valid RFC3339 datetime.

+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
@@ -166,26 +266,97 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
+        '#attributes' => [
+          // The representation of the date/time as RFC 3339 "date-time".
+          // @see https://www.w3.org/TR/2011/WD-html-markup-20110405/time.html
+          'datetime' => $this->dateFormatter->format($item->value, 'html_datetime', '', $timezone),

Core uses the pattern 'Y-m-d\TH:i:sO' which is not valid RFC3339. It results in (for example) "2018-08-13T16:17:33+0200".
The RFC3339 standard describes the format with a required colon between the offset timezone hours:

"2018-08-13T16:17:33+02:00"

The PHP DateTime class also states that the pattern should be "Y-m-d\TH:i:sP" (using the "P" format character instead of "O", see reference).

At first I thought this might be a core issue but I think core is assuming ISO8601 for html_datetime. So instead I would suggest using a custom format 'Y-m-d\TH:i:sP' which seems to work with all browsers.

askibinski’s picture

StatusFileSize
new47.57 KB
new1.03 KB

Here is a new patch addressing the Safari issue explained #67.
Additional tests as mentioned in #66 by Sean still have to be written.

claudiu.cristea’s picture

@askibinski, thank you. One question: Have you tested your patch in all major browsers?

Also, it would be nice to expand the comment just above the changed line to explain why we're not using the core html_datetime (the explanation from #67, but more compacted, would be great).

claudiu.cristea’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 68: 2921810-68.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

+++ b/core/misc/timeago.es6.js
@@ -0,0 +1,238 @@
+            // duration if the lowest unit of "time ago".

s/if/of

claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea

Assigning for fixing the failures and trying to improve the testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new47.41 KB
new5.66 KB
  • Fixed the test failures.
  • Fixed PHPCS issues.
  • Using the \DateTime::RFC3339 constant instead of a hardcoded format. Improved the docs.
  • Fixed a typo.

Looking now on how to improve the testing.

claudiu.cristea’s picture

Issue tags: +Needs change record
StatusFileSize
new53.58 KB
new22.16 KB

OK, finally I managed to do a unit testing for the JS functions. I had to move a function outside but I'm sure it was worthy. I know that we don't have a unified way to unit test javascript but in this case, due to the implemented tests, I managed to catch few bugs. I'm still trying to find a way to test the UI, where now we have only an assertion.

Also this needs a change record.

claudiu.cristea’s picture

StatusFileSize
new53.63 KB
new1.38 KB

PHPCs & small docs fixes.

claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned

You're welcome to review :)

claudiu.cristea’s picture

StatusFileSize
new9.96 KB
new60.12 KB

This formatter is used also in Views, so we need tests. Added tests for views and improved the entity view page test as it has been suggested in #63.

Status: Needs review » Needs work

The last submitted patch, 78: 2921810-78.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record

Fixed the test. The failure was because of this JS assertion that failed: '10 seconds' > '9 seconds'. Now I'm extracting the numeric part and making the comparison as numeric values. Safer.

I've created also the change notice https://www.drupal.org/node/2993639

claudiu.cristea’s picture

StatusFileSize
new60.52 KB
new2.23 KB

Ouch, the patch :)

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +
    +    $form['timeago']['refresh'] = [
    +      '#type' => 'select',
    +      '#title' => $this->t('Refresh interval (seconds)'),
    +      '#description' => $this->t("The interval to refresh the displayed 'time ago'."),
    +      '#default_value' => $timeago['refresh'],
    +      '#options' => $this->getRefreshIntervals(),
    +      '#states' => $states,
    +    ];
    

    I do have one conceptually question. I get how nice it is to refresh this, but I would have expected from reading the first bit of the issue summary, that this is something which could be moved into its own dedicated issue

  2. +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +          Drupal.showTimeAgo = timeElement => {
    +            const timestamp = new Date(
    +              $(timeElement).attr('datetime'),
    

    You create this function dynamically once at least for every ```.each``` function. This could be moved to a more top level function right? This way it would need to be reassigned all the time.

    On top of that the code would be a bit easier to understand

  3. +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +   * There are cases when the configured refresh timeout performs the refresh
    +   * even when it is not needed. This function optimizes the configured refresh
    +   * timeout to higher values, if the structure of the timeago interval doesn't
    +   * require refreshing too often.
    

    It would be super nice to give some examples in the summary of this function. I think this would massively make it easier to understand what is going on

  4. +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +  Drupal.dateFormatter.formatDiff = (from, to, options) => {
    ...
    +    options = $.extend({ granularity: 2, strict: true }, options);
    

    I would have expected that core JS could use default values on options already

  5. +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +    $.each(Drupal.dateFormatter.intervals, (interval, duration) => {
    

    ❓Is there a reason we don't use ```Array.prototype.forEach```?

jonathanshaw’s picture

Awesome work @claudiu.cristea. Most of these are nits or UX wordings, all are tentative suggestions.

  1. +++ b/core/config/schema/core.entity.schema.yml
    @@ -331,6 +331,35 @@ field.formatter.settings.timestamp:
    +      label: 'Options to show as timeago'
    

    Possibly 'Time difference'. The 'Options' seems unnecessary in the label and not consistent with tooltip.

  2. +++ b/core/config/schema/core.entity.schema.yml
    @@ -331,6 +331,35 @@ field.formatter.settings.timestamp:
    +          label: 'Granularity'
    

    Maybe 'Granularity of format' or 'Time units'?

  3. +++ b/core/core.libraries.yml
    @@ -942,3 +942,13 @@ drupal.dialog.off_canvas:
    +drupal.timeago:
    +  version: VERSION
    +  js:
    +    misc/timeago.js:
    

    Should the addition of this behavior and library be mentioned in the change record?

  4. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    -    $elements['date_format'] = [
    +    $form['date_format'] = [
    

    What is date_format used for if using timeago? Should we be hiding it?

  5. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +    $tooltip = $this->getSetting('tooltip');
    +    $form['tooltip']['#tree'] = TRUE;
    +    $form['tooltip']['date_format'] = [
    

    It seems strange having this above the timeago in the form; I think it should be below?

  6. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#title' => $this->t("Display as 'time ago'"),
    +      '#description' => $this->t("If checked, the timestamp will be displayed as a 'time ago' string, in javascript enabled browsers. With javascript disabled, the main behavior is in place."),
    

    This is problematic as help text because 'time ago' is not a phrase a normal person understands, and ago is specifically past. I'm also not sure we need to explain the no-JS fallback, and 'If checked' is a bit clumsy.

    I suggest:
    'Display as a time difference' and
    "Show the difference in time between the timestamp and the current time, e.g. '6 months ago'."

    Edit: I see now why we're mentioning JS/non-JS, it helps explain what the other time format settings are doing. I'm not sure what to propose, but overall the settings form is a little puzzling to the novice as it stands.

  7. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
    

    We should make our language consistent with the labels above. Maybe 'where you want the formatted time difference to appear'?

  8. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#type' => 'number',
    

    Would this be better as a select element, as it only has 7 possible values?

  9. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#title' => $this->t('Granularity'),
    

    Maybe 'Time units' would help the layman more

  10. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#description' => $this->t('How many time interval units should be shown in the formatted output.'),
    

    This could use more explanation. Maybe 'How many time units will be used in formatting the time difference. For example, if '1' is selected then the displayed time difference will only contain a single time unit such as '2 years' or '5 minutes' never '2 years 3 months' or '5 minutes 8 seconds'.

  11. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +    $form['timeago']['refresh'] = [
    
    @@ -166,26 +266,97 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
    +        $elements[$delta]['#attributes']['data-drupal-timeago'] = Json::encode($settings);
    
    +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +   *
    
    +++ b/core/misc/timeago.js
    @@ -0,0 +1,143 @@
    +          var timeagoSettings = JSON.parse($(timeElement).attr('data-drupal-timeago'));
    

    Now that we have intelligent refreshing that dynamically adapts to the time interval, I'm not clear of the point of this setting.

    In core we usually like to keep things simple and extensible rather than cater for every possibility.

    A 60 second minimum refresh interval would seem to be safe for almost any browser even with a huge amount of fields on screen, and adequate for 99% of use cases.

    It would be trivial for someone to extend this formatter and specify a faster refresh using the data-drupal-timeago attribute.

  12. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#title' => $this->t('Refresh interval (seconds)'),
    

    It's confusing labelling this as seconds and then having '1 minute' as the default option.

  13. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,101 @@ public static function defaultSettings() {
    +      '#description' => $this->t("The interval to refresh the displayed 'time ago'."),
    

    Perhaps 'How often (in seconds) to refresh the displayed time difference.

  14. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -147,13 +219,41 @@ public function settingsSummary() {
    +    $summary[] = $this->t('Tooltip date format: @date_format', ['@date_format' => $tooltip['date_format']]);
    ...
    +      $summary[] = $this->t("Displayed as 'time ago'");
    ...
    +      $summary[] = $this->t('Past date: %display', ['%display' => $display]);
    ...
    +        $summary[] = $this->t('Refresh in @interval', ['@interval' => $refresh_intervals[$timeago['refresh']]]);
    

    We're adding a lot of options here. Do we add everything to the summary or does that get too big at some point?

    Also better 'Displayed as a time difference'.

    Better 'Refresh every @interval'. And this doesn't handle the 'No refresh' option very well.

  15. +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +   * There are cases when the configured refresh timeout performs the refresh
    +   * even when it is not needed. This function optimizes the configured refresh
    ...
    +   *   The configured refresh timeout interval in seconds.
    ...
    +      // interval duration is bigger than the configured refresh, use the
    

    It shouldn't say configured as the value might not come from configuration in all use cases.

  16. +++ b/core/misc/timeago.es6.js
    @@ -0,0 +1,278 @@
    +    if (lastInterval !== 'second') {
    

    Does this respect really high refresh timeouts like 1000 seconds? Looks like it may bypass them and update times like "3 minutes" every minute.

  17. As I understand it, the CR should be focused on what's changed. This might be clearer if it emphasised more what has been added to the default formatter, not the comparison with the timestamp_ago formatter. I'm happy to edit for grammar once the content is settled.
claudiu.cristea’s picture

Issue summary: View changes
StatusFileSize
new61.46 KB
new38.95 KB

@dawehner, @jonathanshaw, than you for your review:

#82.1: It was a discussion at some point but never a decision to postpone the refresh to a followup. And now the work is there and tested.

#82.2: Agree. Fixed.

#82.3: Below, inline with the code, I gave some examples but, right, let's add them also in the function docblock.

#82.4: Well... the reason is that I wanted to follow as much as I can the PHP version of this function \Drupal\Core\Datetime\DateFormatter::formatDiff(). Also, I want to keep is as a generic function for JS.

#82.5: I wasn't able to use Array.prototype.forEach(). Probably because we're iterating over an object, not over an array? I have no idea.

#831-3, 6-10, 12-15: Yes, that's a better wording. I review changed also the code/settings names to be consistent with the UI.

#83.14:

Better 'Refresh every @interval'. And this doesn't handle the 'No refresh' option very well.

If 'No refresh', nothing is displayed, see the enclosing condition.

#83.16: I added a new test case for that and it seems that works correctly :)

#83.17: Changed the CR but still need to refer to the other formatter. That was the reason for this issue.

Updated the IS

jonathanshaw’s picture

Changing the machine & variable names to 'diff' not 'ago' seems like a good decision/

You didn't reply to #83.11 where I suggest removing the refresh setting from the formatter UI & config.

Some nits:

  1. +++ b/core/misc/time-diff.es6.js
    @@ -1,83 +1,83 @@
    +   * Replaces a timestamp, formatted as a date/time, with a time difference.
    

    Is this comment quite accurate? The code doesn't seem to know or care how the time is currently formatted.

    Should it be something more like 'Fills all time elements that have the data-drupal-timeago attribute with a refreshing time difference'?

  2. +++ b/core/misc/time-diff.es6.js
    @@ -95,57 +95,66 @@
    +   *   The configured refresh interval in seconds.
    ...
    +      // lowest unit duration is bigger than the configured refresh, use the
    

    'refresh interval' not 'configured refresh'

  3. +++ b/core/misc/time-diff.es6.js
    @@ -95,57 +95,66 @@
        * require refreshing too often.
    

    Should be 'refreshing more often'.

claudiu.cristea’s picture

Title: Allow TimestampFormatter to show as a fully cacheable 'time ago' with JS » Allow TimestampFormatter to show as a fully cacheable time difference with JS
StatusFileSize
new1.37 KB
new61.46 KB

@jonathanshaw, thank you

You didn't reply to #83.11 where I suggest removing the refresh setting from the formatter UI & config.

True, I just forgot that. Here's my point: I don't think that we should hardcode the value to 60 sec. Even, probably, 60 sec would satisfy most of the cases I clearly see use cases for having different refresh intervals. I can imagine, for instance, an article that announces the opening of a store and on the page they are showing a dynamic time difference "We're opening in 2 days 2 hours 39 minutes 15 seconds" with the refresh interval set to 1 sec so that the visitor sees a dynamic countdown while he's visiting the page. And I can imagine a lot of other use-cases.

However, if for some UX reasons the settings form is looking too cluttered, I would still keep the setting but removed from the UI. Then, the value could be changed via config management, without needing to extend the class just for such a minor change. But I would not do that either.

#85.1: Agree but I had to find a more compact phrase in order to honour the coding standards.

#85.2,3: True. Fixed.

jonathanshaw’s picture

#86 thanks for the reply - I think it's good to have it spelled out like this so others can decide.

I've worked on the CR, please check to see you think it's better and still accurate.

claudiu.cristea’s picture

@jonathanshaw,

I've worked on the CR, please check to see you think it's better and still accurate.

Wow, yes! It's a huge improvement and everything is now more clear. I'm sorry for the long list of wording and grammar issue, I'm not a native English speaker.

jonathanshaw’s picture

I created #2993811: Use the time-diff library in the datetime module's formatters as the needed follow-up. Looks like we need @mpdonadio to weigh in now.

claudiu.cristea’s picture

Assigned: Unassigned » mpdonadio

Let's assign, then.

@mpdonadio,

I removed the deprecation of TimeAgoFormatter after you raised your concerns in #28. There's a followup #2938705: Consider deprecating TimestampAgoFormatter, postponed on this , where we should decide if to deprecate or not. Anyway, now this issue is no more blocked. We need your review as subsystem maintainer.

dawehner’s picture

#82.5: I wasn't able to use Array.prototype.forEach(). Probably because we're iterating over an object, not over an array? I have no idea.

In that case you can use Object.keys() | Object.values() ... dependencies on the usecase.

claudiu.cristea’s picture

@dawehner, I need both. Object.keys() is returning an array of keys, while Object.values() an array of values. None fits.

mpdonadio’s picture

Assigned: mpdonadio » jhedstrom

Sincerely apologize for being away for so long. Life got in the way...

Reread the issue a few times and the latest patch.

I think essentially what we are saying is (restating the IS in my own words more for my own benefit)

- TimestampAgoFormatter, with the max-age stuff, works well for ago formats places where things can be old, but still want to update, like the "On Drupal.org ..." on our profiles.
- TimestampAgoFormatter, with the max-age stuff, works poorly for ago formats that change rather frequently, like the top of Twitter.

In other words, the max-age is good in theory, but in practice has big UX/DX impacts. So

- Let's output timestamps as proper HTML5 time elements regardless, with the correct attribute
- When outputting an ago format, add a JS ticker to update the display value of the HTML5 time element
- Defer TTL to the consumer
- Defer what to do with TimestampAgoFormatter and the datetime formatters to a followup

And we are also implying this is the better choice than adding a setting to TimestampAgoFormatter to output as a non-cached HTML5 time element w/ JS updater?

I think I am OK with this. Not passing the buck, but I think getting @jhedstrom to weigh in too, is a good idea. There is nothing technically wrong with this patch at a high level, if @jhedstrom thinks this is OK, too, we are just down to checking it for mistakes.

mpdonadio’s picture

Related issues:

NM.

jhedstrom’s picture

Assigned: jhedstrom » Unassigned
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs subsystem maintainer review

I spent quite a bit of time reviewing the patch, and manually testing. I can't find anything to improve upon at this time, and it definitely works as expected. Allowing the time-ago format on fully-cacheable markup will be great, and even better when we get this in in the follow-up for the datetime formatters. This even has tests for the upgrade path, which is great!

The last submitted patch, 62: 2921810-62.patch, failed testing. View results

jhedstrom’s picture

I'm not sure why the testbot is claiming the patch doesn't apply. I've applied to to the latest 8.7.x locally with no problem.

claudiu.cristea’s picture

@jhedstrom, he's complaining about an old patch, from #62. Don't know why

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -166,26 +265,97 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
    +    if ($tooltip['date_format'] === 'custom' && $tooltip['custom_date_format'] === 'r') {
    +      $tooltip_langcode = 'en';
    +    }
    

    This appears to be untested.

  2. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -166,26 +265,97 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
    +      1 => $this->t('1 second'),
    +      10 => $this->t('10 seconds'),
    +      15 => $this->t('15 seconds'),
    +      30 => $this->t('30 seconds'),
    +      60 => $this->t('1 minute'),
    +      300 => $this->t('5 minutes'),
    +      600 => $this->t('10 minutes'),
    
    +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,287 @@
    +              Drupal.formatPlural(units, '1 minute', '@count minutes'),
    ...
    +              Drupal.formatPlural(units, '1 second', '@count seconds'),
    

    If you used formatPlural() here and not t() then we'd be introducing less new translatable strings. Also this seems to be a lot of options. 10,15,30 - maybe we only need one of those? Or was there a reason for the chosen numbers - if so it should be documented.

  3. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,287 @@
    +    year: 365 * 24 * 60 * 60,
    +    month: 30 * 24 * 60 * 60,
    +    week: 7 * 24 * 60 * 60,
    +    day: 24 * 60 * 60,
    +    hour: 60 * 60,
    +    minute: 60,
    +    second: 1,
    

    Let's do the math here. There's no need for computers to do this everytime we load the JS it's not going to change.

  4. +++ b/core/modules/system/system.post_update.php
    @@ -169,3 +170,26 @@ function system_post_update_extra_fields(&$sandbox = NULL) {
    +function system_post_update_timestamp_formatter() {
    +  /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $entity_view_display */
    +  foreach (EntityViewDisplay::loadMultiple() as $entity_view_display) {
    

    This needs to use the \Drupal\Core\Config\Entity\ConfigEntityUpdater - see image_post_update_scale_and_crop_effect_add_anchor() for an example.

  5. +++ b/core/modules/system/system.post_update.php
    @@ -169,3 +170,26 @@ function system_post_update_extra_fields(&$sandbox = NULL) {
    +        if (get_class($formatter_plugin) === TimestampFormatter::class || is_subclass_of($formatter_plugin, TimestampFormatter::class)) {
    

    Can use instanceof here. I.e if ($formatter_plugin instanceof TimestampFormatter::class) {

  6. +++ b/core/modules/system/system.post_update.php
    @@ -169,3 +170,26 @@ function system_post_update_extra_fields(&$sandbox = NULL) {
    +          $component['settings'] = $formatter_plugin->getSettings();
    +          $entity_view_display->setComponent($name, $component);
    

    Do we actually need to do this? Won't the entity view display self heal when you save it and add the default in?

claudiu.cristea’s picture

@alexpott,

+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
@@ -166,26 +265,97 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
+    if ($tooltip['date_format'] === 'custom' && $tooltip['custom_date_format'] === 'r') {
+      $tooltip_langcode = 'en';
+    }

This appears to be untested.

Actually, I think this check should be moved into DateFormatter::format() so that a call such as:

    \Drupal::service('date.formatter')->format(1234567890, 'custom', 'r')
    \Drupal::service('date.formatter')->format(1234567890, 'custom', 'r', NULL, 'fr')

will always force the $langcode to 'en' when 'custom' and 'r'.

wilfred waltman’s picture

StatusFileSize
new61.36 KB

Rerolled patch #86 for Drupal 8.6.4

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new3.67 KB
new61.23 KB

#99.1: As that is (should be) an implementation detail of \Drupal::service('date.formatter')->format(), I'll split that into a new issue and will block this on that issue.

#99.2: Fixed. From 10, 15, 30, I opted for 15 but I have no idea why. Can you be more specific?

#99.4, 5, 6: Fixed but note that $entity_view_display->setComponent($name, $component); still cannot be avoided. That's the place where the component settings are self healing.

claudiu.cristea’s picture

StatusFileSize
new67.39 KB
new8.38 KB

Ok. I tried to see what's going on with #99.1. Suprize! DateFormatter::format() always handles that correctly. I attached 2 Unit tests and one Kernel test to prove that. Anyway, reading the code, you can find this in DrupalDateTime::format(), line ~105:

$format = preg_replace(['/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'], ["\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"], $format);

where it's obvious that the "r" date format marker is not encoded to be translated.

This closes also #99.1 making this issue RTBC if there are no other concerns.

claudiu.cristea’s picture

StatusFileSize
new2.32 KB
new67.39 KB

Typos...

The last submitted patch, 103: 2921810-103.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 104: 2921810-104.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new67.35 KB
new794 bytes

I should not have changed this line...

claudiu.cristea’s picture

StatusFileSize
new1.45 KB
new67.32 KB

Nits.

jonathanshaw’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 108: 2921810-108.patch, failed testing. View results

krzysztof domański’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 108: 2921810-108.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community

It was random failure.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 108: 2921810-108.patch, failed testing. View results

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community

It was a random failure, back to RTBC.

krzysztof domański’s picture

Status: Reviewed & tested by the community » Needs work

2 of the last 16 tests (12,5%) ended with the same random failure.
https://www.drupal.org/pift-ci-job/1242425

25 Feb 2019: https://www.drupal.org/pift-ci-job/1211085
1 Mar 2019: https://www.drupal.org/pift-ci-job/1215415

Drupal\FunctionalJavascriptTests\Core\Field\TimestampFormatterWithTimeDiffViewsTest::testTimestampFormatterWithTimeDiff
Javascript condition met:
jQuery('.entity-11 time').text() > '9 seconds ago'
Failed asserting that false is true.

/var/www/html/core/tests/Drupal/KernelTests/AssertLegacyTrait.php:35
/var/www/html/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php:145
/var/www/html/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffViewsTest.php:66

FAILURES!
Tests: 1, Assertions: 15, Failures: 1.

This test testTimestampFormatterWithTimeDiff() generates random test failures so I change to needs work.

--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffViewsTest.php
@@ -0,0 +1,141 @@
+
+  /**
+   * Tests the timestamp formatter used with time difference setting in views.
+   */
+  public function testTimestampFormatterWithTimeDiff() {
+    $data = $this->getRowData();
+
+    // Create the entities.
+    foreach ($data as $delta => $row) {
+      EntityTest::create([
+        'type' => 'test',
+        // Using this also as field class.
+        'name' => "entity-$delta",
+        'created' => $row['timestamp'],
+      ])->save();
+    }
+    $this->drupalGet('formatter_timestamp_as_time_diff');
+
+    $page = $this->getSession()->getPage();
+    foreach ($data as $delta => $row) {
+      $time_diff = $page->find('css', ".entity-$delta")->getText();
+      $regex_pattern = "#{$row['pattern']}#";
+      // Test that the correct time difference is displayed. Note that we are
+      // able to check an exact match for rows that have a creation date more
+      // distant, but we use regexp to check the entities that are only few
+      // seconds away because of the latency introduced by the test run.
+      $this->assertRegExp($regex_pattern, $time_diff);
+    }
+
+    // Wait at least 1 second + 1 milisecond to make sure the 'right now'
+    // time difference was refreshed.
+    $this->assertJsCondition("jQuery('.entity-$delta time').text() > '$time_diff'", 1001);
+  }
krzysztof domański’s picture

I re-tested #108 to confirm the previous comment.

The test failed again https://www.drupal.org/pift-ci-job/1245779

Drupal\FunctionalJavascriptTests\Core\Field\TimestampFormatterWithTimeDiffViewsTest::testTimestampFormatterWithTimeDiff
Javascript condition met:
jQuery('.entity-11 time').text() > '9 seconds ago'
Failed asserting that false is true.

/var/www/html/core/tests/Drupal/KernelTests/AssertLegacyTrait.php:35
/var/www/html/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php:145
/var/www/html/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffViewsTest.php:66

FAILURES!
Tests: 1, Assertions: 15, Failures: 1.
claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new1.62 KB
new67.23 KB

Not sure I got the failure correctly.

claudiu.cristea’s picture

Discussed with @alexpott, at DrupalCon Seattle about the failure with PHP 5.5 & MySQL 5.5 and he told that this failure is not related to this issue and happens from time to time with the HEAD too. So, this could be safely RTBCed.

EDIT: The plan is to remove the support for PHP 5 starting with 8.8.x, so this failure will not be relevant anymore.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

I think the failures are all related to the switch to daylight savings that happens around this time of year for various regions. Moving back to RTBC.

krzysztof domański’s picture

1. The default timezone for testing is Australia/Sydney (See core/includes/install.core.inc). All previous random test failures were repeated on different days so I think they are not related to DST.

25 Feb 2019: https://www.drupal.org/pift-ci-job/1211085
1 Mar 2019: https://www.drupal.org/pift-ci-job/1215415
30 Mar 2019 https://www.drupal.org/pift-ci-job/1245779

2. Here is a recent random failure probably related to the switch to daylight savings in Australia.

#3046570: \Drupal\Tests\datetime\Kernel\Views\FilterDateTest::testDateOffsets can fail during DST changes

3. Patch #108:

--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffViewsTest.php
@@ -0,0 +1,141 @@
+
+  /**
+   * Tests the timestamp formatter used with time difference setting in views.
+   */
+  public function testTimestampFormatterWithTimeDiff() {

     (...)

+
+    // Wait at least 1 second + 1 milisecond to make sure the 'right now'
+    // time difference was refreshed.
+    $this->assertJsCondition("jQuery('.entity-$delta time').text() > '$time_diff'", 1001);
+  }

IMO this looks like time-sensitive random test failure.

New in Symfony 2.8: Clock mocking and time sensitive tests:

This code is so simple that it seems impossible to fail. However, depending on the load of the server, the $duration could be for example 10.00000023 and the test would fail for no apparent reason.

claudiu.cristea’s picture

#122.3: That is already fixed in #119. The reason for the failure is that, on Drupal CI, in certain conditions, Selenium has some latency on updating the DOM. That latency might go beyond 1001 ms. With #119, that is fixed.

larowlan’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,287 @@
    +        $.each(Drupal.dateFormatter.intervals, (interval, duration) => {
    +          if (interval === lastUnit) {
    +            refresh = refresh < duration ? duration : refresh;
    +            return false;
    +          }
    +        });
    

    nit: we could return after this and avoid the else

  2. +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffViewsTest.php
    @@ -0,0 +1,135 @@
    +  public function testTimestampFormatterWithTimeDiff() {
    

    How confident are we this won't introduce random fails?

  3. +++ b/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
    @@ -11,6 +15,8 @@
    +  use PhpunitCompatibilityTrait;
    +
    

    By my reckoning UnitTestCase already includes this - so not needed?

NW for last point

yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
yogeshmpawar’s picture

Assigned: yogeshmpawar » Unassigned
Status: Needs work » Needs review
StatusFileSize
new67.09 KB
new1.34 KB

Addressed comments #125 in updated patch & added an interdiff as well.

jonathanshaw’s picture

Status: Needs review » Needs work

#127 does not remove the "else" that was the purpose of #125.1

yogeshmpawar’s picture

Status: Needs work » Needs review
StatusFileSize
new67.03 KB
new2.24 KB

Made changes as per #128 & #125.1 & added an interdiff as well.

Status: Needs review » Needs work

The last submitted patch, 129: 2921810-129.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

  1. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,285 @@
    +        $.each(Drupal.dateFormatter.intervals, (interval, duration) => {
    +          if (interval === lastUnit) {
    +            refresh = refresh < duration ? duration : refresh;
    +            return false;
    +          }
    +        });
    

    It seems that is missing return refresh; after this block.

  2. +++ b/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
    @@ -3,7 +3,11 @@
    +use Drupal\Tests\PhpunitCompatibilityTrait;
    

    Unused statement?

Responding also to #125.2: In #119, I ran few consecutive tests, also the patch is for 46 days in RTBC, so is tested daily. No failures.

yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
yogeshmpawar’s picture

Assigned: yogeshmpawar » Unassigned
Status: Needs work » Needs review
StatusFileSize
new67.03 KB
new1.42 KB

Thanks @claudiu.cristea for review, Comments addressed in #131 & added an interdiff as well.

yogeshmpawar’s picture

StatusFileSize
new67.09 KB
new1.73 KB

Adding 'else condition' again in code which was removed by me in #129 mistakenly.

claudiu.cristea’s picture

#134, I think the removal of else {...} was correct.

claudiu.cristea’s picture

Status: Needs review » Needs work

As i told in #134, #133 is correct. Could you re-post that patch just to have the final work?

jonathanshaw’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new67.03 KB

Patch from #133 reuploaded.

alexpott’s picture

StatusFileSize
new67.04 KB

Rerolled the patch - there was a simple conflict in core/tests/Drupal/Tests/Core/Datetime/DateTest.php due to the getMock deprecation. I also fixed an es6 to js complication thing.

I have not reviewed the patch properly yet.

idebr’s picture

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs usability review, +Needs screenshots
  1. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,100 @@ public static function defaultSettings() {
    +      '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
    

    I can't really understand what the description is suggesting here. Tagging for screenshots too.

  2. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,100 @@ public static function defaultSettings() {
    +      '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
    

    Same issue.

  3. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -101,42 +114,100 @@ public static function defaultSettings() {
    +      '#description' => $this->t('How often to refresh the displayed time difference.'),
    

    Should this explicitly say it's for JavaScript refresh? Also do we really need this to be configurable per formatter - could it not be a sitewide thing controlled by settings?

  4. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
    @@ -164,28 +263,87 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
    +
    +    if ($time_diff['enabled']) {
    +      $elements['#attached']['library'][] = 'core/drupal.time-diff';
         }
     
    

    Could we move this above the foreach, it looks a bit disjointed from everything else down here.

  5. +++ b/core/modules/system/system.post_update.php
    @@ -206,3 +207,22 @@ function system_post_update_add_expand_all_items_key_in_system_menu_block(&$sand
    +function system_post_update_timestamp_formatter(&$sandbox = NULL) {
    

    Like some other config entity updates, this should be done in a config save listener so that it also catches default configuration provided by modules and similar, and then the update can just load and save all entity view displays.

claudiu.cristea’s picture

@catch

Like some other config entity updates, this should be done in a config save listener so that it also catches default configuration provided by modules and similar

But the config save dispatcher is dispatching the event (ConfigEvents::SAVE) after saving. There's no config pre-save event. Could you give a similar example?

claudiu.cristea’s picture

Status: Needs work » Needs review
Issue tags: -Needs screenshots
StatusFileSize
new131.19 KB
new2.87 KB
new67.34 KB

#140.1, 2: That was copied from TimestampAgoFormatter. Hopefully, is much more clear now. However, as I'm not a native English speaker, maybe someone else could find a better description phrase. Added screenshot.

#140. 3: Added explanation.

Also do we really need this to be configurable per formatter - could it not be a sitewide thing controlled by settings?

This was discussed already and we decided to go with a "per formatter" settings. See #86.

#140.4: Fixed

#140.5: I don't understand the requirement, as I commented in #141.

EDIT: I've been searched for all usages of ConfigEvents::SAVE in core and none is updating the config when its schema is changing and it has to receive new config defaults.

renguer0’s picture

Hi there. I'm testing lastest patch (#142). I've seen a difference when patching in :

use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -67,6 +69,9 @@ protected function setUp() {
     $this->entityTypeManager->expects($this->any())->method('getStorage')->with('date_format')->willReturn($entity_storage);

$this->languageManager = $this-><strong>createMock</strong>('Drupal\Core\Language\LanguageManagerInterface');

My file shows getMock instead of createMock. I applied manual modifications to this file and all seems to be OK.

I really don't know can I apply this modifications to article comments views in my site and I'm using twig templates variables. Any tips?

BTW the label shows in the system and I using it for taxonomy terms and frontpage.

Thanks to all to colaborating with this.

chi’s picture

Issue tags: +Needs reroll
chi’s picture

Status: Needs review » Needs work

The patch is not applicable to 8.8.x.

kostyashupenko’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new67.17 KB

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

claudiu.cristea’s picture

Issue tags: -JavaScript +JavaScript
StatusFileSize
new4.49 KB
new69.92 KB

Finally I has a Slack discussion with @catch on #140.8. The suggestion is to use the presave hook and provide defaults for modules that are not providing them in the default configuration. See system_entity_form_display_presave() and system_post_update_entity_reference_autocomplete_match_limit() for an example. About the "deprecated version" and "removed in" version, I;m quoting from chat:

clau
@catch to summarize: This message would cover? (...)
> Not ensuring the ‘timestamp’ formatter plugin ‘tooltip’ and ‘time_diff’ settings is deprecated in drupal:8.8.0. The settings are mandatory in drupal:9.0.0. See

catch
@clau probably closer to "The 'timestamp' formatter plugin 'tooltip' and 'time_diff' settings were added in drupal:8.9.0 and will be mandatory in Drupal 10.0.0. See"

Now, this still needs UX review. How to proceed?

claudiu.cristea’s picture

StatusFileSize
new697 bytes
new69.91 KB

In the meantime the core keys has been removed from the view config entity, in #3087692: Remove the core key from views configuration..

The last submitted patch, 148: 2921810-148.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 149: 2921810-149.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new4.59 KB
new73.8 KB

In the meantime Media module has added some tests using the timestamp formatter.

claudiu.cristea’s picture

Issue summary: View changes
StatusFileSize
new131.19 KB
new66.48 KB
new63 KB
new101.96 KB
new99.81 KB

Added:

  • Manual testing guidelines
  • Screenshots

webchick credited ckrina.

webchick’s picture

Issue tags: -Needs usability review
StatusFileSize
new158.22 KB

We went over this on today's UX meeting (https://youtu.be/0Fjk4cFiIV0 once it's finished processing).

First of all, big +1 to this feature. Seems like a nice performance improvement with minimal end user disruption.

For reference, the "before" version of the date/time formatter looks like this:

A handful of settings

The proposed "after" version looks like this:

Many many more settings

Which is... quite a bit more settings. ;)

The first question we had is whether or not we can handle this as new "Time ago/until" date format (as in something you'd configure at admin/config/regional/date-time). Because then, we would only need to put these settings in one place, globally, and would not need to worry about doing something like going out and changing something like "Time interval" from 1 minute to 15 minutes in 10 different places (one for each date/time field on our site). Instead, it would follow the existing pattern of formatters, which is to allow people to select from one of the predefined options that are found elsewhere. Then it could also presumably be used outside the Field API as well, for e.g. user created timestamps or what have you.

Failing that, using the #states API to hide all of the various other options until and unless "Display as a time difference" is selected would help a lot with the visual clutter here. If we go the checkbox route, one small improvement would also be moving the example to the checkbox label vs. the description as it was initially missed. "Display as a time difference (e.g. '6 months ago')" The other formats have the advantage of "previewing" the choice you're making in the selections themselves.

And then finally, there's nothing in the user interface at all that promotes this option as something that's important to do for performance. So it would be nice to add that to the field description in some way, to encourage people to make the selection.

It also doesn't look like there's any visual treatment that would show people that a "time ago" is something they can hover over to get a tooltip to get the actual date/time value. We should explore if Bartik has some pre-existing styles we can use for that (e.g. on Drupal.org we use a 1px dotted underline on these times), else maybe open a follow-up for that.

jonathanshaw’s picture

#86 has some prior discussion about removing the 'Refresh interval' setting from the UI. Even if the format is moved to something configured globally, the refresh interval probably does NOT belong on that as the reaons for specifying it would depend on how rapidly the particular entity being displayed is likely to change.

claudiu.cristea’s picture

Issue summary: View changes
Status: Needs review » Needs work
StatusFileSize
new165.97 KB

@webchick & UX team, thank you for reviewing this patch. I was watching the video related to this review. Here are my notes:

The first question we had is whether or not we can handle this as new "Time ago/until" date format (as in something you'd configure at admin/config/regional/date-time). Because then, we would only need to put these settings in one place, globally, and would not need to worry about doing something like going out and changing something like "Time interval" from 1 minute to 15 minutes in 10 different places (one for each date/time field on our site). Instead, it would follow the existing pattern of formatters, which is to allow people to select from one of the predefined options that are found elsewhere. Then it could also presumably be used outside the Field API as well, for e.g. user created timestamps or what have you.

The strings "@interval hence" & "@interval ago" are NOT date/time formats. They are translatable strings, thus we can't assimilate them with date/time formats. What we can probably do is to create a new config type that stores such strings plus the time units (int) and the refresh interval (int). Such a config entity is actually a preset for this formatter. Then in the formatter, the site builder just picks-up a preset. But what if the they want a custom config? The whole UI gets more complex and we have to handle a lot of cases (e.g. the preset changes, what happens with a specific formatter config?). Is it worth to bring such a complexity? And the new "preset config" needs also its admin UI, and so on... About the per-formatter refresh interval a discussion already took place in #83 & #86.

Failing that, using the #states API to hide all of the various other options until and unless "Display as a time difference" is selected would help a lot with the visual clutter here. (...)

Yes, this is already implemented by I forgot to add a screenshot. I’ve uploaded now. I think this solves the case by offering a default sitewide preset.

(...) If we go the checkbox route, one small improvement would also be moving the example to the checkbox label vs. the description as it was initially missed. "Display as a time difference (e.g. '6 months ago')" The other formats have the advantage of "previewing" the choice you're making in the selections themselves.

I can see the value. Setting to "Needs work" to implement this.

And then finally, there's nothing in the user interface at all that promotes this option as something that's important to do for performance. So it would be nice to add that to the field description in some way, to encourage people to make the selection.

There is the other formatter timestamp_ago, which has problems with the cache and there's a question in #2938705: Consider deprecating TimestampAgoFormatter if it should be deprecated in favour of this solution. Wouldn't be better to add a there a short phrase indicating this option as better in terms of caching & performance?

It also doesn't look like there's any visual treatment that would show people that a "time ago" is something they can hover over to get a tooltip to get the actual date/time value. We should explore if Bartik has some pre-existing styles we can use for that (e.g. on Drupal.org we use a 1px dotted underline on these times), else maybe open a follow-up for that.

I will look into that but as far as i know is only d.o doing this. We've also using now the HTML5 <time /> tag probably themes can select and apply such formats.

claudiu.cristea’s picture

Issue summary: View changes
dennis cohn’s picture

The patch #152 failed to apply on 8.8.4

wilfred waltman’s picture

StatusFileSize
new73.89 KB

Rerolled patch 152 for Drupal 8.8.4.
No changes to the code. Same code just on different lines.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new73.95 KB

Rerolled for 8.9.x. Setting to NR to run the tests.

nod_’s picture

Status: Needs review » Needs work

I didn't look at the PHP, here is the JS review:

  1. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +  Drupal.timestampAsTimeDiff = Drupal.timestampAsTimeDiff || {};
    +  Drupal.dateFormatter = Drupal.dateFormatter || {};
    

    Not necessary, if this file is the one that declare those 2 properties, no need to check if they exists.

    Other scripts that needs to rely on this will need to add the library as a dependency.

  2. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +      $('time.js-time-diff')
    

    can we use a data- attribute instead of a class please?
    need to be scoped with the context too: $(context).find()

  3. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +        elements.removeOnce('time-diff');
    +        elements.each((index, $timeElement) => {
    +          clearInterval($timeElement.timer);
    +        });
    

    the each needs to be chained after the removeOnce, removeOnce act as a filter on "elements". since elements is a jquery object, it should be named "$elements".

  4. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +    const now = Date.now();
    

    shouldn't that be global to the script instead of having a version of "now" for each time element?

  5. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +        $.each(Drupal.dateFormatter.intervals, (interval, duration) => {
    

    if it's an array, use array methods, jquery.each is unnecessary.

  6. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +  Drupal.dateFormatter.formatDiff = (from, to, options) => {
    

    the name and parameters isn't coherent. I would expect 2 arguments: the diff value and options.

    The comments should specify if it's a unix timestamp in seconds or miliseconds that is expected.

  7. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +    options = $.extend({ granularity: 2, strict: true }, options);
    

    you can use Object.assign, instead of $.extend()

  8. +++ b/core/misc/time-diff.es6.js
    @@ -0,0 +1,286 @@
    +            output.push(Drupal.formatPlural(units, '1 year', '@count years'));
    ...
    +            output.push(Drupal.formatPlural(units, '1 month', '@count months'));
    ...
    +            output.push(Drupal.formatPlural(units, '1 week', '@count weeks'));
    ...
    +            output.push(Drupal.formatPlural(units, '1 day', '@count days'));
    ...
    +            output.push(Drupal.formatPlural(units, '1 hour', '@count hours'));
    ...
    +              Drupal.formatPlural(units, '1 minute', '@count minutes'),
    ...
    +              Drupal.formatPlural(units, '1 second', '@count seconds'),
    

    We might want to provide a way to change the format for this, beside the translation. I could see people wanting to have "1s" instead of "1 second".

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

seanb’s picture

StatusFileSize
new72.34 KB

Reroll for 9.1.x.

claudiu.cristea’s picture

@nod_

We might want to provide a way to change the format for this, beside the translation. I could see people wanting to have "1s" instead of "1 second".

Can we do this in a followup? This issue already includes a huge amount of work, the patch is almost 80K. If I create that configurable HERE, then we need config schema, we need UI. And if we need UI, then we need again usability review. This will never end and it's not the right way to manage such a big change. Can we followup, please?

nod_’s picture

I'm ok with that

claudiu.cristea’s picture

I've created a MR. The 9.2.x development will continue there. The MR has been created from #150.

nod_’s picture

added a few more comments on top of #165

claudiu.cristea’s picture

Status: Needs work » Needs review

#165.1, 2, 3: Fixed
#165.4: That will not gonna work. Every time it needs a fresh value.
#165.5: Is not working with forEach(). The jQuery each() knows to break the iteration when returning false and that's a feature we need here.
#165.6, 7: Fixed
#165.8: Will create a follow-up

claudiu.cristea’s picture

Latest changes:

  • #157.2 (moving the example to label): I did that and I've removed the description for UX reasons, as now it makes no sense to clutter the UI.
  • #165.5: Yeah, finally I've fixed this by fully removing the jQuery dependency.
  • #165.8: Created #3209343: Make time diff chunks configurable as followup.

I think I've covered all remarks with changes and some with explanations. This is ready for RTBC.

claudiu.cristea’s picture

In 350f12, I've separated more clear Drupal.timestampAsTimeDiff and Drupal.dateFormatter. Previously, Drupal.dateFormatter had a dependency on Drupal.timestampAsTimeDiff. @nod_ & @droplet, do you think that it's worth it to have them in separate files, or even separate libraries (with dependency)?

claudiu.cristea’s picture

claudiu.cristea’s picture

nod_’s picture

Status: Needs review » Needs work

Some comments about UI for sitebuilders and assumptions being broken between settings and code.
In short I think we might want to have a new formater to be able to make things clear for site builders (we can name it "Time Ago (dynamic)" or something to differentiate with the other, or rework the old one to be smarter maybe?). Maybe doing the inverse of what we did for the active class in menu links? (set with JS for auth users, set on the PHP side for anon users)

For the JS we call the library time-diff and there are 2 members declared on the Drupal object, put everything in Drupal.timeDiff to limit confusion, as this is not a generic library (it needs settings and specific markup) I'm not sure it belongs to the misc folder (we're trying to avoid adding more stuff in there).

Any reasons why it's not added to the date field type? Granularity will be days but it should still work for that one too because date also has a Time Ago formatter.

claudiu.cristea’s picture

Status: Needs work » Needs review
Issue tags: +Needs subsystem maintainer review

previous comment about this method being too smart. Either there are no refresh settings and we figure it out based on the date granularity or we have a refresh rate and don't try to be smarter than the sitebuilder.

It's not about being smarter than the site builder. I want to still honour their choice but eliminate hidden refreshes that make no sense. Here are my remarks:

  • Option 1: Strictly refresh on configured interval. If it has been configured to 1 sec, a time ago such as 7 years 8 months would still refresh every second. This I wanted to avoid, senseless refresh from UI perspective and resources consumption.
  • Option 2: Smart refresh computed from granularity: What if the site builder wants seconds in granularity but still don't want to refresh every second because such a refresh would distract the user from the main content?

In short I think we might want to have a new formater to be able to make things clear for site builders (we can name it "Time Ago (dynamic)" or something to differentiate with the other, or rework the old one to be smarter maybe?)

This discussion took place already, see the earlier comments. If we start again and again, this will never end. My point: creating a new formatter will add more confusion for site builders. Fixing this here and deprecating the other (see the follow-up) would be the logic path. Also we've already discussed in earlier comments about wording. We avoid "Time ago", we use "Time difference".


I would work to fix the other remarks but I'll not do it until these ^^^ two are clarified, otherwise it's just a waste of work on a complex new feature. Tagging with "Needs subsystem maintainer review" to get an opinion.


Also:

Any reasons why it's not added to the date field type? Granularity will be days but it should still work for that one too because date also has a Time Ago formatter.

This is for the core timestamp field type. We have a followup to add this to datetime field too.

nod_’s picture

Thanks for the reply, not trying to reset the whole thing.

These are good points and it won't be such an issue with more accurate labels and descriptions. As is it is confusing and expectations/tradeoffs are not clear.

  • I'd put the time diff checkbox at the top of the form, and change labels based on that
  • change "Date format" and "Custom Date format" in "Fallback Date format", "Fallback Custom Date format" with some description saying it's for no-js use case
  • only when when checked display the new tooltip field
  • Change "Refresh interval" into "Minimum(or is it maximum?) refresh interval" with a description explaining/mentioning the relation with granularity

That would solve most of the issues I have with putting that in the same formatter

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

seanb’s picture

StatusFileSize
new76.05 KB

A lot of nice work being done here! Here is an attempt to reroll the latest changes for 9.1.x.

seanb’s picture

StatusFileSize
new76.1 KB

Sorry, here is another attempt.

seanb’s picture

StatusFileSize
new72.42 KB

Ah, jQuery once has been removed from the patch for 9.2.x. That is going to be an issue. I guess for now 9.1.x user can technically use a reroll of #167 and upgrade to the 9.2.x patch later.

aaronmchale’s picture

Ah, jQuery once has been removed from the patch for 9.2.x. That is going to be an issue. I guess for now 9.1.x user can technically use a reroll of #167 and upgrade to the 9.2.x patch later.

Does https://www.drupal.org/project/once help here?

seanb’s picture

Thanks! That could have fixed the issue as well. Good to know!

aaronmchale’s picture

Thanks! That could have fixed the issue as well. Good to know!

You're welcome :) According to the change logs, that library is now included in 9.2 and above, so if needed, this issue should be able to rely on that as a replacement for jQuery Once.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

seanb’s picture

StatusFileSize
new72.46 KB

Reroll for 9.3.x.

seanb’s picture

StatusFileSize
new75.98 KB

Doh, should have based the reroll on the MR instead of #184. Here is a reroll based on the MR for 9.3.x.

dww’s picture

This would be fabulous! Very exciting. Hope to do a thorough review soon. Not exactly qualified to do a formal subsystem maintainer review for this, since I'm not formally a datetime maintainer (although I am a co-maintainer for datetime_extras), but I'll do what I can. 😉

catch’s picture

Category: Feature request » Bug report
Issue tags: +Bug Smash Initiative

This is really a bug report, time ago formatting is broken with render caching.

ankithashetty’s picture

StatusFileSize
new76.03 KB
new5.57 KB

Rerolled the patch and fixed the custom command failure errors in #190, thanks!

Status: Needs review » Needs work

The last submitted patch, 193: 2921810-193.patch, failed testing. View results

ravi.shankar’s picture

StatusFileSize
new76.04 KB
new988 bytes

Tried to address failed tests.

claudiu.cristea’s picture

Trying to revive this issue as Drupal evolved a lot in one year

claudiu.cristea’s picture

Note on 390220c4:

In #140.5, @catch recommended to add an entity listener that would provide the new settings with their default values:

Like some other config entity updates, this should be done in a config save listener so that it also catches default configuration provided by modules and similar, and then the update can just load and save all entity view displays.

This has been done in #148.

However, while trying to add a test for the deprecation message, I found that the message is never triggered because, even a module's default configuration lacks the new settings, they are automatically added by the display entity on entity creation. See EntityDisplayBase, specifically EntityDisplayBase::init(). The method calls ::setComponent() which ensures full configuration via FormatterPluginManager::prepareConfiguration(). I removed that hook implementation as is never triggered.

claudiu.cristea’s picture

@nod_, regarding #180:

  • I'd put the time diff checkbox at the top of the form, and change labels based on that
  • change "Date format" and "Custom Date format" in "Fallback Date format", "Fallback Custom Date format" with some description saying it's for no-js use case

This is a nice idea. This I've implemented but only server side. Client side, I really don't know how can we change the field's label/description by using #states. Or, is there a good example on how to achieve this with JS (adding new JS would make this huge MR even more complex: it needs JS code, libraries entry, tests, etc)

  • only when when checked display the new tooltip field

With this I disagree. The new tooltip is designated also for normal timestamp format. You may have a very short format but with a more detailed format as tooltip.

  • Change "Refresh interval" into "Minimum(or is it maximum?) refresh interval" with a description explaining/mentioning the relation with granularity

I think this will only confuse the user. As I've explained, the adaptive refresh is transparent for the user. They will see the same thing regardless if the refresh is "smart" or not. It's only an optimization. I think the current label and description are pretty clear.

claudiu.cristea’s picture

Status: Needs work » Needs review

Discussed with @nod_ on Slack that we should add a description for the no-js case and avoid labels manipulation.

This is ready, IMHO

nod_’s picture

  • I added a description field to explain that the date format options are fallbacks when the time ago checkbox is checked.
  • Moved the tooltip field above the timezone field as the timezone selection applies to both the date and tooltip.
  • The JS library and the data-drupal-time-diff attributes are added only when the refresh rate is above 0. (left the check in the JS just in case though)
  • On the JS side used a weakmap to store the timers instead of adding a member to the native DOM element.
  • Resolved the translations issues in the JS file (new strings created and the use of a variable in the Drupal.t() function. The translation for the past/future text will need to happen at the field config level
claudiu.cristea’s picture

Issue summary: View changes
StatusFileSize
new232 KB
new92.12 KB
new28.25 KB

Updated screenshots according to latest changes.

nod_’s picture

Can't RTBC but it is for me

danflanagan8’s picture

Status: Needs review » Needs work

First, this is awesome. Thanks for all the nice work.

I made a couple comments on the MR related a problem when refresh is 0.

I have some more general musings on the default settings. The update hook works for entity view displays that are configured through Field UI. But it doesn't work for field blocks in Layout Builder that are using the TimestampFormatter. And I suspect it doesn't work for Views that are using the TimestampFormatter to render a field. This was initially worrying since the change record reads

Not ensuring the timestamp formatter settings tooltip and time_diff is deprecated in Drupal 9.4.0. The settings are mandatory in Drupal 10.0.0.

But all the new settings have default values set in the formatter class, so I don't think the imperfect update is actually a problem. And I think the foreboding language can be removed from the CR. The code of the formatter is in reality quite forgiving and BC.

I also have questions about how the tooltip is implemented. Currently, if I'm using the timestamp formatter and I follow this upgrade path, my timestamp will get a tooltip by default. That seems strange because I didn't ask for the tooltip and the tooltip provides questionable value when the timestamp is not being displayed as an interval.

I would think the tooltip should either

1) be set to "No tooltip" by default

or

2) Keep the default settings but only use the tooltip is the timestamp is being rendered as an interval

nod_’s picture

Fixed the refresh issue, thanks for the find :)

claudiu.cristea’s picture

Status: Needs work » Needs review

@danflanagan8, thank you for review. I think @nod_ and me, we've covered all your remarks:

  • Show time diff even refresh === 0
  • Do not add tooltip on existing sites formatters (preserve BC)
  • Update path for Views fields using the time diff formatter
  • Update path for Layout Builder fields using the time diff formatter
  • Expand test coverage for new update paths
  • JS test the case with refresh === 0
danflanagan8’s picture

Status: Needs review » Needs work

@claudiu.cristea, looks awesome! My manual testing, including the new post_update hooks, went perfectly. The LB update doesn't work for overrides, but I don't know that there's any way to do that. In fact, the recent change to the image formatter that has similar update hooks (and caused one of the conflicts) doesn't even worry about LB at all. I think your LB post update is great.

The only comment I have now is that the new test method for testing a refresh of 0 needs to be reworked a bit. I left comments on the MR. Please let me know if any of my ramblings are not clear enough.

Thanks for all the awesome work on this one. Back to NW to update the new test.

claudiu.cristea’s picture

Status: Needs work » Needs review

Some random failures on last commits but is green now. Fixed #206. Ready for review.

claudiu.cristea’s picture

Issue summary: View changes
  • Added release notes snippet
  • Updated CR with latest screenshots

Good to go!

danflanagan8’s picture

Status: Needs review » Reviewed & tested by the community

I think everything is in order now. Thanks!

quietone’s picture

Version: 9.4.x-dev » 10.0.x-dev
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

This needs to be against 10.0.x now and have an MR for 9.5.x. Adding tag for a reroll.

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs reroll
danflanagan8’s picture

Status: Reviewed & tested by the community » Needs work

Custom commands failed on that MR. I can't figure out what went wrong.

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community

@danflanagan8, It seems that time-diff.es6.js needed re-compiling. Let's see

claudiu.cristea’s picture

Status: Reviewed & tested by the community » Needs review

@danflanagan8, A fixture has been removed between D9 > 10. I see they've also changed the update tests location. As this has some code changes, could you, please, review?

danflanagan8’s picture

Status: Needs review » Reviewed & tested by the community

Looks great! I'm going to RTBC even though the tests are still running; they have passed recently.

dww’s picture

For the record, here's the bug I was trying to address with commit 0dc164ff : #3122231: Singular variant for plural string must contain @count. I agree for scope management it makes more sense to fix that separately, and update the docs for formatPlural() accordingly. But I wasn't just making stuff up. 😉

Thanks/sorry,
-Derek

spokje’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

MR doesn't apply any more since 8 Jun 2022 at 23:56 CEST.

andregp made their first commit to this issue’s fork.

andregp’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll

Merge Request rebased.

claudiu.cristea’s picture

Status: Needs review » Reviewed & tested by the community

@andregp, what kind of conflict resolution duplicates methods? https://git.drupalcode.org/project/drupal/-/merge_requests/570/diffs?com...

This is only a simple reroll. Back to RTBC

larowlan’s picture

Status: Reviewed & tested by the community » Needs review

There's a few unresolved threads here on the MR

larowlan’s picture

Version: 10.0.x-dev » 10.1.x-dev

This will be 10.1.x only at this point as we're trying to avoid adding any new update hooks to 10.0 or 9.5 so the major update path is as risk free as possible.

But the good news is, it can go in to 10.1 pretty quickly

claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea

OK, but it's the last time I'm touching this

EDIT: Because too often such complex issues/MRs are sent back for such minor nits that eventually can be fixed in followups.

danflanagan8’s picture

Status: Needs review » Reviewed & tested by the community

@claudiu.cristea has addressed all the changes requested by @larowlan, so I'm going to throw this back to RTBC!

I want to note that the deprecation notice has in fact been removed entirely. The deprecation notice is not needed because there's not actually a breaking change of any kind here. The 'tooltip' and 'time_diff' settings have sensible default values and as such are not "mandatory" in that leaving them off will not result in any errors even if the update hooks are not managed correctly by the end user.

Thanks for your continued excellent work on this, @claudiu.cristea! This is a really nice feature that I am looking forward to using. :)

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Unfortunately this issue is not mergeable at the moment. 10.1.x needs to be merged in and conflicts resolved.

alexpott’s picture

Also there's no .es6 files in D10 anymore and JS standards have changed because no IE.

danflanagan8’s picture

Status: Needs work » Reviewed & tested by the community

The recent changes all look good to me. I had a little concern with the last commit, but after a back-and-forth with myself, I'm happy!

claudiu.cristea’s picture

Issue tags: +GlobalContributionWeekend2023
gambry’s picture

Issue tags: -GlobalContributionWeekend2023 +ContributionWeekend2023

Swapping the Drupal Global Contribution Weekend 2023 tag with the correct one.

lauriii’s picture

Status: Reviewed & tested by the community » Needs work

Left some minor feedback on the MR

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community

@lauriii, fixed your suggestion and answered the other 2 remarks. Setting back to RTBC.

catch’s picture

Status: Reviewed & tested by the community » Needs work

Looks like @lauriii's last comment on the MR still needs to be resolved.

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community

I've only removed the class and, as this features already crossed RTBC several times, I dare to set it as RTBC.

claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned
chi’s picture

Given that the rendered time is updated occasionally via JavaScript do we need to add aria-live attribute to it?

claudiu.cristea’s picture

This complex new feature is constantly moved back and forth with improvement requests that could be solved in followups. Could we, please, postpone this potential improvement to a followup? It becomes more and more expensive to keep this MR alive and aligned

rohan-sinha’s picture

Reviewed the Patch on MR !570, worked for me.

rohan-sinha’s picture

Considering #235 , checked the rendered time is updated occasionally via JavaScript, yes it needs accessibility work, will try to work on it.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new89 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

jonathanshaw’s picture

I don't believe the bot, so I scheduled a retest

dww’s picture

Status: Needs work » Needs review
StatusFileSize
new2.5 KB

"Can not add test, MR is not mergeable"

Bot was right. A couple of merge conflicts in the rebase to today's 10.1.x branch. All of them in core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php in the settingsForm() method. Attached an example of one of the conflicts. I think I resolved it all properly, but I haven't tested. Let's see what the bot says now.

claudiu.cristea’s picture

Status: Needs review » Reviewed & tested by the community

It was just a reroll

catch’s picture

Status: Reviewed & tested by the community » Needs work

Given #235 and #236, I checked whether aria-live was an accessibility requirement here or not.

Docs at https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Liv...

Normally, only aria-live="polite" is used. Any region which receives updates that are important for the user to receive, but not so rapid as to be annoying, should receive this attribute. The screen reader will speak changes whenever the user is idle.

aria-live="assertive" should only be used for time-sensitive/critical notifications that absolutely require the user's immediate attention. Generally, a change to an assertive live region will interrupt any announcement a screen reader is currently making. As such, it can be extremely annoying and disruptive and should only be used sparingly.

As aria-live="off" is the assumed default for elements, it should not be necessary to set this explicitly, unless you're trying to suppress the announcement of elements which have an implicit live region role (such as role="alert").

Given this can potentially update very frequently, and it doesn't add new information to the page, it just silently updates information that's already there so that it's up-to-date if you leave a tab open for a long time or similar, I don't think we should add aria-live at all, if we did, we'd be setting it to aria-live="off" which is discouraged.

I am not really qualified to review the JavaScript in this issue, although to the extent that I can, I really like the approach of optimising the refresh to the maximum possible interval - that should hopefully make this cheap on people's tabs.

I did find one issue with the layout update path that needs fixing before this can go in - see comments on the MR. Marking needs work for that.

claudiu.cristea’s picture

Status: Needs work » Needs review
danflanagan8’s picture

Status: Needs review » Reviewed & tested by the community

The new layout_builder_entity_view_display_presave function as well as the updates to layout_builder_post_update_timestamp_formatter look like what was requested by @catch.

I also like to change from strpos to str_starts_with, which was a nice touch.

Back to RTBC, hopefully for the last time. ;)

chi’s picture

Re #236 I think accessibility is not an improvement but something that needs to be done by default every time a new feature is delivered. I actually came to the same conclusion as #243. Just wanted someone with proper experience confirmed this.

  • catch committed 2c3f5743 on 10.1.x
    Issue #2921810 by claudiu.cristea, nod_, seanB, yogeshmpawar, dww,...
catch’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Fixed
Issue tags: +10.1.0 highlights, +10.1.0 release notes

Thanks that looks better.

As mentioned I'm not qualified to sign off on the JS, but I checked who'd reviewed, and it's had extensive review from nod_ and droplet and others, so I think that's covered, and what I can review looked very good.

The PHP side looks great, it adds some extra form options, but if we didn't have them, I'd probably have asked for them, so I think it's good to have the flexibility those provide.

Did my best with the issue credits here but it's a very long issue with a lot of contributors, so apologies if I missed anyone.

Tagging for 10.1.0 release highlights.

Committed/pushed to 10.1.x, thanks!

I've also unpostponed the three spin-offs/follow-ups.

longwave’s picture

Issue tags: -10.1.0 highlights +10.1.0 release highlights

Fixing tag

wim leers’s picture

@claudiu.cristea opened this on 8 Nov 2017 at 12:06 CET and was the last person to touch the MR. Epic! 😄🚀

claudiu.cristea’s picture

@catch,

Did my best with the issue credits here but it's a very long issue with a lot of contributors, so apologies if I missed anyone.

I think @lauriii, @Chi and @Krzysztof Domański are missing. Crediting now.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

heddn’s picture

heddn’s picture

Recording for my future self. Converting to use #theme => time now adds html markup to all views responses. That is mostly ok, until it isn't. Mainly with Views REST data exports read by React front-ends that don't know what do with the markup response when previously it was a raw ISO 8601 html_date.