diff --git a/frontend/custom-sv-config.json b/frontend/custom-sv-config.json
index dee3dab18..245b47229 100644
--- a/frontend/custom-sv-config.json
+++ b/frontend/custom-sv-config.json
@@ -38,7 +38,12 @@
}
},
{
- "component": "blocks"
+ "component": "simpleproof",
+ "mobileOrder": 6,
+ "props": {
+ "label": "Executive Decrees",
+ "key": "el_salvador_decretos"
+ }
},
{
"component": "addressTransactions",
diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
index 13cdd97ce..285b897bf 100644
--- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
+++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
@@ -305,6 +305,20 @@
}
+ @case ('simpleproof') {
+
+ }
}
}
diff --git a/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.html b/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.html
new file mode 100644
index 000000000..dc8f24ea1
--- /dev/null
+++ b/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+ @if (isLoading) {
+ loading!
+
+ } @else if (error || !verified.length) {
+
+ temporarily unavailable
+
+ } @else {
+
+
+
+ Filename |
+ Hash |
+ Verified |
+ Proof |
+
+
+
+ {{ item.file_name }} |
+ {{ item.sha256 }} |
+
+
+ |
+
+
+
+
+
+ Verify
+
+ |
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
diff --git a/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.scss b/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.scss
new file mode 100644
index 000000000..63e2fe046
--- /dev/null
+++ b/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.scss
@@ -0,0 +1,114 @@
+.spinner-wrapper, .error-wrapper {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.spinner-border {
+ height: 25px;
+ width: 25px;
+ margin-top: -10px;
+ margin-left: -13px;
+ flex-shrink: 0;
+}
+
+.container-xl {
+ max-width: 1400px;
+}
+.container-xl.widget {
+ padding-left: 0px;
+ padding-bottom: 0px;
+ padding-right: 0px;
+}
+.container-xl.legacy {
+ max-width: 1140px;
+}
+
+.container {
+ max-width: 100%;
+}
+
+tr, td, th {
+ border: 0px;
+ padding-top: 0.71rem !important;
+ padding-bottom: 0.7rem !important;
+}
+
+.clear-link {
+ color: white;
+}
+
+.disabled {
+ pointer-events: none;
+ opacity: 0.5;
+}
+
+.progress {
+ background-color: var(--secondary);
+}
+
+.filename {
+ width: 50%;
+ max-width: 300px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.hash {
+ width: 25%;
+ max-width: 700px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+td.hash {
+ font-family: monospace;
+}
+.widget .hash {
+ display: none;
+}
+@media (max-width: 1200px) {
+ .hash {
+ display: none;
+ }
+}
+
+.verified {
+ width: 25%;
+}
+td.verified {
+ color: var(--tertiary);
+}
+
+.proof {
+ width: 25%;
+}
+
+.badge-verify {
+ font-size: 1.05em;
+ font-weight: normal;
+ background: var(--nav-bg);
+ color: white;
+
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: auto;
+
+ .icon {
+ margin: -0.25em;
+ margin-right: 0.5em;
+
+ .icon-img {
+ width: 16px;
+ font-size: 16px;
+ }
+ }
+}
diff --git a/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.ts b/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.ts
new file mode 100644
index 000000000..6cfa31bdb
--- /dev/null
+++ b/frontend/src/app/components/simpleproof-widget/simpleproof-widget.component.ts
@@ -0,0 +1,86 @@
+import { Component, Input, SecurityContext, SimpleChanges, OnChanges } from '@angular/core';
+import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
+import { ServicesApiServices } from '@app/services/services-api.service';
+import { catchError, of } from 'rxjs';
+
+export interface SimpleProof {
+ file_name: string;
+ sha256: string;
+ ots_verification: string;
+ block_height: number;
+ block_hash: string;
+ block_time: number;
+ simpleproof_url: string;
+ key?: string;
+ sanitized_url?: SafeResourceUrl;
+}
+
+@Component({
+ selector: 'app-simpleproof-widget',
+ templateUrl: './simpleproof-widget.component.html',
+ styleUrls: ['./simpleproof-widget.component.scss'],
+})
+export class SimpleProofWidgetComponent implements OnChanges {
+ @Input() key: string = window['__env']?.customize?.dashboard.widgets?.find(w => w.component ==='simpleproof')?.props?.key ?? '';
+ @Input() label: string = window['__env']?.customize?.dashboard.widgets?.find(w => w.component ==='simpleproof')?.props?.label ?? 'Verified Documents';
+ @Input() widget: boolean = false;
+ @Input() width = 300;
+ @Input() height = 400;
+
+ verified: SimpleProof[] = [];
+ verifiedPage: SimpleProof[] = [];
+ isLoading: boolean = true;
+ error: boolean = false;
+ page = 1;
+ lastPage = 1;
+ itemsPerPage = 15;
+ paginationMaxSize = window.innerWidth <= 767.98 ? 3 : 5;
+
+ constructor(
+ private servicesApiService: ServicesApiServices,
+ public sanitizer: DomSanitizer,
+ ) {}
+
+ ngOnInit(): void {
+ this.loadVerifications();
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.widget) {
+ this.itemsPerPage = this.widget ? 6 : 15;
+ }
+ if (changes.key) {
+ this.loadVerifications();
+ }
+ }
+
+ loadVerifications(): void {
+ if (this.key) {
+ this.isLoading = true;
+ this.servicesApiService.getSimpleProofs$(this.key).pipe(
+ catchError(() => {
+ this.isLoading = false;
+ this.error = true;
+ return of({});
+ }),
+ ).subscribe((data: Record) => {
+ if (Object.keys(data).length) {
+ this.verified = Object.keys(data).map(key => ({
+ ...data[key],
+ file_name: data[key].file_name.replace('source-', '').replace('_', ' '),
+ key,
+ sanitized_url: this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, data[key]['simpleproof-url']) ?? ''),
+ })).sort((a, b) => b.key.localeCompare(a.key));
+ this.verifiedPage = this.verified.slice((this.page - 1) * this.itemsPerPage, this.page * this.itemsPerPage);
+ this.isLoading = false;
+ this.error = false;
+ }
+ });
+ }
+ }
+
+ pageChange(page: number): void {
+ this.page = page;
+ this.verifiedPage = this.verified.slice((this.page - 1) * this.itemsPerPage, this.page * this.itemsPerPage);
+ }
+}
diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts
index 2ee2e0bd8..24c1a91e6 100644
--- a/frontend/src/app/master-page.module.ts
+++ b/frontend/src/app/master-page.module.ts
@@ -13,6 +13,7 @@ import { RbfList } from '@components/rbf-list/rbf-list.component';
import { ServerHealthComponent } from '@components/server-health/server-health.component';
import { ServerStatusComponent } from '@components/server-health/server-status.component';
import { FaucetComponent } from '@components/faucet/faucet.component'
+import { SimpleProofWidgetComponent } from './components/simpleproof-widget/simpleproof-widget.component';
const browserWindow = window || {};
// @ts-ignore
@@ -130,6 +131,13 @@ if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) {
}
}
+if (window['__env']?.customize?.dashboard.widgets?.some(w => w.component ==='simpleproof')) {
+ routes[0].children.push({
+ path: 'sp/verified',
+ component: SimpleProofWidgetComponent,
+ });
+}
+
@NgModule({
imports: [
RouterModule.forChild(routes)
diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts
index bec9d88a1..101a38813 100644
--- a/frontend/src/app/services/services-api.service.ts
+++ b/frontend/src/app/services/services-api.service.ts
@@ -8,6 +8,7 @@ import { Observable, of, ReplaySubject, tap, catchError, share, filter, switchMa
import { IBackendInfo } from '@interfaces/websocket.interface';
import { Acceleration, AccelerationHistoryParams } from '@interfaces/node-api.interface';
import { AccelerationStats } from '@components/acceleration/acceleration-stats/acceleration-stats.component';
+import { SimpleProof } from '../components/simpleproof-widget/simpleproof-widget.component';
export interface IUser {
username: string;
@@ -217,4 +218,8 @@ export class ServicesApiServices {
getPaymentStatus$(orderId: string): Observable {
return this.httpClient.get(`${this.stateService.env.SERVICES_API}/payments/bitcoin/check?order_id=${orderId}`, { observe: 'response' });
}
+
+ getSimpleProofs$(key: string): Observable> {
+ return this.httpClient.get>(`${this.stateService.env.SERVICES_API}/sp/verified/${key}`);
+ }
}
diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts
index ce5ac0f65..214d549cc 100644
--- a/frontend/src/app/shared/shared.module.ts
+++ b/frontend/src/app/shared/shared.module.ts
@@ -116,6 +116,7 @@ import { CalculatorComponent } from '@components/calculator/calculator.component
import { BitcoinsatoshisPipe } from '@app/shared/pipes/bitcoinsatoshis.pipe';
import { HttpErrorComponent } from '@app/shared/components/http-error/http-error.component';
import { TwitterWidgetComponent } from '@components/twitter-widget/twitter-widget.component';
+import { SimpleProofWidgetComponent } from '@components/simpleproof-widget/simpleproof-widget.component';
import { FaucetComponent } from '@components/faucet/faucet.component';
import { TwitterLogin } from '@components/twitter-login/twitter-login.component';
import { BitcoinInvoiceComponent } from '@components/bitcoin-invoice/bitcoin-invoice.component';
@@ -235,6 +236,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/
OrdDataComponent,
HttpErrorComponent,
TwitterWidgetComponent,
+ SimpleProofWidgetComponent,
FaucetComponent,
TwitterLogin,
BitcoinInvoiceComponent,
@@ -369,6 +371,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/
OrdDataComponent,
HttpErrorComponent,
TwitterWidgetComponent,
+ SimpleProofWidgetComponent,
TwitterLogin,
BitcoinInvoiceComponent,
BitcoinsatoshisPipe,
diff --git a/frontend/src/resources/sp.svg b/frontend/src/resources/sp.svg
new file mode 100644
index 000000000..e8ebb5a3c
--- /dev/null
+++ b/frontend/src/resources/sp.svg
@@ -0,0 +1,36 @@
+
+