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