diff --git a/.gitignore b/.gitignore index 14fe5b7..4c55524 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /dist /tmp /out-tsc +/tests # Only exists if Bazel was run /bazel-out diff --git a/docs/compodoc/interceptors/ConnectionInterceptor.html b/docs/compodoc/interceptors/ConnectionInterceptor.html new file mode 100644 index 0000000..ec12b46 --- /dev/null +++ b/docs/compodoc/interceptors/ConnectionInterceptor.html @@ -0,0 +1,371 @@ +<!doctype html> +<html class="no-js" lang=""> + <head> + <meta charset="utf-8"> + <meta http-equiv="x-ua-compatible" content="ie=edge"> + <title>CICADA</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <link rel="icon" type="image/x-icon" href="../images/favicon.ico"> + <link rel="stylesheet" href="../styles/style.css"> + </head> + <body> + + <div class="navbar navbar-default navbar-fixed-top visible-xs"> + <a href="../" class="navbar-brand">CICADA</a> + <button type="button" class="btn btn-default btn-menu ion-ios-menu" id="btn-menu"></button> + </div> + + <div class="xs-menu menu" id="mobile-menu"> + <div id="book-search-input" role="search"><input type="text" placeholder="Type to search"></div> <compodoc-menu></compodoc-menu> + </div> + + <div class="container-fluid main"> + <div class="row main"> + <div class="hidden-xs menu"> + <compodoc-menu mode="normal"></compodoc-menu> + </div> + <!-- START CONTENT --> + <div class="content interceptor"> + <div class="content-data"> + + + + + + + + +<ol class="breadcrumb"> + <li>Interceptors</li> + <li>ConnectionInterceptor</li> +</ol> + +<ul class="nav nav-tabs" role="tablist"> + <li class="active"> + <a href="#info" role="tab" id="info-tab" data-toggle="tab" data-link="info">Info</a> + </li> + <li > + <a href="#source" role="tab" id="source-tab" data-toggle="tab" data-link="source">Source</a> + </li> +</ul> + +<div class="tab-content"> + <div class="tab-pane fade active in" id="c-info"> + <p class="comment"> + <h3>File</h3> + </p> + <p class="comment"> + <code>src/app/_interceptors/connection.interceptor.ts</code> + </p> + + <p class="comment"> + <h3>Description</h3> + </p> + <p class="comment"> + <p>Intercepts and handles of events from outgoing HTTP request. </p> + + </p> + + + <section> + <h3 id="index">Index</h3> + <table class="table table-sm table-bordered index-table"> + <tbody> + + <tr> + <td class="col-md-4"> + <h6><b>Methods</b></h6> + </td> + </tr> + <tr> + <td class="col-md-4"> + <ul class="index-list"> + <li> + <a href="#intercept">intercept</a> + </li> + </ul> + </td> + </tr> + + + + + + </tbody> + </table> +</section> + + <section> + <h3 id="constructor">Constructor</h3> + <table class="table table-sm table-bordered"> + <tbody> + <tr> + <td class="col-md-4"> +<code>constructor(loggingService: <a href="../injectables/LoggingService.html">LoggingService</a>)</code> + </td> + </tr> + <tr> + <td class="col-md-4"> + <div class="io-line">Defined in <a href="" data-line="14" class="link-to-prism">src/app/_interceptors/connection.interceptor.ts:14</a></div> + </td> + </tr> + + <tr> + <td class="col-md-4"> + <div class="io-description"><p>Initialization of the connection interceptor.</p> +</div> + <div> + <b>Parameters :</b> + <table class="params"> + <thead> + <tr> + <td>Name</td> + <td>Type</td> + <td>Optional</td> + <td>Description</td> + </tr> + </thead> + <tbody> + <tr> + <td>loggingService</td> + + <td> + <code><a href="../injectables/LoggingService.html" target="_self" >LoggingService</a></code> + </td> + + <td> + No + </td> + + <td> + <code><ul> +<li>A service that provides logging capabilities.</li> +</ul> +</code> + </td> + </tr> + </tbody> + </table> + </div> + </td> + </tr> + </tbody> + </table> +</section> + + <section> + + <h3 id="methods"> + Methods + </h3> + <table class="table table-sm table-bordered"> + <tbody> + <tr> + <td class="col-md-4"> + <a name="intercept"></a> + <span class="name"> + <b> + intercept + </b> + <a href="#intercept"><span class="icon ion-ios-link"></span></a> + </span> + </td> + </tr> + <tr> + <td class="col-md-4"> +<code>intercept(request: HttpRequest<unknown>, next: <a href="https://angular.io/api/common/http/HttpHandler" target="_blank">HttpHandler</a>)</code> + </td> + </tr> + + + <tr> + <td class="col-md-4"> + <div class="io-line">Defined in <a href="" data-line="29" + class="link-to-prism">src/app/_interceptors/connection.interceptor.ts:29</a></div> + </td> + </tr> + + + <tr> + <td class="col-md-4"> + <div class="io-description"><p>Intercepts HTTP requests.</p> +</div> + + <div class="io-description"> + <b>Parameters :</b> + <table class="params"> + <thead> + <tr> + <td>Name</td> + <td>Type</td> + <td>Optional</td> + <td>Description</td> + </tr> + </thead> + <tbody> + <tr> + <td>request</td> + <td> + <code>HttpRequest<unknown></code> + </td> + + <td> + No + </td> + + + <td> + <ul> +<li>An outgoing HTTP request with an optional typed body.</li> +</ul> + + </td> + </tr> + <tr> + <td>next</td> + <td> + <code><a href="https://angular.io/api/common/http/HttpHandler" target="_blank" >HttpHandler</a></code> + </td> + + <td> + No + </td> + + + <td> + <ul> +<li>The next HTTP handler or the outgoing request dispatcher.</li> +</ul> + + </td> + </tr> + </tbody> + </table> + </div> + <div> + </div> + <div class="io-description"> + <b>Returns : </b> <code>Observable<HttpEvent<unknown>></code> + + </div> + <div class="io-description"> + <p>The forwarded request.</p> + + </div> + </td> + </tr> + </tbody> + </table> +</section> + + </div> + + + <div class="tab-pane fade tab-source-code" id="c-source"> + <pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +// Third party imports +import { Observable } from 'rxjs'; + +// Application imports +import { LoggingService } from '@app/_services/logging.service'; +import { checkOnlineStatus } from '@app/_helpers'; + +/** Intercepts and handles of events from outgoing HTTP request. */ +@Injectable() +export class ConnectionInterceptor implements HttpInterceptor { + /** + * Initialization of the connection interceptor. + * + * @param loggingService - A service that provides logging capabilities. + */ + constructor(private loggingService: LoggingService) {} + + /** + * Intercepts HTTP requests. + * + * @param request - An outgoing HTTP request with an optional typed body. + * @param next - The next HTTP handler or the outgoing request dispatcher. + * @returns The forwarded request. + */ + intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { + checkOnlineStatus().then((online) => { + if (!online) { + this.loggingService.sendErrorLevelMessage('No internet connection on device!', this, { + error: `NetworkError when attempting to fetch resource ${request.url}.`, + }); + return; + } else { + return next.handle(request); + } + }); + return next.handle(request); + } +} +</code></pre> + </div> +</div> + + + + + + + + + + + + </div><div class="search-results"> + <div class="has-results"> + <h1 class="search-results-title"><span class='search-results-count'></span> result-matching "<span class='search-query'></span>"</h1> + <ul class="search-results-list"></ul> + </div> + <div class="no-results"> + <h1 class="search-results-title">No results matching "<span class='search-query'></span>"</h1> + </div> +</div> +</div> + <!-- END CONTENT --> + </div> + </div> + + <script> + var COMPODOC_CURRENT_PAGE_DEPTH = 1; + var COMPODOC_CURRENT_PAGE_CONTEXT = 'interceptor'; + var COMPODOC_CURRENT_PAGE_URL = 'ConnectionInterceptor.html'; + var MAX_SEARCH_RESULTS = 15; + </script> + + <script src="../js/libs/custom-elements.min.js"></script> + <script src="../js/libs/lit-html.js"></script> + <!-- Required to polyfill modern browsers as code is ES5 for IE... --> + <script src="../js/libs/custom-elements-es5-adapter.js" charset="utf-8" defer></script> + <script src="../js/menu-wc.js" defer></script> + + <script src="../js/libs/bootstrap-native.js"></script> + + <script src="../js/libs/es6-shim.min.js"></script> + <script src="../js/libs/EventDispatcher.js"></script> + <script src="../js/libs/promise.min.js"></script> + <script src="../js/libs/zepto.min.js"></script> + + <script src="../js/compodoc.js"></script> + + <script src="../js/tabs.js"></script> + <script src="../js/menu.js"></script> + <script src="../js/libs/clipboard.min.js"></script> + <script src="../js/libs/prism.js"></script> + <script src="../js/sourceCode.js"></script> + <script src="../js/search/search.js"></script> + <script src="../js/search/lunr.min.js"></script> + <script src="../js/search/search-lunr.js"></script> + <script src="../js/search/search_index.js"></script> + <script src="../js/lazy-load-graphs.js"></script> + + + </body> +</html> diff --git a/docs/typedoc/classes/app__interceptors_connection_interceptor.connectioninterceptor.html b/docs/typedoc/classes/app__interceptors_connection_interceptor.connectioninterceptor.html new file mode 100644 index 0000000..3eef76d --- /dev/null +++ b/docs/typedoc/classes/app__interceptors_connection_interceptor.connectioninterceptor.html @@ -0,0 +1,246 @@ +<!doctype html> +<html class="default no-js"> +<head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <title>ConnectionInterceptor | CICADA</title> + <meta name="description" content="Documentation for CICADA"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="../assets/css/main.css"> + <script async src="../assets/js/search.js" id="search-script"></script> +</head> +<body> +<header> + <div class="tsd-page-toolbar"> + <div class="container"> + <div class="table-wrap"> + <div class="table-cell" id="tsd-search" data-index="../assets/js/search.json" data-base=".."> + <div class="field"> + <label for="tsd-search-field" class="tsd-widget search no-caption">Search</label> + <input id="tsd-search-field" type="text" /> + </div> + <ul class="results"> + <li class="state loading">Preparing search index...</li> + <li class="state failure">The search index is not available</li> + </ul> + <a href="../index.html" class="title">CICADA</a> + </div> + <div class="table-cell" id="tsd-widgets"> + <div id="tsd-filter"> + <a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a> + <div class="tsd-filter-group"> + <div class="tsd-select" id="tsd-filter-visibility"> + <span class="tsd-select-label">All</span> + <ul class="tsd-select-list"> + <li data-value="public">Public</li> + <li data-value="protected">Public/Protected</li> + <li data-value="private" class="selected">All</li> + </ul> + </div> + <input type="checkbox" id="tsd-filter-inherited" checked /> + <label class="tsd-widget" for="tsd-filter-inherited">Inherited</label> + <input type="checkbox" id="tsd-filter-externals" checked /> + <label class="tsd-widget" for="tsd-filter-externals">Externals</label> + </div> + </div> + <a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a> + </div> + </div> + </div> + </div> + <div class="tsd-page-title"> + <div class="container"> + <ul class="tsd-breadcrumb"> + <li> + <a href="../modules.html">CICADA</a> + </li> + <li> + <a href="../modules/app__interceptors_connection_interceptor.html">app/_interceptors/connection.interceptor</a> + </li> + <li> + <a href="app__interceptors_connection_interceptor.connectioninterceptor.html">ConnectionInterceptor</a> + </li> + </ul> + <h1>Class ConnectionInterceptor</h1> + </div> + </div> +</header> +<div class="container container-main"> + <div class="row"> + <div class="col-8 col-content"> + <section class="tsd-panel tsd-comment"> + <div class="tsd-comment tsd-typography"> + <div class="lead"> + <p>Intercepts and handles of events from outgoing HTTP request.</p> + </div> + </div> + </section> + <section class="tsd-panel tsd-hierarchy"> + <h3>Hierarchy</h3> + <ul class="tsd-hierarchy"> + <li> + <span class="target">ConnectionInterceptor</span> + </li> + </ul> + </section> + <section class="tsd-panel"> + <h3>Implements</h3> + <ul class="tsd-hierarchy"> + <li><span class="tsd-signature-type">HttpInterceptor</span></li> + </ul> + </section> + <section class="tsd-panel-group tsd-index-group"> + <h2>Index</h2> + <section class="tsd-panel tsd-index-panel"> + <div class="tsd-index-content"> + <section class="tsd-index-section "> + <h3>Constructors</h3> + <ul class="tsd-index-list"> + <li class="tsd-kind-constructor tsd-parent-kind-class"><a href="app__interceptors_connection_interceptor.connectioninterceptor.html#constructor" class="tsd-kind-icon">constructor</a></li> + </ul> + </section> + <section class="tsd-index-section "> + <h3>Methods</h3> + <ul class="tsd-index-list"> + <li class="tsd-kind-method tsd-parent-kind-class"><a href="app__interceptors_connection_interceptor.connectioninterceptor.html#intercept" class="tsd-kind-icon">intercept</a></li> + </ul> + </section> + </div> + </section> + </section> + <section class="tsd-panel-group tsd-member-group "> + <h2>Constructors</h2> + <section class="tsd-panel tsd-member tsd-kind-constructor tsd-parent-kind-class"> + <a name="constructor" class="tsd-anchor"></a> + <h3>constructor</h3> + <ul class="tsd-signatures tsd-kind-constructor tsd-parent-kind-class"> + <li class="tsd-signature tsd-kind-icon">new <wbr>Connection<wbr>Interceptor<span class="tsd-signature-symbol">(</span>loggingService<span class="tsd-signature-symbol">: </span><a href="app__services_logging_service.loggingservice.html" class="tsd-signature-type" data-tsd-kind="Class">LoggingService</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><a href="app__interceptors_connection_interceptor.connectioninterceptor.html" class="tsd-signature-type" data-tsd-kind="Class">ConnectionInterceptor</a></li> + </ul> + <ul class="tsd-descriptions"> + <li class="tsd-description"> + <aside class="tsd-sources"> + <ul> + <li>Defined in src/app/_interceptors/connection.interceptor.ts:14</li> + </ul> + </aside> + <div class="tsd-comment tsd-typography"> + <div class="lead"> + <p>Initialization of the connection interceptor.</p> + </div> + </div> + <h4 class="tsd-parameters-title">Parameters</h4> + <ul class="tsd-parameters"> + <li> + <h5>loggingService: <a href="app__services_logging_service.loggingservice.html" class="tsd-signature-type" data-tsd-kind="Class">LoggingService</a></h5> + <div class="tsd-comment tsd-typography"> + <p>A service that provides logging capabilities.</p> + </div> + </li> + </ul> + <h4 class="tsd-returns-title">Returns <a href="app__interceptors_connection_interceptor.connectioninterceptor.html" class="tsd-signature-type" data-tsd-kind="Class">ConnectionInterceptor</a></h4> + </li> + </ul> + </section> + </section> + <section class="tsd-panel-group tsd-member-group "> + <h2>Methods</h2> + <section class="tsd-panel tsd-member tsd-kind-method tsd-parent-kind-class"> + <a name="intercept" class="tsd-anchor"></a> + <h3>intercept</h3> + <ul class="tsd-signatures tsd-kind-method tsd-parent-kind-class"> + <li class="tsd-signature tsd-kind-icon">intercept<span class="tsd-signature-symbol">(</span>request<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">HttpRequest</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">></span>, next<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">HttpHandler</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">Observable</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">HttpEvent</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></li> + </ul> + <ul class="tsd-descriptions"> + <li class="tsd-description"> + <aside class="tsd-sources"> + <p>Implementation of HttpInterceptor.intercept</p> + <ul> + <li>Defined in src/app/_interceptors/connection.interceptor.ts:29</li> + </ul> + </aside> + <div class="tsd-comment tsd-typography"> + <div class="lead"> + <p>Intercepts HTTP requests.</p> + </div> + </div> + <h4 class="tsd-parameters-title">Parameters</h4> + <ul class="tsd-parameters"> + <li> + <h5>request: <span class="tsd-signature-type">HttpRequest</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">></span></h5> + <div class="tsd-comment tsd-typography"> + <p>An outgoing HTTP request with an optional typed body.</p> + </div> + </li> + <li> + <h5>next: <span class="tsd-signature-type">HttpHandler</span></h5> + <div class="tsd-comment tsd-typography"> + <p>The next HTTP handler or the outgoing request dispatcher.</p> + </div> + </li> + </ul> + <h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Observable</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">HttpEvent</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4> + <p>The forwarded request.</p> + </li> + </ul> + </section> + </section> + </div> + <div class="col-4 col-menu menu-sticky-wrap menu-highlight"> + <nav class="tsd-navigation primary"> + <ul> + <li class=" "> + <a href="../modules.html">Exports</a> + </li> + <li class="current tsd-kind-module"> + <a href="../modules/app__interceptors_connection_interceptor.html">app/_<wbr>interceptors/connection.interceptor</a> + </li> + </ul> + </nav> + <nav class="tsd-navigation secondary menu-sticky"> + <ul class="before-current"> + </ul> + <ul class="current"> + <li class="current tsd-kind-class tsd-parent-kind-module"> + <a href="app__interceptors_connection_interceptor.connectioninterceptor.html" class="tsd-kind-icon">Connection<wbr>Interceptor</a> + <ul> + <li class=" tsd-kind-constructor tsd-parent-kind-class"> + <a href="app__interceptors_connection_interceptor.connectioninterceptor.html#constructor" class="tsd-kind-icon">constructor</a> + </li> + <li class=" tsd-kind-method tsd-parent-kind-class"> + <a href="app__interceptors_connection_interceptor.connectioninterceptor.html#intercept" class="tsd-kind-icon">intercept</a> + </li> + </ul> + </li> + </ul> + <ul class="after-current"> + </ul> + </nav> + </div> + </div> +</div> +<footer class="with-border-bottom"> + <div class="container"> + <h2>Legend</h2> + <div class="tsd-legend-group"> + <ul class="tsd-legend"> + <li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li> + <li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li> + <li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li> + </ul> + <ul class="tsd-legend"> + <li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li> + <li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li> + </ul> + <ul class="tsd-legend"> + <li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li> + </ul> + </div> + </div> +</footer> +<div class="container tsd-generator"> + <p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p> +</div> +<div class="overlay"></div> +<script src="../assets/js/main.js"></script> +</body> +</html> \ No newline at end of file diff --git a/docs/typedoc/modules/app__helpers_online_status.html b/docs/typedoc/modules/app__helpers_online_status.html new file mode 100644 index 0000000..e876cae --- /dev/null +++ b/docs/typedoc/modules/app__helpers_online_status.html @@ -0,0 +1,146 @@ +<!doctype html> +<html class="default no-js"> +<head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <title>app/_helpers/online-status | CICADA</title> + <meta name="description" content="Documentation for CICADA"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="../assets/css/main.css"> + <script async src="../assets/js/search.js" id="search-script"></script> +</head> +<body> +<header> + <div class="tsd-page-toolbar"> + <div class="container"> + <div class="table-wrap"> + <div class="table-cell" id="tsd-search" data-index="../assets/js/search.json" data-base=".."> + <div class="field"> + <label for="tsd-search-field" class="tsd-widget search no-caption">Search</label> + <input id="tsd-search-field" type="text" /> + </div> + <ul class="results"> + <li class="state loading">Preparing search index...</li> + <li class="state failure">The search index is not available</li> + </ul> + <a href="../index.html" class="title">CICADA</a> + </div> + <div class="table-cell" id="tsd-widgets"> + <div id="tsd-filter"> + <a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a> + <div class="tsd-filter-group"> + <div class="tsd-select" id="tsd-filter-visibility"> + <span class="tsd-select-label">All</span> + <ul class="tsd-select-list"> + <li data-value="public">Public</li> + <li data-value="protected">Public/Protected</li> + <li data-value="private" class="selected">All</li> + </ul> + </div> + <input type="checkbox" id="tsd-filter-inherited" checked /> + <label class="tsd-widget" for="tsd-filter-inherited">Inherited</label> + <input type="checkbox" id="tsd-filter-externals" checked /> + <label class="tsd-widget" for="tsd-filter-externals">Externals</label> + </div> + </div> + <a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a> + </div> + </div> + </div> + </div> + <div class="tsd-page-title"> + <div class="container"> + <ul class="tsd-breadcrumb"> + <li> + <a href="../modules.html">CICADA</a> + </li> + <li> + <a href="app__helpers_online_status.html">app/_helpers/online-status</a> + </li> + </ul> + <h1>Module app/_helpers/online-status</h1> + </div> + </div> +</header> +<div class="container container-main"> + <div class="row"> + <div class="col-8 col-content"> + <section class="tsd-panel-group tsd-index-group"> + <h2>Index</h2> + <section class="tsd-panel tsd-index-panel"> + <div class="tsd-index-content"> + <section class="tsd-index-section "> + <h3>Functions</h3> + <ul class="tsd-index-list"> + <li class="tsd-kind-function tsd-parent-kind-module"><a href="app__helpers_online_status.html#checkonlinestatus" class="tsd-kind-icon">check<wbr>Online<wbr>Status</a></li> + </ul> + </section> + </div> + </section> + </section> + <section class="tsd-panel-group tsd-member-group "> + <h2>Functions</h2> + <section class="tsd-panel tsd-member tsd-kind-function tsd-parent-kind-module"> + <a name="checkonlinestatus" class="tsd-anchor"></a> + <h3>check<wbr>Online<wbr>Status</h3> + <ul class="tsd-signatures tsd-kind-function tsd-parent-kind-module"> + <li class="tsd-signature tsd-kind-icon">check<wbr>Online<wbr>Status<span class="tsd-signature-symbol">(</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">boolean</span><span class="tsd-signature-symbol">></span></li> + </ul> + <ul class="tsd-descriptions"> + <li class="tsd-description"> + <aside class="tsd-sources"> + <ul> + <li>Defined in src/app/_helpers/online-status.ts:6</li> + </ul> + </aside> + <h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">boolean</span><span class="tsd-signature-symbol">></span></h4> + </li> + </ul> + </section> + </section> + </div> + <div class="col-4 col-menu menu-sticky-wrap menu-highlight"> + <nav class="tsd-navigation primary"> + <ul> + <li class=" "> + <a href="../modules.html">Exports</a> + </li> + <li class="current tsd-kind-module"> + <a href="app__helpers_online_status.html">app/_<wbr>helpers/online-<wbr>status</a> + </li> + </ul> + </nav> + <nav class="tsd-navigation secondary menu-sticky"> + <ul class="before-current"> + <li class=" tsd-kind-function tsd-parent-kind-module"> + <a href="app__helpers_online_status.html#checkonlinestatus" class="tsd-kind-icon">check<wbr>Online<wbr>Status</a> + </li> + </ul> + </nav> + </div> + </div> +</div> +<footer class="with-border-bottom"> + <div class="container"> + <h2>Legend</h2> + <div class="tsd-legend-group"> + <ul class="tsd-legend"> + <li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li> + <li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li> + </ul> + <ul class="tsd-legend"> + <li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li> + </ul> + <ul class="tsd-legend"> + <li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li> + </ul> + </div> + </div> +</footer> +<div class="container tsd-generator"> + <p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p> +</div> +<div class="overlay"></div> +<script src="../assets/js/main.js"></script> +</body> +</html> \ No newline at end of file diff --git a/docs/typedoc/modules/app__interceptors_connection_interceptor.html b/docs/typedoc/modules/app__interceptors_connection_interceptor.html new file mode 100644 index 0000000..5097c4d --- /dev/null +++ b/docs/typedoc/modules/app__interceptors_connection_interceptor.html @@ -0,0 +1,126 @@ +<!doctype html> +<html class="default no-js"> +<head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <title>app/_interceptors/connection.interceptor | CICADA</title> + <meta name="description" content="Documentation for CICADA"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="../assets/css/main.css"> + <script async src="../assets/js/search.js" id="search-script"></script> +</head> +<body> +<header> + <div class="tsd-page-toolbar"> + <div class="container"> + <div class="table-wrap"> + <div class="table-cell" id="tsd-search" data-index="../assets/js/search.json" data-base=".."> + <div class="field"> + <label for="tsd-search-field" class="tsd-widget search no-caption">Search</label> + <input id="tsd-search-field" type="text" /> + </div> + <ul class="results"> + <li class="state loading">Preparing search index...</li> + <li class="state failure">The search index is not available</li> + </ul> + <a href="../index.html" class="title">CICADA</a> + </div> + <div class="table-cell" id="tsd-widgets"> + <div id="tsd-filter"> + <a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a> + <div class="tsd-filter-group"> + <div class="tsd-select" id="tsd-filter-visibility"> + <span class="tsd-select-label">All</span> + <ul class="tsd-select-list"> + <li data-value="public">Public</li> + <li data-value="protected">Public/Protected</li> + <li data-value="private" class="selected">All</li> + </ul> + </div> + <input type="checkbox" id="tsd-filter-inherited" checked /> + <label class="tsd-widget" for="tsd-filter-inherited">Inherited</label> + <input type="checkbox" id="tsd-filter-externals" checked /> + <label class="tsd-widget" for="tsd-filter-externals">Externals</label> + </div> + </div> + <a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a> + </div> + </div> + </div> + </div> + <div class="tsd-page-title"> + <div class="container"> + <ul class="tsd-breadcrumb"> + <li> + <a href="../modules.html">CICADA</a> + </li> + <li> + <a href="app__interceptors_connection_interceptor.html">app/_interceptors/connection.interceptor</a> + </li> + </ul> + <h1>Module app/_interceptors/connection.interceptor</h1> + </div> + </div> +</header> +<div class="container container-main"> + <div class="row"> + <div class="col-8 col-content"> + <section class="tsd-panel-group tsd-index-group"> + <h2>Index</h2> + <section class="tsd-panel tsd-index-panel"> + <div class="tsd-index-content"> + <section class="tsd-index-section "> + <h3>Classes</h3> + <ul class="tsd-index-list"> + <li class="tsd-kind-class tsd-parent-kind-module"><a href="../classes/app__interceptors_connection_interceptor.connectioninterceptor.html" class="tsd-kind-icon">Connection<wbr>Interceptor</a></li> + </ul> + </section> + </div> + </section> + </section> + </div> + <div class="col-4 col-menu menu-sticky-wrap menu-highlight"> + <nav class="tsd-navigation primary"> + <ul> + <li class=" "> + <a href="../modules.html">Exports</a> + </li> + <li class="current tsd-kind-module"> + <a href="app__interceptors_connection_interceptor.html">app/_<wbr>interceptors/connection.interceptor</a> + </li> + </ul> + </nav> + <nav class="tsd-navigation secondary menu-sticky"> + <ul class="before-current"> + <li class=" tsd-kind-class tsd-parent-kind-module"> + <a href="../classes/app__interceptors_connection_interceptor.connectioninterceptor.html" class="tsd-kind-icon">Connection<wbr>Interceptor</a> + </li> + </ul> + </nav> + </div> + </div> +</div> +<footer class="with-border-bottom"> + <div class="container"> + <h2>Legend</h2> + <div class="tsd-legend-group"> + <ul class="tsd-legend"> + <li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li> + <li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li> + </ul> + <ul class="tsd-legend"> + <li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li> + </ul> + <ul class="tsd-legend"> + <li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li> + </ul> + </div> + </div> +</footer> +<div class="container tsd-generator"> + <p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p> +</div> +<div class="overlay"></div> +<script src="../assets/js/main.js"></script> +</body> +</html> \ No newline at end of file diff --git a/src/app/_helpers/index.ts b/src/app/_helpers/index.ts index 82fdb87..c1f744b 100644 --- a/src/app/_helpers/index.ts +++ b/src/app/_helpers/index.ts @@ -9,3 +9,4 @@ export * from '@app/_helpers/mock-backend'; export * from '@app/_helpers/read-csv'; export * from '@app/_helpers/schema-validation'; export * from '@app/_helpers/sync'; +export * from '@app/_helpers/online-status'; diff --git a/src/app/_helpers/online-status.ts b/src/app/_helpers/online-status.ts new file mode 100644 index 0000000..2655e23 --- /dev/null +++ b/src/app/_helpers/online-status.ts @@ -0,0 +1,15 @@ +const apiUrls = [ + 'https://api.coindesk.com/v1/bpi/currentprice.json', + 'https://dog.ceo/api/breeds/image/random', +]; + +async function checkOnlineStatus(): Promise<boolean> { + try { + const online = await fetch(apiUrls[Math.floor(Math.random() * apiUrls.length)]); + return online.status >= 200 && online.status < 300; + } catch (error) { + return false; + } +} + +export { checkOnlineStatus }; diff --git a/src/app/_interceptors/connection.interceptor.spec.ts b/src/app/_interceptors/connection.interceptor.spec.ts new file mode 100644 index 0000000..465d252 --- /dev/null +++ b/src/app/_interceptors/connection.interceptor.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ConnectionInterceptor } from './connection.interceptor'; + +describe('ConnectionInterceptor', () => { + beforeEach(() => + TestBed.configureTestingModule({ + providers: [ConnectionInterceptor], + }) + ); + + it('should be created', () => { + const interceptor: ConnectionInterceptor = TestBed.inject(ConnectionInterceptor); + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/_interceptors/connection.interceptor.ts b/src/app/_interceptors/connection.interceptor.ts new file mode 100644 index 0000000..2116add --- /dev/null +++ b/src/app/_interceptors/connection.interceptor.ts @@ -0,0 +1,42 @@ +// Core imports +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +// Third party imports +import { Observable } from 'rxjs'; + +// Application imports +import { LoggingService } from '@app/_services/logging.service'; +import { checkOnlineStatus } from '@app/_helpers'; + +/** Intercepts and handles of events from outgoing HTTP request. */ +@Injectable() +export class ConnectionInterceptor implements HttpInterceptor { + /** + * Initialization of the connection interceptor. + * + * @param loggingService - A service that provides logging capabilities. + */ + constructor(private loggingService: LoggingService) {} + + /** + * Intercepts HTTP requests. + * + * @param request - An outgoing HTTP request with an optional typed body. + * @param next - The next HTTP handler or the outgoing request dispatcher. + * @returns The forwarded request. + */ + intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { + checkOnlineStatus().then((online) => { + if (!online) { + this.loggingService.sendErrorLevelMessage('No internet connection on device!', this, { + error: `NetworkError when attempting to fetch resource ${request.url}.`, + }); + return; + } else { + return next.handle(request); + } + }); + return next.handle(request); + } +} diff --git a/src/app/_interceptors/error.interceptor.ts b/src/app/_interceptors/error.interceptor.ts index e189d9b..4e2deea 100644 --- a/src/app/_interceptors/error.interceptor.ts +++ b/src/app/_interceptors/error.interceptor.ts @@ -59,9 +59,10 @@ export class ErrorInterceptor implements HttpInterceptor { this.router.navigateByUrl('/auth').then(); break; case 403: // forbidden - this.errorDialogService.openDialog( - { message: 'Access to resource is not allowed (Error 403)'}) - //alert('Access to resource is not allowed!'); + this.errorDialogService.openDialog({ + message: 'Access to resource is not allowed (Error 403)', + }); + // alert('Access to resource is not allowed!'); break; } // Return an observable with a user-facing error message. diff --git a/src/app/_interceptors/index.ts b/src/app/_interceptors/index.ts index a9ce065..a3fb1f1 100644 --- a/src/app/_interceptors/index.ts +++ b/src/app/_interceptors/index.ts @@ -1,3 +1,4 @@ +export * from '@app/_interceptors/connection.interceptor'; export * from '@app/_interceptors/error.interceptor'; export * from '@app/_interceptors/http-config.interceptor'; export * from '@app/_interceptors/logging.interceptor'; diff --git a/src/app/_services/auth.service.ts b/src/app/_services/auth.service.ts index 53e3d13..9342b11 100644 --- a/src/app/_services/auth.service.ts +++ b/src/app/_services/auth.service.ts @@ -5,7 +5,6 @@ import { environment } from '@src/environments/environment'; import { LoggingService } from '@app/_services/logging.service'; import { MutableKeyStore } from '@app/_pgp'; import { ErrorDialogService } from '@app/_services/error-dialog.service'; -import { HttpClient } from '@angular/common/http'; import { HttpError, rejectBody } from '@app/_helpers/global-error-handler'; import { Staff } from '@app/_models'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -23,7 +22,6 @@ export class AuthService { trustedUsersSubject: Observable<Array<Staff>> = this.trustedUsersList.asObservable(); constructor( - private httpClient: HttpClient, private loggingService: LoggingService, private errorDialogService: ErrorDialogService ) {} @@ -48,7 +46,7 @@ export class AuthService { } getWithToken(): Promise<boolean> { - const sessionToken = this.getSessionToken() + const sessionToken = this.getSessionToken(); const headers = { Authorization: 'Bearer ' + sessionToken, 'Content-Type': 'application/json;charset=utf-8', @@ -94,33 +92,32 @@ export class AuthService { async login(): Promise<boolean> { if (this.getSessionToken()) { sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); - } + } const o = await this.getChallenge(); const r = await signChallenge( - o.challenge, - o.realm, - environment.cicMetaUrl, - this.mutableKeyStore + o.challenge, + o.realm, + environment.cicMetaUrl, + this.mutableKeyStore ); - const tokenResponse = await this.sendSignedChallenge(r) - .then((response) => { - const token = response.headers.get('Token'); - if (token) { - return token; - } - if (response.status === 401) { - throw new HttpError('You are not authorized to use this system', response.status); - } - if (!response.ok) { - throw new HttpError('Unknown error from authentication server', response.status); - } + const tokenResponse = await this.sendSignedChallenge(r).then((response) => { + const token = response.headers.get('Token'); + if (token) { + return token; + } + if (response.status === 401) { + throw new HttpError('You are not authorized to use this system', response.status); + } + if (!response.ok) { + throw new HttpError('Unknown error from authentication server', response.status); + } }); if (tokenResponse) { this.setSessionToken(tokenResponse); - //this.setState('Click button to log in'); + // this.setState('Click button to log in'); return true; } return false; diff --git a/src/app/_services/block-sync.service.ts b/src/app/_services/block-sync.service.ts index 785d452..0435ec1 100644 --- a/src/app/_services/block-sync.service.ts +++ b/src/app/_services/block-sync.service.ts @@ -4,7 +4,6 @@ import { TransactionHelper } from '@cicnet/cic-client'; import { first } from 'rxjs/operators'; import { TransactionService } from '@app/_services/transaction.service'; import { environment } from '@src/environments/environment'; -import { LoggingService } from '@app/_services/logging.service'; import { RegistryService } from '@app/_services/registry.service'; import { Web3Service } from '@app/_services/web3.service'; @@ -15,17 +14,9 @@ export class BlockSyncService { readyStateTarget: number = 2; readyState: number = 0; - constructor( - private transactionService: TransactionService, - private loggingService: LoggingService - ) {} - - async init(): Promise<void> { - await this.transactionService.init(); - } + constructor(private transactionService: TransactionService) {} async blockSync(address: string = null, offset: number = 0, limit: number = 100): Promise<void> { - this.transactionService.resetTransactionsList(); const settings: Settings = new Settings(this.scan); const readyStateElements: { network: number } = { network: 2 }; settings.w3.provider = environment.web3Provider; diff --git a/src/app/_services/location.service.ts b/src/app/_services/location.service.ts index 562952f..740cc5e 100644 --- a/src/app/_services/location.service.ts +++ b/src/app/_services/location.service.ts @@ -35,6 +35,7 @@ export class LocationService { return queriedAreaName; } } + return 'other'; } getAreaTypes(): void { @@ -54,5 +55,6 @@ export class LocationService { return queriedAreaType; } } + return 'other'; } } diff --git a/src/app/_services/logging.service.ts b/src/app/_services/logging.service.ts index 0865e88..d7e38a1 100644 --- a/src/app/_services/logging.service.ts +++ b/src/app/_services/logging.service.ts @@ -5,9 +5,6 @@ import { NGXLogger } from 'ngx-logger'; providedIn: 'root', }) export class LoggingService { - env: string; - canDebug: boolean; - constructor(private logger: NGXLogger) { // TRACE|DEBUG|INFO|LOG|WARN|ERROR|FATAL|OFF if (isDevMode()) { diff --git a/src/app/_services/token.service.ts b/src/app/_services/token.service.ts index a572729..120bcc7 100644 --- a/src/app/_services/token.service.ts +++ b/src/app/_services/token.service.ts @@ -22,9 +22,7 @@ export class TokenService { async init(): Promise<void> { this.registry = await RegistryService.getRegistry(); - this.tokenRegistry = new TokenRegistry( - await this.registry.getContractAddressByName('TokenRegistry') - ); + this.tokenRegistry = await RegistryService.getTokenRegistry(); this.load.next(true); } diff --git a/src/app/_services/transaction.service.ts b/src/app/_services/transaction.service.ts index 1977c3e..4a46e73 100644 --- a/src/app/_services/transaction.service.ts +++ b/src/app/_services/transaction.service.ts @@ -10,7 +10,6 @@ import { add0x, fromHex, strip0x, toHex } from '@src/assets/js/ethtx/dist/hex'; import { Tx } from '@src/assets/js/ethtx/dist'; import { toValue } from '@src/assets/js/ethtx/dist/tx'; import * as secp256k1 from 'secp256k1'; -import { AuthService } from '@app/_services/auth.service'; import { defaultAccount } from '@app/_models'; import { LoggingService } from '@app/_services/logging.service'; import { HttpClient } from '@angular/common/http'; @@ -28,13 +27,11 @@ export class TransactionService { transactions: any[] = []; private transactionList = new BehaviorSubject<any[]>(this.transactions); transactionsSubject = this.transactionList.asObservable(); - userInfo: any; web3: Web3; registry: CICRegistry; constructor( private httpClient: HttpClient, - private authService: AuthService, private userService: UserService, private loggingService: LoggingService ) { @@ -42,8 +39,6 @@ export class TransactionService { } async init(): Promise<void> { - await this.authService.init(); - await this.userService.init(); this.registry = await RegistryService.getRegistry(); } diff --git a/src/app/_services/user.service.ts b/src/app/_services/user.service.ts index 975dd72..d6d95a6 100644 --- a/src/app/_services/user.service.ts +++ b/src/app/_services/user.service.ts @@ -7,11 +7,9 @@ import { ArgPair, Envelope, Phone, Syncable, User } from 'cic-client-meta'; import { AccountDetails } from '@app/_models'; import { LoggingService } from '@app/_services/logging.service'; import { TokenService } from '@app/_services/token.service'; -import { AccountIndex } from '@app/_eth'; import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp'; import { RegistryService } from '@app/_services/registry.service'; import { CICRegistry } from '@cicnet/cic-client'; -import { AuthService } from '@app/_services/auth.service'; import { personValidation, updateSyncable, vcardValidation } from '@app/_helpers'; import { add0x } from '@src/assets/js/ethtx/dist/hex'; import { KeystoreService } from '@app/_services/keystore.service'; @@ -43,13 +41,10 @@ export class UserService { constructor( private httpClient: HttpClient, private loggingService: LoggingService, - private tokenService: TokenService, - private authService: AuthService + private tokenService: TokenService ) {} async init(): Promise<void> { - await this.authService.init(); - await this.tokenService.init(); this.keystore = await KeystoreService.getKeystore(); this.signer = new PGPSigner(this.keystore); this.registry = await RegistryService.getRegistry(); @@ -203,12 +198,6 @@ export class UserService { } async loadAccounts(limit: number = 100, offset: number = 0): Promise<void> { - this.resetAccountsList(); - // const accountIndexAddress: string = await this.registry.getContractAddressByName( - // 'AccountRegistry' - // ); - // const accountIndexQuery = new AccountIndex(accountIndexAddress); - // const accountAddresses: Array<string> = await accountIndexQuery.last(limit); try { const accountRegistry = await RegistryService.getAccountRegistry(); const accountAddresses: Array<string> = await accountRegistry.last(limit); @@ -271,10 +260,6 @@ export class UserService { this.accountsList.next(this.accounts); } - searchAccountByName(name: string): any { - return; - } - getCategories(): void { this.httpClient .get(`${environment.cicMetaUrl}/categories`) @@ -292,6 +277,7 @@ export class UserService { return queriedCategory; } } + return 'other'; } getAccountTypes(): Observable<any> { diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d9e4a2e..96c0507 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,12 +1,16 @@ import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; import { AuthService, + BlockSyncService, ErrorDialogService, LoggingService, + TokenService, TransactionService, + UserService, } from '@app/_services'; -import { catchError } from 'rxjs/operators'; import { SwUpdate } from '@angular/service-worker'; +import { NavigationEnd, Router } from '@angular/router'; +import { filter } from 'rxjs/operators'; @Component({ selector: 'app-root', @@ -16,16 +20,20 @@ import { SwUpdate } from '@angular/service-worker'; }) export class AppComponent implements OnInit { title = 'CICADA'; - readyStateTarget: number = 3; - readyState: number = 0; mediaQuery: MediaQueryList = window.matchMedia('(max-width: 768px)'); + url: string; + accountDetailsRegex = '/accounts/[a-z,A-Z,0-9]{40}'; constructor( private authService: AuthService, - private transactionService: TransactionService, - private loggingService: LoggingService, + private blockSyncService: BlockSyncService, private errorDialogService: ErrorDialogService, - private swUpdate: SwUpdate + private loggingService: LoggingService, + private tokenService: TokenService, + private transactionService: TransactionService, + private userService: UserService, + private swUpdate: SwUpdate, + private router: Router ) { this.mediaQuery.addEventListener('change', this.onResize); this.onResize(this.mediaQuery); @@ -33,7 +41,34 @@ export class AppComponent implements OnInit { async ngOnInit(): Promise<void> { await this.authService.init(); + await this.tokenService.init(); + await this.userService.init(); await this.transactionService.init(); + await this.router.events + .pipe(filter((e) => e instanceof NavigationEnd)) + .forEach(async (routeInfo) => { + if (routeInfo instanceof NavigationEnd) { + this.url = routeInfo.url; + if (!this.url.match(this.accountDetailsRegex) || !this.url.includes('tx')) { + await this.blockSyncService.blockSync(); + } + if (!this.url.includes('accounts')) { + try { + // TODO it feels like this should be in the onInit handler + await this.userService.loadAccounts(100); + } catch (error) { + this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, { error }); + } + } + if (!this.url.includes('tokens')) { + this.tokenService.load.subscribe(async (status: boolean) => { + if (status) { + await this.tokenService.getTokens(); + } + }); + } + } + }); try { const publicKeys = await this.authService.getPublicKeys(); await this.authService.mutableKeyStore.importPublicKey(publicKeys); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9c0c7b3..01ce07e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -11,7 +11,12 @@ import { MatTableModule } from '@angular/material/table'; import { AuthGuard } from '@app/_guards'; import { LoggerModule } from 'ngx-logger'; import { environment } from '@src/environments/environment'; -import { ErrorInterceptor, HttpConfigInterceptor, LoggingInterceptor } from '@app/_interceptors'; +import { + ConnectionInterceptor, + ErrorInterceptor, + HttpConfigInterceptor, + LoggingInterceptor, +} from '@app/_interceptors'; import { MutablePgpKeyStore } from '@app/_pgp'; import { ServiceWorkerModule } from '@angular/service-worker'; @@ -38,6 +43,7 @@ import { ServiceWorkerModule } from '@angular/service-worker'; MockBackendProvider, GlobalErrorHandler, { provide: ErrorHandler, useClass: GlobalErrorHandler }, + { provide: HTTP_INTERCEPTORS, useClass: ConnectionInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true }, diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index d715fd4..cf72b96 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -3,7 +3,6 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { CustomErrorStateMatcher } from '@app/_helpers'; import { AuthService } from '@app/_services'; import { ErrorDialogService } from '@app/_services/error-dialog.service'; -import { LoggingService } from '@app/_services/logging.service'; import { Router } from '@angular/router'; @Component({ @@ -25,7 +24,7 @@ export class AuthComponent implements OnInit { private errorDialogService: ErrorDialogService ) {} - async ngOnInit(): Promise<void> { + ngOnInit(): void { this.keyForm = this.formBuilder.group({ key: ['', Validators.required], }); @@ -58,7 +57,7 @@ export class AuthComponent implements OnInit { } } catch (HttpError) { this.errorDialogService.openDialog({ - message: "Failed to login please try again.", + message: 'Failed to login please try again.', }); } } diff --git a/src/app/pages/accounts/account-details/account-details.component.html b/src/app/pages/accounts/account-details/account-details.component.html index 4378beb..672da8a 100644 --- a/src/app/pages/accounts/account-details/account-details.component.html +++ b/src/app/pages/accounts/account-details/account-details.component.html @@ -382,6 +382,11 @@ <mat-icon matSuffix>search</mat-icon> </mat-form-field> + <div *ngIf="transactionsLoading"> + <h2 class="text-center"><strong>Loading Transactions!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <table mat-table class="mat-elevation-z10" @@ -491,6 +496,11 @@ <mat-icon matSuffix>search</mat-icon> </mat-form-field> + <div *ngIf="accountsLoading"> + <h2 class="text-center"><strong>Loading Accounts!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <mat-table class="mat-elevation-z10" [dataSource]="userDataSource" diff --git a/src/app/pages/accounts/account-details/account-details.component.ts b/src/app/pages/accounts/account-details/account-details.component.ts index 5290a9a..7a9f1cd 100644 --- a/src/app/pages/accounts/account-details/account-details.component.ts +++ b/src/app/pages/accounts/account-details/account-details.component.ts @@ -1,4 +1,5 @@ import { + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, @@ -31,7 +32,7 @@ import { AccountDetails, Transaction } from '@app/_models'; styleUrls: ['./account-details.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AccountDetailsComponent implements OnInit { +export class AccountDetailsComponent implements OnInit, AfterViewInit { transactionsDataSource: MatTableDataSource<any>; transactionsDisplayedColumns: Array<string> = ['sender', 'recipient', 'value', 'created', 'type']; transactionsDefaultPageSize: number = 10; @@ -68,6 +69,8 @@ export class AccountDetailsComponent implements OnInit { category: string; area: string; areaType: string; + accountsLoading: boolean = true; + transactionsLoading: boolean = true; constructor( private formBuilder: FormBuilder, @@ -103,10 +106,7 @@ export class AccountDetailsComponent implements OnInit { location: ['', Validators.required], locationType: ['', Validators.required], }); - await this.blockSyncService.init(); - await this.tokenService.init(); - await this.transactionService.init(); - await this.userService.init(); + this.transactionService.resetTransactionsList(); await this.blockSyncService.blockSync(this.accountAddress); this.userService.resetAccountsList(); (await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe( @@ -114,7 +114,6 @@ export class AccountDetailsComponent implements OnInit { if (res !== undefined) { this.account = res; this.cdr.detectChanges(); - this.loggingService.sendInfoLevelMessage(this.account); this.locationService.areaNamesSubject.subscribe((response) => { this.area = this.locationService.getAreaNameByLocation( this.account.location.area_name, @@ -158,6 +157,9 @@ export class AccountDetailsComponent implements OnInit { this.userDataSource.paginator = this.userTablePaginator; this.userDataSource.sort = this.userTableSort; this.accounts = accounts; + if (accounts.length > 0) { + this.accountsLoading = false; + } this.cdr.detectChanges(); }); @@ -166,6 +168,9 @@ export class AccountDetailsComponent implements OnInit { this.transactionsDataSource.paginator = this.transactionTablePaginator; this.transactionsDataSource.sort = this.transactionTableSort; this.transactions = transactions; + if (transactions.length > 0) { + this.transactionsLoading = false; + } this.cdr.detectChanges(); }); this.userService.getCategories(); @@ -199,6 +204,17 @@ export class AccountDetailsComponent implements OnInit { }); } + ngAfterViewInit(): void { + if (this.userDataSource) { + this.userDataSource.paginator = this.userTablePaginator; + this.userDataSource.sort = this.userTableSort; + } + if (this.transactionsDataSource) { + this.transactionsDataSource.paginator = this.transactionTablePaginator; + this.transactionsDataSource.sort = this.transactionTableSort; + } + } + doTransactionFilter(value: string): void { this.transactionsDataSource.filter = value.trim().toLocaleLowerCase(); } diff --git a/src/app/pages/accounts/account-search/account-search.component.ts b/src/app/pages/accounts/account-search/account-search.component.ts index f0ea97d..a24708a 100644 --- a/src/app/pages/accounts/account-search/account-search.component.ts +++ b/src/app/pages/accounts/account-search/account-search.component.ts @@ -13,9 +13,6 @@ import { environment } from '@src/environments/environment'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccountSearchComponent implements OnInit { - nameSearchForm: FormGroup; - nameSearchSubmitted: boolean = false; - nameSearchLoading: boolean = false; phoneSearchForm: FormGroup; phoneSearchSubmitted: boolean = false; phoneSearchLoading: boolean = false; @@ -29,9 +26,6 @@ export class AccountSearchComponent implements OnInit { private userService: UserService, private router: Router ) { - this.nameSearchForm = this.formBuilder.group({ - name: ['', Validators.required], - }); this.phoneSearchForm = this.formBuilder.group({ phoneNumber: ['', Validators.required], }); @@ -40,13 +34,8 @@ export class AccountSearchComponent implements OnInit { }); } - async ngOnInit(): Promise<void> { - await this.userService.init(); - } + ngOnInit(): void {} - get nameSearchFormStub(): any { - return this.nameSearchForm.controls; - } get phoneSearchFormStub(): any { return this.phoneSearchForm.controls; } @@ -54,16 +43,6 @@ export class AccountSearchComponent implements OnInit { return this.addressSearchForm.controls; } - onNameSearch(): void { - this.nameSearchSubmitted = true; - if (this.nameSearchForm.invalid) { - return; - } - this.nameSearchLoading = true; - this.userService.searchAccountByName(this.nameSearchFormStub.name.value); - this.nameSearchLoading = false; - } - async onPhoneSearch(): Promise<void> { this.phoneSearchSubmitted = true; if (this.phoneSearchForm.invalid) { diff --git a/src/app/pages/accounts/accounts.component.html b/src/app/pages/accounts/accounts.component.html index 62bc39f..5831b2a 100644 --- a/src/app/pages/accounts/accounts.component.html +++ b/src/app/pages/accounts/accounts.component.html @@ -64,6 +64,11 @@ <mat-icon matSuffix>search</mat-icon> </mat-form-field> + <div *ngIf="loading"> + <h2 class="text-center"><strong>Loading Accounts!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <mat-table class="mat-elevation-z10" [dataSource]="dataSource" diff --git a/src/app/pages/accounts/accounts.component.ts b/src/app/pages/accounts/accounts.component.ts index d23735b..2e4c864 100644 --- a/src/app/pages/accounts/accounts.component.ts +++ b/src/app/pages/accounts/accounts.component.ts @@ -1,4 +1,10 @@ -import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + OnInit, + ViewChild, +} from '@angular/core'; import { MatTableDataSource } from '@angular/material/table'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; @@ -16,7 +22,7 @@ import { AccountDetails } from '@app/_models'; styleUrls: ['./accounts.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AccountsComponent implements OnInit { +export class AccountsComponent implements OnInit, AfterViewInit { dataSource: MatTableDataSource<any>; accounts: Array<AccountDetails> = []; displayedColumns: Array<string> = ['name', 'phone', 'created', 'balance', 'location']; @@ -25,32 +31,34 @@ export class AccountsComponent implements OnInit { accountsType: string = 'all'; accountTypes: Array<string>; tokenSymbol: string; + loading: boolean = true; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; constructor( - private userService: UserService, private loggingService: LoggingService, + private userService: UserService, private router: Router, private tokenService: TokenService ) {} async ngOnInit(): Promise<void> { - await this.userService.init(); - await this.tokenService.init(); + this.userService.accountsSubject.subscribe((accounts) => { + this.dataSource = new MatTableDataSource<any>(accounts); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + this.accounts = accounts; + if (accounts.length > 0) { + this.loading = false; + } + }); try { // TODO it feels like this should be in the onInit handler await this.userService.loadAccounts(100); } catch (error) { this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, { error }); } - this.userService.accountsSubject.subscribe((accounts) => { - this.dataSource = new MatTableDataSource<any>(accounts); - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; - this.accounts = accounts; - }); this.userService .getAccountTypes() .pipe(first()) @@ -62,6 +70,13 @@ export class AccountsComponent implements OnInit { }); } + ngAfterViewInit(): void { + if (this.dataSource) { + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + } + doFilter(value: string): void { this.dataSource.filter = value.trim().toLocaleLowerCase(); } diff --git a/src/app/pages/accounts/accounts.module.ts b/src/app/pages/accounts/accounts.module.ts index 2d3cf37..fde59b1 100644 --- a/src/app/pages/accounts/accounts.module.ts +++ b/src/app/pages/accounts/accounts.module.ts @@ -23,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ReactiveFormsModule } from '@angular/forms'; import { AccountSearchComponent } from './account-search/account-search.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ declarations: [ @@ -51,6 +52,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'; MatProgressSpinnerModule, ReactiveFormsModule, MatSnackBarModule, + MatProgressBarModule, ], }) export class AccountsModule {} diff --git a/src/app/pages/accounts/create-account/create-account.component.ts b/src/app/pages/accounts/create-account/create-account.component.ts index ab6dfbc..c29d714 100644 --- a/src/app/pages/accounts/create-account/create-account.component.ts +++ b/src/app/pages/accounts/create-account/create-account.component.ts @@ -25,8 +25,7 @@ export class CreateAccountComponent implements OnInit { private userService: UserService ) {} - async ngOnInit(): Promise<void> { - await this.userService.init(); + ngOnInit(): void { this.createForm = this.formBuilder.group({ accountType: ['', Validators.required], idNumber: ['', Validators.required], diff --git a/src/app/pages/admin/admin.component.html b/src/app/pages/admin/admin.component.html index b933f3e..950057d 100644 --- a/src/app/pages/admin/admin.component.html +++ b/src/app/pages/admin/admin.component.html @@ -43,6 +43,11 @@ <mat-icon matSuffix>search</mat-icon> </mat-form-field> + <div *ngIf="loading"> + <h2 class="text-center"><strong>Loading Actions!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <mat-table class="mat-elevation-z10" [dataSource]="dataSource" multiTemplateDataRows> <!-- Expand Column --> <ng-container matColumnDef="expand"> diff --git a/src/app/pages/admin/admin.component.ts b/src/app/pages/admin/admin.component.ts index d33be3c..c505c80 100644 --- a/src/app/pages/admin/admin.component.ts +++ b/src/app/pages/admin/admin.component.ts @@ -6,7 +6,7 @@ import { LoggingService, UserService } from '@app/_services'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { first } from 'rxjs/operators'; import { exportCsv } from '@app/_helpers'; -import { Action } from '../../_models'; +import { Action } from '@app/_models'; @Component({ selector: 'app-admin', @@ -26,20 +26,23 @@ export class AdminComponent implements OnInit { displayedColumns: Array<string> = ['expand', 'user', 'role', 'action', 'status', 'approve']; action: Action; actions: Array<Action>; + loading: boolean = true; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; constructor(private userService: UserService, private loggingService: LoggingService) {} - async ngOnInit(): Promise<void> { - await this.userService.init(); + ngOnInit(): void { this.userService.getActions(); this.userService.actionsSubject.subscribe((actions) => { this.dataSource = new MatTableDataSource<any>(actions); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; this.actions = actions; + if (actions.length > 0) { + this.loading = false; + } }); } diff --git a/src/app/pages/admin/admin.module.ts b/src/app/pages/admin/admin.module.ts index 7e50ba1..d29f774 100644 --- a/src/app/pages/admin/admin.module.ts +++ b/src/app/pages/admin/admin.module.ts @@ -13,6 +13,7 @@ import { MatSortModule } from '@angular/material/sort'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatButtonModule } from '@angular/material/button'; import { MatRippleModule } from '@angular/material/core'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ declarations: [AdminComponent], @@ -29,6 +30,7 @@ import { MatRippleModule } from '@angular/material/core'; MatPaginatorModule, MatButtonModule, MatRippleModule, + MatProgressBarModule, ], }) export class AdminModule {} diff --git a/src/app/pages/pages-routing.module.ts b/src/app/pages/pages-routing.module.ts index 94ee6c9..02fe0a1 100644 --- a/src/app/pages/pages-routing.module.ts +++ b/src/app/pages/pages-routing.module.ts @@ -22,10 +22,10 @@ const routes: Routes = [ path: 'tokens', loadChildren: () => import('@pages/tokens/tokens.module').then((m) => m.TokensModule), }, - { - path: 'admin', - loadChildren: () => import('@pages/admin/admin.module').then((m) => m.AdminModule), - }, + // { + // path: 'admin', + // loadChildren: () => import('@pages/admin/admin.module').then((m) => m.AdminModule), + // }, { path: '**', redirectTo: 'home', pathMatch: 'full' }, ]; diff --git a/src/app/pages/settings/settings.component.html b/src/app/pages/settings/settings.component.html index 2ed99a0..24fc878 100644 --- a/src/app/pages/settings/settings.component.html +++ b/src/app/pages/settings/settings.component.html @@ -40,6 +40,7 @@ </div> </div> </div> + <div class="col-12 mb-2"> <div class="card"> <mat-card-title class="card-header"> @@ -67,6 +68,12 @@ /> <mat-icon matSuffix>search</mat-icon> </mat-form-field> + + <div *ngIf="loading"> + <h2 class="text-center"><strong>Loading Trusted Users!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <mat-table class="mat-elevation-z10" [dataSource]="dataSource" diff --git a/src/app/pages/settings/settings.component.ts b/src/app/pages/settings/settings.component.ts index 8e167b0..32304f1 100644 --- a/src/app/pages/settings/settings.component.ts +++ b/src/app/pages/settings/settings.component.ts @@ -13,24 +13,26 @@ import { exportCsv } from '@app/_helpers'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class SettingsComponent implements OnInit { - date: string; dataSource: MatTableDataSource<any>; displayedColumns: Array<string> = ['name', 'email', 'userId']; trustedUsers: Array<Staff>; userInfo: Staff; + loading: boolean = true; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; constructor(private authService: AuthService) {} - async ngOnInit(): Promise<void> { - await this.authService.init(); + ngOnInit(): void { this.authService.trustedUsersSubject.subscribe((users) => { this.dataSource = new MatTableDataSource<any>(users); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; this.trustedUsers = users; + if (users.length > 0) { + this.loading = false; + } }); this.userInfo = this.authService.getPrivateKeyInfo(); } diff --git a/src/app/pages/settings/settings.module.ts b/src/app/pages/settings/settings.module.ts index 139feae..0eb5648 100644 --- a/src/app/pages/settings/settings.module.ts +++ b/src/app/pages/settings/settings.module.ts @@ -18,6 +18,7 @@ import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatSelectModule } from '@angular/material/select'; import { MatMenuModule } from '@angular/material/menu'; import { ReactiveFormsModule } from '@angular/forms'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ declarations: [SettingsComponent, OrganizationComponent], @@ -38,6 +39,7 @@ import { ReactiveFormsModule } from '@angular/forms'; MatSelectModule, MatMenuModule, ReactiveFormsModule, + MatProgressBarModule, ], }) export class SettingsModule {} diff --git a/src/app/pages/tokens/tokens.component.html b/src/app/pages/tokens/tokens.component.html index 56d843b..bebf178 100644 --- a/src/app/pages/tokens/tokens.component.html +++ b/src/app/pages/tokens/tokens.component.html @@ -45,6 +45,11 @@ <mat-icon matSuffix>search</mat-icon> </mat-form-field> + <div *ngIf="loading"> + <h2 class="text-center"><strong>Loading Tokens!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <mat-table class="mat-elevation-z10 table-responsive" [dataSource]="dataSource" diff --git a/src/app/pages/tokens/tokens.component.ts b/src/app/pages/tokens/tokens.component.ts index 9f96497..e0301f1 100644 --- a/src/app/pages/tokens/tokens.component.ts +++ b/src/app/pages/tokens/tokens.component.ts @@ -1,9 +1,14 @@ -import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + OnInit, + ViewChild, +} from '@angular/core'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; -import { LoggingService, TokenService } from '@app/_services'; +import { TokenService } from '@app/_services'; import { MatTableDataSource } from '@angular/material/table'; -import { Router } from '@angular/router'; import { exportCsv } from '@app/_helpers'; import { Token } from '@app/_models'; @@ -13,36 +18,41 @@ import { Token } from '@app/_models'; styleUrls: ['./tokens.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class TokensComponent implements OnInit { +export class TokensComponent implements OnInit, AfterViewInit { dataSource: MatTableDataSource<any>; columnsToDisplay: Array<string> = ['name', 'symbol', 'address', 'supply']; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; tokens: Array<Token>; token: Token; + loading: boolean = true; - constructor( - private tokenService: TokenService, - private loggingService: LoggingService, - private router: Router - ) {} + constructor(private tokenService: TokenService) {} - async ngOnInit(): Promise<void> { - await this.tokenService.init(); + ngOnInit(): void { this.tokenService.load.subscribe(async (status: boolean) => { if (status) { await this.tokenService.getTokens(); } }); this.tokenService.tokensSubject.subscribe((tokens) => { - this.loggingService.sendInfoLevelMessage(tokens); this.dataSource = new MatTableDataSource(tokens); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; this.tokens = tokens; + if (tokens.length > 0) { + this.loading = false; + } }); } + ngAfterViewInit(): void { + if (this.dataSource) { + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + } + doFilter(value: string): void { this.dataSource.filter = value.trim().toLocaleLowerCase(); } diff --git a/src/app/pages/tokens/tokens.module.ts b/src/app/pages/tokens/tokens.module.ts index 938d4fb..b3a6332 100644 --- a/src/app/pages/tokens/tokens.module.ts +++ b/src/app/pages/tokens/tokens.module.ts @@ -17,6 +17,7 @@ import { MatSidenavModule } from '@angular/material/sidenav'; import { MatButtonModule } from '@angular/material/button'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatCardModule } from '@angular/material/card'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ declarations: [TokensComponent, TokenDetailsComponent], @@ -37,6 +38,7 @@ import { MatCardModule } from '@angular/material/card'; MatToolbarModule, MatCardModule, MatRippleModule, + MatProgressBarModule, ], }) export class TokensModule {} diff --git a/src/app/pages/transactions/transaction-details/transaction-details.component.ts b/src/app/pages/transactions/transaction-details/transaction-details.component.ts index 29322bb..ca324e1 100644 --- a/src/app/pages/transactions/transaction-details/transaction-details.component.ts +++ b/src/app/pages/transactions/transaction-details/transaction-details.component.ts @@ -36,9 +36,7 @@ export class TransactionDetailsComponent implements OnInit { private tokenService: TokenService ) {} - async ngOnInit(): Promise<void> { - await this.transactionService.init(); - await this.tokenService.init(); + ngOnInit(): void { if (this.transaction?.type === 'conversion') { this.traderBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions'; diff --git a/src/app/pages/transactions/transactions.component.html b/src/app/pages/transactions/transactions.component.html index 96cfcc9..cfceae0 100644 --- a/src/app/pages/transactions/transactions.component.html +++ b/src/app/pages/transactions/transactions.component.html @@ -63,6 +63,11 @@ <mat-icon matSuffix>search</mat-icon> </mat-form-field> + <div *ngIf="loading"> + <h2 class="text-center"><strong>Loading Transactions!</strong></h2> + <mat-progress-bar [mode]="'query'"></mat-progress-bar> + </div> + <table mat-table class="mat-elevation-z10" diff --git a/src/app/pages/transactions/transactions.component.ts b/src/app/pages/transactions/transactions.component.ts index dd25baf..78e2d39 100644 --- a/src/app/pages/transactions/transactions.component.ts +++ b/src/app/pages/transactions/transactions.component.ts @@ -29,6 +29,7 @@ export class TransactionsComponent implements OnInit, AfterViewInit { transactionsType: string = 'all'; transactionsTypes: Array<string>; tokenSymbol: string; + loading: boolean = true; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -41,17 +42,16 @@ export class TransactionsComponent implements OnInit, AfterViewInit { ) {} async ngOnInit(): Promise<void> { + await this.blockSyncService.blockSync(); this.transactionService.transactionsSubject.subscribe((transactions) => { this.transactionDataSource = new MatTableDataSource<any>(transactions); this.transactionDataSource.paginator = this.paginator; this.transactionDataSource.sort = this.sort; this.transactions = transactions; + if (transactions.length > 0) { + this.loading = false; + } }); - await this.blockSyncService.init(); - await this.tokenService.init(); - await this.transactionService.init(); - await this.userService.init(); - await this.blockSyncService.blockSync(); this.userService .getTransactionTypes() .pipe(first()) @@ -85,8 +85,10 @@ export class TransactionsComponent implements OnInit, AfterViewInit { } ngAfterViewInit(): void { - this.transactionDataSource.paginator = this.paginator; - this.transactionDataSource.sort = this.sort; + if (this.transactionDataSource) { + this.transactionDataSource.paginator = this.paginator; + this.transactionDataSource.sort = this.sort; + } } downloadCsv(): void { diff --git a/src/app/pages/transactions/transactions.module.ts b/src/app/pages/transactions/transactions.module.ts index 4a5b044..e71342b 100644 --- a/src/app/pages/transactions/transactions.module.ts +++ b/src/app/pages/transactions/transactions.module.ts @@ -17,6 +17,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatCardModule } from '@angular/material/card'; import { MatRippleModule } from '@angular/material/core'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ declarations: [TransactionsComponent, TransactionDetailsComponent], @@ -37,6 +38,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'; MatCardModule, MatRippleModule, MatSnackBarModule, + MatProgressBarModule, ], }) export class TransactionsModule {} diff --git a/src/app/shared/network-status/network-status.component.html b/src/app/shared/network-status/network-status.component.html index 54d821a..92b9802 100644 --- a/src/app/shared/network-status/network-status.component.html +++ b/src/app/shared/network-status/network-status.component.html @@ -1,6 +1,6 @@ <nav class="navbar navbar-dark background-dark"> <h1 class="navbar-brand"> - <div *ngIf="noInternetConnection; then offlineBlock; else onlineBlock"></div> + <div *ngIf="online; then onlineBlock; else offlineBlock"></div> <ng-template #offlineBlock> <strong style="color: red">OFFLINE </strong> <img width="20rem" src="assets/images/no-wifi.svg" alt="Internet Disconnected" /> diff --git a/src/app/shared/network-status/network-status.component.ts b/src/app/shared/network-status/network-status.component.ts index b7533ea..47f4ed0 100644 --- a/src/app/shared/network-status/network-status.component.ts +++ b/src/app/shared/network-status/network-status.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; +import { checkOnlineStatus } from '@src/app/_helpers'; @Component({ selector: 'app-network-status', @@ -7,21 +8,30 @@ import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@ changeDetection: ChangeDetectionStrategy.OnPush, }) export class NetworkStatusComponent implements OnInit { - noInternetConnection: boolean = !navigator.onLine; + online: boolean = navigator.onLine; constructor(private cdr: ChangeDetectorRef) { this.handleNetworkChange(); } - ngOnInit(): void {} + ngOnInit(): void { + window.addEventListener('online', (event: any) => { + this.online = true; + this.cdr.detectChanges(); + }); + window.addEventListener('offline', (event: any) => { + this.online = false; + this.cdr.detectChanges(); + }); + } handleNetworkChange(): void { - setTimeout(() => { - if (!navigator.onLine !== this.noInternetConnection) { - this.noInternetConnection = !navigator.onLine; + setTimeout(async () => { + if (this.online !== (await checkOnlineStatus())) { + this.online = await checkOnlineStatus(); this.cdr.detectChanges(); } this.handleNetworkChange(); - }, 5000); + }, 3000); } }