001package arez.dom;
002
003import akasha.EventListener;
004import akasha.WindowGlobal;
005import arez.ComputableValue;
006import arez.annotations.Action;
007import arez.annotations.ArezComponent;
008import arez.annotations.ComputableValueRef;
009import arez.annotations.DepType;
010import arez.annotations.Feature;
011import arez.annotations.Memoize;
012import arez.annotations.Observable;
013import arez.annotations.OnActivate;
014import arez.annotations.OnDeactivate;
015import java.util.Date;
016import javax.annotation.Nonnull;
017
018/**
019 * An observable model that declares state that tracks when the user is "online".
020 * The online state is essentially a reflection of the browsers "navigator.onLine"
021 * value. If an observer is observing the model, the model listens for changes from
022 * the browser and updates the online state as appropriate. However if there is no
023 * observer for the state, the model will not listen to to the browser events so as
024 * not to have any significant performance impact.
025 *
026 * <p>A very simple example</p>
027 * <pre>{@code
028 * import com.google.gwt.core.client.EntryPoint;
029 * import akasha.Global;
030 * import akasha.Console;
031 * import arez.Arez;
032 * import arez.networkstatus.NetworkStatus;
033 *
034 * public class NetworkStatusExample
035 *   implements EntryPoint
036 * {
037 *   public void onModuleLoad()
038 *   {
039 *     final NetworkStatus networkStatus = NetworkStatus.create();
040 *     Arez.context().observer( () -> {
041 *       Console.log( "Network Status: " + ( networkStatus.isOnLine() ? "Online" : "Offline" ) );
042 *       if ( networkStatus.isOffLine() )
043 *       {
044 *         Console.log( "Offline since: " + networkStatus.getLastChangedAt() );
045 *       }
046 *     } );
047 *   }
048 * }
049 * }</pre>
050 */
051@ArezComponent( requireId = Feature.DISABLE )
052public abstract class NetworkStatus
053{
054  @Nonnull
055  private final EventListener _listener = e -> updateOnlineStatus( getOnLineComputableValue() );
056
057  /**
058   * Create an instance of NetworkStatus.
059   *
060   * @return the NetworkStatus instance.
061   */
062  @Nonnull
063  public static NetworkStatus create()
064  {
065    return new Arez_NetworkStatus( new Date() );
066  }
067
068  NetworkStatus()
069  {
070  }
071
072  /**
073   * Return true if the browser is offline, false otherwise.
074   *
075   * @return true if the browser is offline, false otherwise.
076   */
077  public boolean isOffLine()
078  {
079    return !isOnLine();
080  }
081
082  /**
083   * Return true if the browser is online, false otherwise.
084   *
085   * @return true if the browser is online, false otherwise.
086   */
087  @Memoize( depType = DepType.AREZ_OR_EXTERNAL )
088  public boolean isOnLine()
089  {
090    return WindowGlobal.navigator().onLine();
091  }
092
093  /**
094   * Return the last time at which online status changed.
095   * This will default to the time the component was created, otherwise
096   * the time at which the online status was changed.
097   *
098   * @return the last time at which online status changed.
099   */
100  @Observable
101  @Nonnull
102  public abstract Date getLastChangedAt();
103
104  abstract void setLastChangedAt( @Nonnull Date lastChangedAt );
105
106  @ComputableValueRef
107  abstract ComputableValue<?> getOnLineComputableValue();
108
109  @OnActivate
110  void onOnLineActivate()
111  {
112    WindowGlobal.addOnlineListener( _listener );
113    WindowGlobal.addOfflineListener( _listener );
114  }
115
116  @OnDeactivate
117  void onOnLineDeactivate()
118  {
119    WindowGlobal.removeOnlineListener( _listener );
120    WindowGlobal.removeOfflineListener( _listener );
121  }
122
123  @Action
124  void updateOnlineStatus( @Nonnull final ComputableValue<?> computableValue )
125  {
126    computableValue.reportPossiblyChanged();
127    setLastChangedAt( new Date() );
128  }
129}