You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/07/15 13:16:03 UTC

[isis] branch master updated: ISIS-3085: Wicket Viewer: adds tooltip to time-zone badge

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 3ba8f9454d ISIS-3085: Wicket Viewer: adds tooltip to time-zone badge
3ba8f9454d is described below

commit 3ba8f9454dbb1934980ca329cbcaa58afe733b77
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Jul 15 15:15:55 2022 +0200

    ISIS-3085: Wicket Viewer: adds tooltip to time-zone badge
    
    - which translates offset time to user's local time
---
 .../services/iactnlayer/InteractionContext.java    | 10 ++++
 .../temporal/TemporalValueSemanticsProvider.java   | 70 +++++++++++++++++-----
 2 files changed, 66 insertions(+), 14 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
index 8c28f1f2d8..8674074bd7 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
@@ -19,7 +19,9 @@
 package org.apache.isis.applib.services.iactnlayer;
 
 import java.io.Serializable;
+import java.time.Instant;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.util.Locale;
 import java.util.Optional;
 import java.util.function.UnaryOperator;
@@ -161,4 +163,12 @@ public class InteractionContext implements Serializable {
         return mappers.reduce(t -> t, (a,b) -> a.andThen(b)::apply);
     }
 
+    /**
+     * Returns the {@link ZoneOffset} at the current time {@link Instant}
+     * (at which {@link ZoneId} rules apply to calculate the offset).
+     */
+    public ZoneOffset getTimeZoneOffsetNow() {
+        return getTimeZone().getRules().getOffset(Instant.now());
+    }
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java
index 9364fb0248..ed426a78e8 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProvider.java
@@ -19,6 +19,11 @@
 package org.apache.isis.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
 import java.time.temporal.ChronoUnit;
@@ -26,6 +31,7 @@ import java.time.temporal.Temporal;
 import java.time.temporal.TemporalQuery;
 import java.util.Optional;
 import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
 import java.util.function.Function;
 import java.util.function.UnaryOperator;
 
@@ -144,21 +150,27 @@ implements TemporalValueSemantics<T> {
     public final String titlePresentation(
             final ValueSemanticsProvider.Context context,
             final T value) {
-        return renderTitle(value, getRenderingFormatter(context, UnaryOperator.identity()));
+        return renderTitle(value, getRenderingFormatter(context, (text, tooltip)->text));
     }
 
     @Override
     public final String htmlPresentation(
             final ValueSemanticsProvider.Context context,
             final T value) {
-        return renderHtml(value, getRenderingFormatter(context, this::toBootstrapBadge));
+        return renderHtml(value, getRenderingFormatter(context, this::toBootstrapBadgeWithTooltip));
     }
 
     /**
      * @apiNote Ideally this logic would move to Wicket Viewer, as it depends on presence of <i>Bootstrap</i>.
      */
-    private String toBootstrapBadge(final String text) {
-        return String.format("<span class=\"badge bg-secondary\">%s</span>", text);
+    private String toBootstrapBadgeWithTooltip(final String text, final String tooltip) {
+        return String.format("<span "
+                + "class=\"badge bg-secondary\" "
+                + "data-bs-container=\"body\" "
+                + "data-bs-toggle=\"tooltip\" "
+                + "title=\"%s\">"
+                + "%s"
+                + "</span>", tooltip, text);
     }
 
     // -- PARSER
@@ -201,31 +213,61 @@ implements TemporalValueSemantics<T> {
      */
     protected Function<T, String> getRenderingFormatter(
             final ValueSemanticsProvider.Context context,
-            final UnaryOperator<String> timeZoneDecorator) {
+            final BinaryOperator<String> timeZoneDecorator) {
 
         val dateAndTimeFormatStyle = DateAndTimeFormatStyle.forContext(mmc, context);
 
         return time-> {
 
+                final var temporalNoZoneRenderingFormat = getTemporalNoZoneRenderingFormat(
+                        context, temporalCharacteristic, offsetCharacteristic,
+                        dateAndTimeFormatStyle.getDateFormatStyle(),
+                        dateAndTimeFormatStyle.getTimeFormatStyle());
+
+                final var temporalZoneOnlyRenderingFormat = getTemporalZoneOnlyRenderingFormat(
+                        context, temporalCharacteristic, offsetCharacteristic);
+
                 final var sb = new StringBuffer();
 
-                sb.append(
-                    getTemporalNoZoneRenderingFormat(
-                            context, temporalCharacteristic, offsetCharacteristic,
-                            dateAndTimeFormatStyle.getDateFormatStyle(),
-                            dateAndTimeFormatStyle.getTimeFormatStyle())
-                    .format(time));
+                sb.append(temporalNoZoneRenderingFormat.format(time));
 
-                getTemporalZoneOnlyRenderingFormat(
-                        context, temporalCharacteristic, offsetCharacteristic)
+                temporalZoneOnlyRenderingFormat
                 .ifPresent(zoneOnlyFormat->{
-                    sb.append(' ').append(timeZoneDecorator.apply(zoneOnlyFormat.format(time)));
+                    sb.append(' ').append(timeZoneDecorator.apply(
+                            zoneOnlyFormat.format(time),
+                            translate("your local time: ")
+                                + " "
+                                + temporalNoZoneRenderingFormat.format(toLocalTime(context, time))));
                 });
 
                 return sb.toString();
         };
     }
 
+    /**
+     * Converts given {@link Temporal} when offset,
+     * to a temporal that is local to the user's (client's) time-zone.
+     * In other words, this conversion preserves the time {@link Instant}.
+     */
+    private Temporal toLocalTime(final ValueSemanticsProvider.Context context, final Temporal t) {
+        if(t instanceof ZonedDateTime) {
+            return LocalDateTime.ofInstant(((ZonedDateTime) t).toInstant(),
+                    context.getInteractionContext().getTimeZone());
+        }
+        if(t instanceof OffsetDateTime) {
+            return LocalDateTime.ofInstant(((OffsetDateTime) t).toInstant(),
+                    context.getInteractionContext().getTimeZone());
+        }
+        if(t instanceof OffsetTime) {
+            return ((OffsetTime) t)
+                    // convert to 'user time'
+                    .withOffsetSameInstant(context.getInteractionContext().getTimeZoneOffsetNow())
+                    // remove offset information
+                    .toLocalTime();
+        }
+        return t;
+    }
+
     /**
      * Format used for rendering editable text representation.
      */