From f83404421aeadc076006ff1a976190194a2ccba6 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 1 May 2024 23:40:39 +0000 Subject: [PATCH 01/36] Add x widget --- frontend/custom-sv-config.json | 12 +++- .../custom-dashboard.component.html | 36 +++++++--- .../custom-dashboard.component.ts | 2 + .../twitter-widget.component.html | 16 +++++ .../twitter-widget.component.scss | 10 +++ .../twitter-widget.component.ts | 65 +++++++++++++++++++ frontend/src/app/services/state.service.ts | 1 + frontend/src/app/shared/shared.module.ts | 3 + 8 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 frontend/src/app/components/twitter-widget/twitter-widget.component.html create mode 100644 frontend/src/app/components/twitter-widget/twitter-widget.component.scss create mode 100644 frontend/src/app/components/twitter-widget/twitter-widget.component.ts diff --git a/frontend/custom-sv-config.json b/frontend/custom-sv-config.json index f64f41be8..0f82a9da2 100644 --- a/frontend/custom-sv-config.json +++ b/frontend/custom-sv-config.json @@ -12,19 +12,26 @@ "dashboard": { "widgets": [ { - "component": "fees" + "component": "fees", + "mobileOrder": 4 }, { "component": "balance", + "mobileOrder": 1, "props": { "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo" } }, { - "component": "goggles" + "component": "twitter", + "mobileOrder": 5, + "props": { + "handle": "bitcoinofficesv" + } }, { "component": "address", + "mobileOrder": 2, "props": { "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo", "period": "1m" @@ -35,6 +42,7 @@ }, { "component": "addressTransactions", + "mobileOrder": 3, "props": { "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo" } 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 9180571a0..36a5e956c 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html @@ -4,7 +4,7 @@ @for (widget of widgets; track widget.component) { @switch (widget.component) { @case ('fees') { -
+
Transaction Fees
@@ -14,12 +14,12 @@
} @case ('difficulty') { -
+
} @case ('goggles') { -
+
} @case ('incoming') { -
+
@@ -93,7 +93,7 @@ } @case ('replacements') { -
+
@@ -140,7 +140,7 @@ } @case ('blocks') { -
+
@@ -184,7 +184,7 @@ } @case ('transactions') { -
+
Recent Transactions
@@ -224,13 +224,13 @@ } @case ('balance') { -
+
Treasury
} @case ('address') { -
+
} @case ('addressTransactions') { -
+ diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts index 2847b6586..2ed6ed48d 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts @@ -57,6 +57,7 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni incomingGraphHeight: number = 300; graphHeight: number = 300; webGlEnabled = true; + isMobile: boolean = window.innerWidth <= 767.98; widgets; @@ -368,5 +369,6 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni this.goggleResolution = 86; this.graphHeight = 310; } + this.isMobile = window.innerWidth <= 767.98; } } diff --git a/frontend/src/app/components/twitter-widget/twitter-widget.component.html b/frontend/src/app/components/twitter-widget/twitter-widget.component.html new file mode 100644 index 000000000..d1e042d60 --- /dev/null +++ b/frontend/src/app/components/twitter-widget/twitter-widget.component.html @@ -0,0 +1,16 @@ +@if (loading) { +
+
+
+} @else if (error) { +
+ failed to load X timeline +
+} + + diff --git a/frontend/src/app/components/twitter-widget/twitter-widget.component.scss b/frontend/src/app/components/twitter-widget/twitter-widget.component.scss new file mode 100644 index 000000000..38a39c014 --- /dev/null +++ b/frontend/src/app/components/twitter-widget/twitter-widget.component.scss @@ -0,0 +1,10 @@ +.spinner-wrapper, .error-wrapper { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/frontend/src/app/components/twitter-widget/twitter-widget.component.ts b/frontend/src/app/components/twitter-widget/twitter-widget.component.ts new file mode 100644 index 000000000..7ec865de7 --- /dev/null +++ b/frontend/src/app/components/twitter-widget/twitter-widget.component.ts @@ -0,0 +1,65 @@ +import { Component, Input, ChangeDetectionStrategy, SecurityContext } from '@angular/core'; +import { LanguageService } from '../../services/language.service'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; + +@Component({ + selector: 'app-twitter-widget', + templateUrl: './twitter-widget.component.html', + styleUrls: ['./twitter-widget.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TwitterWidgetComponent { + @Input() handle: string; + @Input() width = 300; + @Input() height = 400; + + loading: boolean = true; + error: boolean = false; + lang: string = 'en'; + + iframeSrc: SafeResourceUrl; + + constructor( + private languageService: LanguageService, + public sanitizer: DomSanitizer, + ) { + this.lang = this.languageService.getLanguage(); + this.setIframeSrc(); + } + + setIframeSrc(): void { + this.iframeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, + 'https://syndication.twitter.com/srv/timeline-profile/screen-name/bitcoinofficesv?creatorScreenName=mempool' + + '&dnt=true' + + '&embedId=twitter-widget-0' + + '&features=eyJ0ZndfdGltZWxpbmVfbGlzdCI6eyJidWNrZXQiOltdLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2ZvbGxvd2VyX2NvdW50X3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9iYWNrZW5kIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19yZWZzcmNfc2Vzc2lvbiI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfZm9zbnJfc29mdF9pbnRlcnZlbnRpb25zX2VuYWJsZWQiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X21peGVkX21lZGlhXzE1ODk3Ijp7ImJ1Y2tldCI6InRyZWF0bWVudCIsInZlcnNpb24iOm51bGx9LCJ0ZndfZXhwZXJpbWVudHNfY29va2llX2V4cGlyYXRpb24iOnsiYnVja2V0IjoxMjA5NjAwLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYmlyZHdhdGNoX3Bpdm90c19lbmFibGVkIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19kdXBsaWNhdGVfc2NyaWJlc190b19zZXR0aW5ncyI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdXNlX3Byb2ZpbGVfaW1hZ2Vfc2hhcGVfZW5hYmxlZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdmlkZW9faGxzX2R5bmFtaWNfbWFuaWZlc3RzXzE1MDgyIjp7ImJ1Y2tldCI6InRydWVfYml0cmF0ZSIsInZlcnNpb24iOm51bGx9LCJ0ZndfbGVnYWN5X3RpbWVsaW5lX3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9mcm9udGVuZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9fQ%3D%3D' + + '&frame=false' + + '&hideBorder=true' + + '&hideFooter=false' + + '&hideHeader=true' + + '&hideScrollBar=false' + + `&lang=${this.lang}` + + '&maxHeight=500px' + + '&origin=https%3A%2F%2Fmempool.space%2F' + // + '&sessionId=88f6d661d0dcca99c43c0a590f6a3e61c89226a9' + + '&showHeader=false' + + '&showReplies=false' + + '&siteScreenName=mempool' + + '&theme=dark' + + '&transparent=true' + + '&widgetsVersion=2615f7e52b7e0%3A1702314776716' + )); + } + + onReady(): void { + console.log('ready!'); + this.loading = false; + this.error = false; + } + + onFailed(): void { + console.log('failed!') + this.loading = false; + this.error = true; + } +} \ No newline at end of file diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 286ae5e48..529e53ff0 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -33,6 +33,7 @@ export interface Customization { dashboard: { widgets: { component: string; + mobileOrder?: number; props: { [key: string]: any }; }[]; }; diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 80d6ca3cd..7f52a1b60 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -112,6 +112,7 @@ import { ClockComponent } from '../components/clock/clock.component'; import { CalculatorComponent } from '../components/calculator/calculator.component'; import { BitcoinsatoshisPipe } from '../shared/pipes/bitcoinsatoshis.pipe'; import { HttpErrorComponent } from '../shared/components/http-error/http-error.component'; +import { TwitterWidgetComponent } from '../components/twitter-widget/twitter-widget.component'; import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives'; @@ -224,6 +225,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AccelerateCheckout, PendingStatsComponent, HttpErrorComponent, + TwitterWidgetComponent, ], imports: [ CommonModule, @@ -351,6 +353,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AccelerateCheckout, PendingStatsComponent, HttpErrorComponent, + TwitterWidgetComponent, MempoolBlockOverviewComponent, ClockchainComponent, From 0990cfe0726161c9807c0003c20c478bb10952be Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 7 May 2024 01:03:59 +0000 Subject: [PATCH 02/36] Don't get stuck fetching orphans --- backend/src/api/blocks.ts | 7 ++-- backend/src/api/chain-tips.ts | 66 +++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 16ae94f66..42e15bf82 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -839,8 +839,11 @@ class Blocks { } else { this.currentBlockHeight++; logger.debug(`New block found (#${this.currentBlockHeight})!`); - this.updateTimerProgress(timer, `getting orphaned blocks for ${this.currentBlockHeight}`); - await chainTips.updateOrphanedBlocks(); + // skip updating the orphan block cache if we've fallen behind the chain tip + if (this.currentBlockHeight >= blockHeightTip - 2) { + this.updateTimerProgress(timer, `getting orphaned blocks for ${this.currentBlockHeight}`); + await chainTips.updateOrphanedBlocks(); + } } this.updateTimerProgress(timer, `getting block data for ${this.currentBlockHeight}`); diff --git a/backend/src/api/chain-tips.ts b/backend/src/api/chain-tips.ts index b68b0b281..b7fd05ad8 100644 --- a/backend/src/api/chain-tips.ts +++ b/backend/src/api/chain-tips.ts @@ -12,32 +12,68 @@ export interface OrphanedBlock { height: number; hash: string; status: 'valid-fork' | 'valid-headers' | 'headers-only'; + prevhash: string; } class ChainTips { private chainTips: ChainTip[] = []; - private orphanedBlocks: OrphanedBlock[] = []; + private orphanedBlocks: { [hash: string]: OrphanedBlock } = {}; + private blockCache: { [hash: string]: OrphanedBlock } = {}; + private orphansByHeight: { [height: number]: OrphanedBlock[] } = {}; public async updateOrphanedBlocks(): Promise { try { this.chainTips = await bitcoinClient.getChainTips(); - this.orphanedBlocks = []; + + const start = Date.now(); + const breakAt = start + 10000; + let newOrphans = 0; + this.orphanedBlocks = {}; for (const chain of this.chainTips) { if (chain.status === 'valid-fork' || chain.status === 'valid-headers') { - let block = await bitcoinClient.getBlock(chain.hash); - while (block && block.confirmations === -1) { - this.orphanedBlocks.push({ - height: block.height, - hash: block.hash, - status: chain.status - }); - block = await bitcoinClient.getBlock(block.previousblockhash); + const orphans: OrphanedBlock[] = []; + let hash = chain.hash; + do { + let orphan = this.blockCache[hash]; + if (!orphan) { + const block = await bitcoinClient.getBlock(hash); + if (block && block.confirmations === -1) { + newOrphans++; + orphan = { + height: block.height, + hash: block.hash, + status: chain.status, + prevhash: block.previousblockhash, + }; + this.blockCache[hash] = orphan; + } + } + if (orphan) { + orphans.push(orphan); + } + hash = orphan?.prevhash; + } while (hash && (Date.now() < breakAt)); + for (const orphan of orphans) { + this.orphanedBlocks[orphan.hash] = orphan; } } + if (Date.now() >= breakAt) { + logger.debug(`Breaking orphaned blocks updater after 10s, will continue next block`); + break; + } } - logger.debug(`Updated orphaned blocks cache. Found ${this.orphanedBlocks.length} orphaned blocks`); + this.orphansByHeight = {}; + const allOrphans = Object.values(this.orphanedBlocks); + for (const orphan of allOrphans) { + if (!this.orphansByHeight[orphan.height]) { + this.orphansByHeight[orphan.height] = []; + } + this.orphansByHeight[orphan.height].push(orphan); + } + + logger.debug(`Updated orphaned blocks cache. Fetched ${newOrphans} new orphaned blocks. Total ${allOrphans.length}`); } catch (e) { logger.err(`Cannot get fetch orphaned blocks. Reason: ${e instanceof Error ? e.message : e}`); } @@ -48,13 +84,7 @@ class ChainTips { return []; } - const orphans: OrphanedBlock[] = []; - for (const block of this.orphanedBlocks) { - if (block.height === height) { - orphans.push(block); - } - } - return orphans; + return this.orphansByHeight[height] || []; } } From 6a767ce5c4d61fc0bc5eaa76a5e23866a4be4931 Mon Sep 17 00:00:00 2001 From: natsoni Date: Mon, 22 Apr 2024 12:36:43 +0200 Subject: [PATCH 03/36] Make links color manageable with themes --- frontend/src/styles.scss | 9 +++++++++ frontend/src/theme-contrast.scss | 4 +++- frontend/src/theme-wiz.scss | 4 +++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index d92a3ec36..1825bbe6a 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -72,6 +72,8 @@ $dropdown-link-active-bg: $active-bg; --tertiary: #{$tertiary}; --success: #{$success}; --info: #{$info}; + --link-color: #{$info}; + --link-hover-color: #{$link-hover-color}; --icon: #f1f1f1; --box-bg: #24273e; @@ -1276,4 +1278,11 @@ app-global-footer { .btn-secondary { background-color: var(--secondary); border-color: var(--secondary); +} + +a { + color: var(--link-color); + &:hover { + color: var(--link-hover-color); + } } \ No newline at end of file diff --git a/frontend/src/theme-contrast.scss b/frontend/src/theme-contrast.scss index 470bad2e9..9b8af4528 100644 --- a/frontend/src/theme-contrast.scss +++ b/frontend/src/theme-contrast.scss @@ -21,7 +21,7 @@ $primary: #007cfa; $secondary: #272f4e; $tertiary: #6225b2; $success: #0aab2f; -$info: #10e0ff; +$info: #00ddff; $h5-font-size: 1.15rem !default; @@ -69,6 +69,8 @@ $dropdown-link-active-bg: $active-bg; --tertiary: #{$tertiary}; --success: #{$success}; --info: #{$info}; + --link-color: #{$info}; + --link-hover-color: #{$link-hover-color}; --icon: #f1f1f1; --box-bg: #171c2a; diff --git a/frontend/src/theme-wiz.scss b/frontend/src/theme-wiz.scss index 470bad2e9..9b8af4528 100644 --- a/frontend/src/theme-wiz.scss +++ b/frontend/src/theme-wiz.scss @@ -21,7 +21,7 @@ $primary: #007cfa; $secondary: #272f4e; $tertiary: #6225b2; $success: #0aab2f; -$info: #10e0ff; +$info: #00ddff; $h5-font-size: 1.15rem !default; @@ -69,6 +69,8 @@ $dropdown-link-active-bg: $active-bg; --tertiary: #{$tertiary}; --success: #{$success}; --info: #{$info}; + --link-color: #{$info}; + --link-hover-color: #{$link-hover-color}; --icon: #f1f1f1; --box-bg: #171c2a; From 4a76cb083aa3514d55fb21783c10fa1397720210 Mon Sep 17 00:00:00 2001 From: natsoni Date: Tue, 23 Apr 2024 18:59:26 +0200 Subject: [PATCH 04/36] More css included in color themes --- .../acceleration-fees-graph.component.scss | 3 +- .../accelerator-dashboard.component.scss | 3 +- .../address-graph.component.scss | 3 +- .../address/address-preview.component.scss | 2 +- .../components/address/address.component.scss | 2 +- .../app/components/asset/asset.component.scss | 2 +- .../block-fee-rates-graph.component.scss | 3 +- .../block-fees-graph.component.scss | 3 +- .../block-health-graph.component.scss | 3 +- .../block-overview-tooltip.component.scss | 2 +- .../block-rewards-graph.component.scss | 3 +- .../block-sizes-weights-graph.component.scss | 3 +- .../blockchain-blocks.component.scss | 4 +- .../blockchain-blocks.component.ts | 6 +- .../blockchain/blockchain.component.scss | 2 +- .../app/components/clock/clock.component.ts | 16 ++--- .../difficulty-mining.component.scss | 3 +- .../difficulty-tooltip.component.scss | 4 +- .../difficulty/difficulty.component.html | 4 +- .../difficulty/difficulty.component.scss | 5 +- .../fees-box/fees-box.component.scss | 2 +- .../components/fees-box/fees-box.component.ts | 4 +- .../hashrate-chart.component.scss | 3 +- .../hashrate-chart-pools.component.scss | 3 +- .../mempool-blocks.component.scss | 4 +- .../mining-dashboard.component.scss | 3 +- .../rbf-timeline-tooltip.component.scss | 4 +- .../rbf-timeline/rbf-timeline.component.scss | 4 +- .../transactions-list.component.scss | 3 +- .../tx-bowtie-graph-tooltip.component.scss | 2 +- .../tx-bowtie-graph.component.ts | 4 +- .../app/dashboard/dashboard.component.scss | 6 +- .../lightning-dashboard.component.scss | 3 +- .../nodes-networks-chart.component.scss | 3 +- .../lightning-statistics-chart.component.scss | 3 +- frontend/src/styles.scss | 65 +++++++++++-------- frontend/src/theme-contrast.scss | 10 +-- frontend/src/theme-wiz.scss | 10 +-- 38 files changed, 124 insertions(+), 88 deletions(-) diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss index 96273dead..984bc2b4c 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss index 563e189cf..e6763f60a 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss @@ -60,7 +60,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/address-graph/address-graph.component.scss b/frontend/src/app/components/address-graph/address-graph.component.scss index 3752203c1..62393644b 100644 --- a/frontend/src/app/components/address-graph/address-graph.component.scss +++ b/frontend/src/app/components/address-graph/address-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/address/address-preview.component.scss b/frontend/src/app/components/address/address-preview.component.scss index 623d72db2..f03e13541 100644 --- a/frontend/src/app/components/address/address-preview.component.scss +++ b/frontend/src/app/components/address/address-preview.component.scss @@ -3,7 +3,7 @@ } .qr-wrapper { - background-color: var(--fg); + background-color: #fff; padding: 10px; padding-bottom: 5px; display: inline-block; diff --git a/frontend/src/app/components/address/address.component.scss b/frontend/src/app/components/address/address.component.scss index 78ca0e80d..da615376c 100644 --- a/frontend/src/app/components/address/address.component.scss +++ b/frontend/src/app/components/address/address.component.scss @@ -1,5 +1,5 @@ .qr-wrapper { - background-color: var(--fg); + background-color: #fff; padding: 10px; padding-bottom: 5px; display: inline-block; diff --git a/frontend/src/app/components/asset/asset.component.scss b/frontend/src/app/components/asset/asset.component.scss index 6f8bc0915..56b1d6258 100644 --- a/frontend/src/app/components/asset/asset.component.scss +++ b/frontend/src/app/components/asset/asset.component.scss @@ -1,5 +1,5 @@ .qr-wrapper { - background-color: var(--fg); + background-color: #fff; padding: 10px; padding-bottom: 5px; display: inline-block; diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss index ed531e63d..5d8c286d3 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss index b73d55685..ec3aeadc8 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.scss b/frontend/src/app/components/block-health-graph/block-health-graph.component.scss index 7b8154bae..992906e5c 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.scss +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss index 507d4c18d..2125c3128 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss @@ -1,6 +1,6 @@ .block-overview-tooltip { position: absolute; - background: rgba(#11131f, 0.95); + background: color-mix(in srgb, var(--active-bg) 95%, transparent); border-radius: 4px; box-shadow: 1px 1px 10px rgba(0,0,0,0.5); color: var(--tooltip-grey); diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss index 7b8154bae..992906e5c 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss index 7b8154bae..992906e5c 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss index 269fdab42..b11a35513 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss @@ -63,7 +63,7 @@ .fee-span { font-size: 11px; margin-bottom: 5px; - color: #fff000; + color: var(--yellow); } .transaction-count { @@ -130,7 +130,7 @@ height: 0; border-left: calc(var(--block-size) * 0.3) solid transparent; border-right: calc(var(--block-size) * 0.3) solid transparent; - border-bottom: calc(var(--block-size) * 0.3) solid #FFF; + border-bottom: calc(var(--block-size) * 0.3) solid var(--fg); } .flashing { diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 1d0e284f8..1a7598079 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -350,7 +350,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { return { left: addLeft + this.blockOffset * index + 'px', background: `repeating-linear-gradient( - #2d3348, + var(--secondary), var(--secondary) ${greenBackgroundHeight}%, ${this.gradientColors[this.network][0]} ${Math.max(greenBackgroundHeight, 0)}%, ${this.gradientColors[this.network][1]} 100% @@ -362,7 +362,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { convertStyleForLoadingBlock(style) { return { ...style, - background: "#2d3348", + background: "var(--secondary)", }; } @@ -371,7 +371,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { return { left: addLeft + (this.blockOffset * index) + 'px', - background: "#2d3348", + background: "var(--secondary)", }; } diff --git a/frontend/src/app/components/blockchain/blockchain.component.scss b/frontend/src/app/components/blockchain/blockchain.component.scss index b0a589a04..4da3746e4 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.scss +++ b/frontend/src/app/components/blockchain/blockchain.component.scss @@ -54,7 +54,7 @@ } .time-toggle { - color: white; + color: var(--fg); font-size: 0.8rem; position: absolute; bottom: -1.8em; diff --git a/frontend/src/app/components/clock/clock.component.ts b/frontend/src/app/components/clock/clock.component.ts index 90f24a753..4a9b19e78 100644 --- a/frontend/src/app/components/clock/clock.component.ts +++ b/frontend/src/app/components/clock/clock.component.ts @@ -32,12 +32,12 @@ export class ClockComponent implements OnInit { limitHeight: number; gradientColors = { - '': ['#9339f4', '#105fb0'], - liquid: ['#116761', '#183550'], - 'liquidtestnet': ['#494a4a', '#272e46'], - testnet: ['#1d486f', '#183550'], - testnet4: ['#1d486f', '#183550'], - signet: ['#6f1d5d', '#471850'], + '': ['var(--mainnet-alt)', 'var(--primary)'], + liquid: ['var(--liquid)', 'var(--testnet-alt)'], + 'liquidtestnet': ['var(--liquidtestnet)', 'var(--liquidtestnet-alt)'], + testnet: ['var(--testnet)', 'var(--testnet-alt)'], + testnet4: ['var(--testnet)', 'var(--testnet-alt)'], + signet: ['var(--signet)', 'var(--signet-alt)'], }; constructor( @@ -100,8 +100,8 @@ export class ClockComponent implements OnInit { return { background: `repeating-linear-gradient( - #2d3348, - #2d3348 ${greenBackgroundHeight}%, + var(--secondary), + var(--secondary) ${greenBackgroundHeight}%, ${this.gradientColors[''][0]} ${Math.max(greenBackgroundHeight, 0)}%, ${this.gradientColors[''][1]} 100% )`, diff --git a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.scss b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.scss index 77f54f267..bd396928f 100644 --- a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.scss +++ b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.scss @@ -119,7 +119,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/difficulty/difficulty-tooltip.component.scss b/frontend/src/app/components/difficulty/difficulty-tooltip.component.scss index 5b4a8a02f..e4fd989af 100644 --- a/frontend/src/app/components/difficulty/difficulty-tooltip.component.scss +++ b/frontend/src/app/components/difficulty/difficulty-tooltip.component.scss @@ -1,9 +1,9 @@ .difficulty-tooltip { position: fixed; - background: rgba(#11131f, 0.95); + background: color-mix(in srgb, var(--active-bg) 95%, transparent); border-radius: 4px; box-shadow: 1px 1px 10px rgba(0,0,0,0.5); - color: #b1b1b1; + color: var(--tooltip-grey); padding: 10px 15px; text-align: left; pointer-events: none; diff --git a/frontend/src/app/components/difficulty/difficulty.component.html b/frontend/src/app/components/difficulty/difficulty.component.html index c9b3d183b..e9bf36515 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.html +++ b/frontend/src/app/components/difficulty/difficulty.component.html @@ -15,8 +15,8 @@ - - + + diff --git a/frontend/src/app/components/difficulty/difficulty.component.scss b/frontend/src/app/components/difficulty/difficulty.component.scss index d10b800a8..8de7fae2c 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.scss +++ b/frontend/src/app/components/difficulty/difficulty.component.scss @@ -128,7 +128,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; @@ -223,7 +224,7 @@ height: 100%; } .background { - background: linear-gradient(to right, var(--primary), #9339f4); + background: linear-gradient(to right, var(--primary), var(--mainnet-alt)); left: 0; right: 0; } diff --git a/frontend/src/app/components/fees-box/fees-box.component.scss b/frontend/src/app/components/fees-box/fees-box.component.scss index 0272936ee..c5843f58b 100644 --- a/frontend/src/app/components/fees-box/fees-box.component.scss +++ b/frontend/src/app/components/fees-box/fees-box.component.scss @@ -79,7 +79,7 @@ display: flex; flex-direction: row; transition: background-color 1s; - color: var(--color-fg); + color: #fff; &.priority { @media (767px < width < 992px), (width < 576px) { width: 100%; diff --git a/frontend/src/app/components/fees-box/fees-box.component.ts b/frontend/src/app/components/fees-box/fees-box.component.ts index e923b26e9..78fd102ca 100644 --- a/frontend/src/app/components/fees-box/fees-box.component.ts +++ b/frontend/src/app/components/fees-box/fees-box.component.ts @@ -16,8 +16,8 @@ export class FeesBoxComponent implements OnInit, OnDestroy { isLoading$: Observable; recommendedFees$: Observable; themeSubscription: Subscription; - gradient = 'linear-gradient(to right, #2e324e, #2e324e)'; - noPriority = '#2e324e'; + gradient = 'linear-gradient(to right, var(--skeleton-bg), var(--skeleton-bg))'; + noPriority = 'var(--skeleton-bg)'; fees: Recommendedfees; constructor( diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index dc19dc9d1..b6b49eed2 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss index 64a4dcb3d..a2e3b8866 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss index 660ad6f54..5f7b15092 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.scss @@ -56,7 +56,7 @@ .fee-span { font-size: 11px; margin-bottom: 5px; - color: #fff000; + color: var(--yellow); } .transaction-count { @@ -119,7 +119,7 @@ height: 0; border-left: calc(var(--block-size) * 0.3) solid transparent; border-right: calc(var(--block-size) * 0.3) solid transparent; - border-bottom: calc(var(--block-size) * 0.3) solid #FFF; + border-bottom: calc(var(--block-size) * 0.3) solid var(--fg); } .blockLink { diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index a7948b40e..f1c49c9e7 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -63,7 +63,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.scss b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.scss index cd31aa562..eeb40a52d 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.scss +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.scss @@ -1,10 +1,10 @@ .rbf-tooltip { position: fixed; z-index: 3; - background: rgba(#11131f, 0.95); + background: color-mix(in srgb, var(--active-bg) 95%, transparent); border-radius: 4px; box-shadow: 1px 1px 10px rgba(0,0,0,0.5); - color: #b1b1b1; + color: var(--tooltip-grey); display: flex; flex-direction: column; justify-content: space-between; diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss index 6803e82ae..1026d6ea9 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss @@ -159,7 +159,7 @@ &.selected { .shape-border { - background: #9339f4; + background: var(--mainnet-alt); } } @@ -183,7 +183,7 @@ width: calc(1em + 16px); .shape { - border: solid 4px #9339f4; + border: solid 4px var(--mainnet-alt); } &:hover { diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.scss b/frontend/src/app/components/transactions-list/transactions-list.component.scss index 2bae3de2c..7efe0ef11 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.scss +++ b/frontend/src/app/components/transactions-list/transactions-list.component.scss @@ -103,7 +103,8 @@ td.amount.large { margin-top: 10px; } .assetBox { - background-color: #653b9c90; + background: color-mix(in srgb, var(--tertiary) 56%, transparent); + } .details-container { diff --git a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss index 0c973ad00..dfb429096 100644 --- a/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss +++ b/frontend/src/app/components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component.scss @@ -1,6 +1,6 @@ .bowtie-graph-tooltip { position: absolute; - background: rgba(#11131f, 0.95); + background: color-mix(in srgb, var(--active-bg) 95%, transparent); border-radius: 4px; box-shadow: 1px 1px 10px rgba(0,0,0,0.5); color: var(--tooltip-grey); diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 30e85a6e7..f371fdf0a 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -84,7 +84,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { refreshOutspends$: ReplaySubject = new ReplaySubject(); gradientColors = { - '': ['#9339f4', '#105fb0', '#9339f400'], + '': ['var(--mainnet-alt)', 'var(--primary)', 'color-mix(in srgb, var(--mainnet-alt) 1%, transparent)'], // liquid: ['#116761', '#183550'], liquid: ['#09a197', '#0f62af', '#09a19700'], // 'liquidtestnet': ['#494a4a', '#272e46'], @@ -96,7 +96,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { signet: ['#d24fc8', '#a84fd2', '#d24fc800'], }; - gradient: string[] = ['#105fb0', '#105fb0']; + gradient: string[] = ['var(--primary)', 'var(--primary)']; constructor( private router: Router, diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index 84eabead2..9ad09981f 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -301,7 +301,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; @@ -435,7 +436,8 @@ .in-progress-message { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: 20px; text-align: center; padding-bottom: 3px; diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss index 7fb011146..f125b30c1 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss @@ -66,7 +66,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss index ab8cb92da..a747586ad 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss index 1779394c9..165c513e5 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss @@ -11,7 +11,8 @@ .main-title { position: relative; - color: #ffffff91; + color: var(--fg); + opacity: var(--opacity); margin-top: -13px; font-size: 10px; text-transform: uppercase; diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 1825bbe6a..12783a332 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -64,7 +64,6 @@ $dropdown-link-active-bg: $active-bg; --active-bg: #{$active-bg}; --hover-bg: #{$hover-bg}; --fg: #{$fg}; - --color-fg: #ffffff; --title-fg: #{$title-fg}; --primary: #{$primary}; @@ -72,9 +71,11 @@ $dropdown-link-active-bg: $active-bg; --tertiary: #{$tertiary}; --success: #{$success}; --info: #{$info}; - --link-color: #{$info}; + --link-color: #{$link-color}; --link-hover-color: #{$link-hover-color}; --icon: #f1f1f1; + --skeleton-bg: #2e324e; + --skeleton-bg-light: #5d6182; --box-bg: #24273e; --stat-box-bg: #181b2d; @@ -83,6 +84,7 @@ $dropdown-link-active-bg: $active-bg; --transparent-fg: #ffffff66; --fade-out-box-bg-start: rgba(36, 39, 62, 0); --fade-out-box-bg-end: rgba(36, 39, 62, 1); + --opacity: 0.57; --testnet: #1d486f; --signet: #6f1d5d; @@ -100,7 +102,7 @@ $dropdown-link-active-bg: $active-bg; --green: #3bcc49; --red: #dc3545; - --yellow: #ffd800; + --yellow: #fff000; --grey: #6c757d; --tooltip-grey: #b1b1b1; --orange: #b86d12; @@ -173,13 +175,13 @@ main { } .form-control { - color: #fff; + color: var(--fg); background-color: var(--secondary); border: 1px solid rgba(17, 19, 31, 0.2); } .form-control:focus { - color: #fff; + color: var(--fg); background-color: var(--secondary); } @@ -208,7 +210,7 @@ main { } .form-control.form-control-secondary { - color: var(--fg); + color: #fff; background-color: var(--secondary); border: 1px solid var(--secondary); } @@ -231,11 +233,11 @@ main { position: relative; animation: progress 2s ease-in-out infinite; - background: #2e324e no-repeat; + background: var(--skeleton-bg) no-repeat; background-image: linear-gradient( 90deg, rgba(255, 255, 255, 0), - #5d6182, + var(--skeleton-bg-light), rgba(255, 255, 255, 0) ); background-size: 200px 100%; @@ -273,14 +275,14 @@ main { .progress-text { span { - color: var(--fg) !important; + color: #fff !important; } } .block-size, .blocks-container { .symbol { font-size: 16px; - color: var(--fg) !important; + color: #fff !important; } } @@ -420,18 +422,18 @@ html:lang(ru) .card-title { font-size: 12px; font-weight: 700; margin-bottom: 2px; - color: var(--fg); + color: #fff; .total-value { float: right; } } .active { - color: yellow !important; + color: var(--yellow) !important; .value, .total-partial { - color: yellow !important; + color: var(--yellow) !important; .symbol { - color: yellow !important; + color: var(--yellow) !important; } } } @@ -479,7 +481,7 @@ html:lang(ru) .card-title { .total-label { width: 100%; text-align: left; - color: var(--fg); + color: #fff; margin-top: 5px; font-size: 14px; span { @@ -730,19 +732,19 @@ h1, h2, h3 { } .progress-mempool { - background: repeating-linear-gradient(to right, $secondary, $secondary 0%, $primary 0%, var(--mainnet-alt) 100%); + background: repeating-linear-gradient(to right, var(--secondary), var(--secondary) 0%, var(--primary) 0%, var(--mainnet-alt) 100%); } .progress-mempool.testnet { - background: repeating-linear-gradient(to right, $secondary, $secondary 0%, var(--testnet) 0%, var(--testnet-alt) 100%); + background: repeating-linear-gradient(to right, var(--secondary), var(--secondary) 0%, var(--testnet) 0%, var(--testnet-alt) 100%); } .progress-mempool.signet { - background: repeating-linear-gradient(to right, $secondary, $secondary 0%, var(--signet) 0%, var(--signet-alt) 100%); + background: repeating-linear-gradient(to right, var(--secondary), var(--secondary) 0%, var(--signet) 0%, var(--signet-alt) 100%); } .progress-mempool.liquid { - background: repeating-linear-gradient(to right, $secondary, $secondary 0%, var(--liquid) 0%, var(--testnet-alt) 100%); + background: repeating-linear-gradient(to right, var(--secondary), var(--secondary) 0%, var(--liquid) 0%, var(--testnet-alt) 100%); } .progress-dark { @@ -754,11 +756,11 @@ h1, h2, h3 { } .progress-light { - background-color: #2e324e; + background-color: var(--skeleton-bg); } .progress.progress-health { - background: repeating-linear-gradient(to right, $secondary, $secondary 0%, $primary 0%, $success 100%); + background: repeating-linear-gradient(to right, var(--secondary), var(--secondary) 0%, var(--primary) 0%, $success 100%); justify-content: flex-end; } @@ -771,7 +773,7 @@ h1, h2, h3 { } .alert-mempool { - color: var(--fg); + color: #fff; background-color: var(--tertiary); border-color: var(--alert-bg); width: 100%; @@ -1139,7 +1141,7 @@ th { display: block; width: 100%; padding: 1rem 2rem; - color: var(--fg); + color: #fff; font-weight: bold; &:focus, &:hover, &:active { text-decoration: none; @@ -1223,7 +1225,7 @@ th { } .blink-bg { - color: var(--fg); + color: #fff; background: repeating-linear-gradient($taproot-dark, $taproot-dark 0.163525%, $taproot-light 100%) !important; animation: shadowyBackground 1s infinite; box-shadow: -10px -15px 75px rgba($taproot, 1); @@ -1246,7 +1248,7 @@ app-master-page, app-liquid-master-page { display: flex; flex-direction: column; min-height: 100vh; - padding-bottom: 60px; + padding-bottom: 56px; @media (min-width: 992px) { padding-bottom: 0px; } @@ -1270,8 +1272,10 @@ app-global-footer { .dropdown-menu { background-color: var(--bg); + color: var(--fg); .dropdown-item:hover, .dropdown-item:focus { background-color: var(--active-bg); + color: var(--fg); } } @@ -1285,4 +1289,13 @@ a { &:hover { color: var(--link-hover-color); } -} \ No newline at end of file +} + +.badge-primary { + color: var(--fg); + background-color: var(--primary); +} + +.badge-info { + background-color: var(--info); +} diff --git a/frontend/src/theme-contrast.scss b/frontend/src/theme-contrast.scss index 9b8af4528..a3faddaef 100644 --- a/frontend/src/theme-contrast.scss +++ b/frontend/src/theme-contrast.scss @@ -61,7 +61,6 @@ $dropdown-link-active-bg: $active-bg; --active-bg: #{$active-bg}; --hover-bg: #{$hover-bg}; --fg: #{$fg}; - --color-fg: #fff; --title-fg: #{$title-fg}; --primary: #{$primary}; @@ -69,17 +68,20 @@ $dropdown-link-active-bg: $active-bg; --tertiary: #{$tertiary}; --success: #{$success}; --info: #{$info}; - --link-color: #{$info}; + --link-color: #{$link-color}; --link-hover-color: #{$link-hover-color}; --icon: #f1f1f1; + --skeleton-bg: #2e324e; + --skeleton-bg-light: #5d6182; --box-bg: #171c2a; --stat-box-bg: #0b1018; --alert-bg: #3a1c61; - --navbar-bg: #212121; + --navbar-bg: #121212; --transparent-fg: #ffffffbb; --fade-out-box-bg-start: rgba(23, 28, 42, 0); --fade-out-box-bg-end: rgba(23, 28, 42, 1); + --opacity: 0.9; --testnet: #1d486f; --signet: #6f1d5d; @@ -97,7 +99,7 @@ $dropdown-link-active-bg: $active-bg; --green: #83fd00; --red: #ff3d00; - --yellow: #ffcc00; + --yellow: #fff000; --grey: #7e7e7e; --tooltip-grey: #b1b1b1; --orange: #ff9f00; diff --git a/frontend/src/theme-wiz.scss b/frontend/src/theme-wiz.scss index 9b8af4528..f46576c11 100644 --- a/frontend/src/theme-wiz.scss +++ b/frontend/src/theme-wiz.scss @@ -61,7 +61,6 @@ $dropdown-link-active-bg: $active-bg; --active-bg: #{$active-bg}; --hover-bg: #{$hover-bg}; --fg: #{$fg}; - --color-fg: #fff; --title-fg: #{$title-fg}; --primary: #{$primary}; @@ -69,17 +68,20 @@ $dropdown-link-active-bg: $active-bg; --tertiary: #{$tertiary}; --success: #{$success}; --info: #{$info}; - --link-color: #{$info}; + --link-color: #{$link-color}; --link-hover-color: #{$link-hover-color}; --icon: #f1f1f1; + --skeleton-bg: #2e324e; + --skeleton-bg-light: #5d6182; --box-bg: #171c2a; --stat-box-bg: #0b1018; --alert-bg: #3a1c61; - --navbar-bg: #212121; + --navbar-bg: #121212; --transparent-fg: #ffffffbb; --fade-out-box-bg-start: rgba(23, 28, 42, 0); --fade-out-box-bg-end: rgba(23, 28, 42, 1); + --opacity: 0.57; --testnet: #1d486f; --signet: #6f1d5d; @@ -97,7 +99,7 @@ $dropdown-link-active-bg: $active-bg; --green: #83fd00; --red: #ff3d00; - --yellow: #ffcc00; + --yellow: #fff000; --grey: #7e7e7e; --tooltip-grey: #b1b1b1; --orange: #ff9f00; From a7e501570ccabf37d7eda9e6546b89d90ea26663 Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 26 Apr 2024 16:05:51 +0200 Subject: [PATCH 05/36] Add El Salvador theme --- frontend/angular.json | 5 + .../theme-selector.component.html | 1 + .../theme-selector.component.ts | 2 +- frontend/src/theme-bukele.scss | 106 ++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 frontend/src/theme-bukele.scss diff --git a/frontend/angular.json b/frontend/angular.json index 46cc3f667..190982225 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -181,6 +181,11 @@ "bundleName": "wiz", "inject": false }, + { + "input": "src/theme-bukele.scss", + "bundleName": "bukele", + "inject": false + }, "node_modules/@fortawesome/fontawesome-svg-core/styles.css" ], "vendorChunk": true, diff --git a/frontend/src/app/components/theme-selector/theme-selector.component.html b/frontend/src/app/components/theme-selector/theme-selector.component.html index 9bacaa08b..ecb96a99c 100644 --- a/frontend/src/app/components/theme-selector/theme-selector.component.html +++ b/frontend/src/app/components/theme-selector/theme-selector.component.html @@ -3,5 +3,6 @@ +
diff --git a/frontend/src/app/components/theme-selector/theme-selector.component.ts b/frontend/src/app/components/theme-selector/theme-selector.component.ts index 57cbee865..be207910c 100644 --- a/frontend/src/app/components/theme-selector/theme-selector.component.ts +++ b/frontend/src/app/components/theme-selector/theme-selector.component.ts @@ -11,7 +11,7 @@ import { Subscription } from 'rxjs'; }) export class ThemeSelectorComponent implements OnInit { themeForm: UntypedFormGroup; - themes = ['default', 'contrast', 'wiz']; + themes = ['default', 'contrast', 'wiz', 'bukele']; themeSubscription: Subscription; constructor( diff --git a/frontend/src/theme-bukele.scss b/frontend/src/theme-bukele.scss new file mode 100644 index 000000000..e9124194e --- /dev/null +++ b/frontend/src/theme-bukele.scss @@ -0,0 +1,106 @@ +/* Theme */ +$bg: #1c1e4d; +$active-bg: #313945; +$hover-bg: #E5E5E5; +$fg: #FFFFFF; +$title-fg: #c9a892; + +$taproot: #eba814; +$taproot-light: #d5a90a; +$taproot-dark: #9d7c05; + +/* Bootstrap */ +$body-bg: $bg; +$body-color: $fg; +$gray-800: $bg; +$gray-700: $fg; + +$nav-tabs-link-active-bg: $active-bg; + +$primary: #343992; +$secondary: #32345e; +$tertiary: #a08674; +$success: #009900; +$info: #e4d3c8; + +$h5-font-size: 1.15rem !default; + +$pagination-bg: $body-bg; +$pagination-border-color: $gray-800; +$pagination-disabled-bg: $fg; +$pagination-disabled-border-color: $bg; +$pagination-active-color: $fg; +$pagination-active-bg: $tertiary; +$pagination-hover-bg: $hover-bg; +$pagination-hover-border-color: $bg; +$pagination-disabled-bg: $bg; + +$custom-select-indicator-color: $fg; + +.input-group-text { + background-color: #1c2031 !important; + border: 1px solid #20263e !important; +} + +$link-color: $info; +$link-decoration: none !default; +$link-hover-color: darken($link-color, 15%) !default; +$link-hover-decoration: underline !default; + +$dropdown-bg: $bg; +$dropdown-link-color: $fg; + +$dropdown-link-hover-color: $fg; +$dropdown-link-hover-bg: $active-bg; + +$dropdown-link-active-color: $fg; +$dropdown-link-active-bg: $active-bg; + +:root { + --bg: #{$bg}; + --active-bg: #{$active-bg}; + --hover-bg: #{$hover-bg}; + --fg: #{$fg}; + --title-fg: #{$title-fg}; + + --primary: #{$primary}; + --secondary: #{$secondary}; + --tertiary: #{$tertiary}; + --success: #{$success}; + --info: #{$info}; + --link-color: #{$link-color}; + --link-hover-color: #{$link-hover-color}; + --icon: #f1f1f1; + --skeleton-bg: #2e324e; + --skeleton-bg-light: #5d6182; + + --box-bg: #15173a; + --stat-box-bg: #0e0f28; + --alert-bg: #3a1c61; + --navbar-bg: #212121; + --transparent-fg: #ffffffbb; + --fade-out-box-bg-start: rgba(23, 28, 42, 0); + --fade-out-box-bg-end: rgba(23, 28, 42, 1); + --opacity: 0.7; + + --testnet: #1d486f; + --signet: #6f1d5d; + --liquid: #116761; + --liquidtestnet: #494a4a; + + --mainnet-alt: #a08674; + --testnet-alt: #183550; + --signet-alt: #471850; + --liquidtestnet-alt: #272e46; + + --taproot: #eba814; + --taproot-light: #d5a90a; + --taproot-dark: #9d7c05; + + --green: #83fd00; + --red: #ff3d00; + --yellow: #fff000; + --grey: #7e7e7e; + --tooltip-grey: #b1b1b1; + --orange: #ff9f00; +} From 463e66081c0d30e0d6e4db67e0e6623dd5881e53 Mon Sep 17 00:00:00 2001 From: natsoni Date: Fri, 26 Apr 2024 16:59:25 +0200 Subject: [PATCH 06/36] CSS theme changes --- .../accelerator-dashboard.component.ts | 2 +- .../block-overview-graph.component.ts | 4 +-- .../block-overview-graph/block-scene.ts | 4 +-- .../components/block-overview-graph/utils.ts | 2 +- .../block-overview-tooltip.component.scss | 6 ++--- .../blockchain/blockchain.component.scss | 2 +- .../theme-selector.component.html | 2 +- frontend/src/app/services/theme.service.ts | 2 +- frontend/src/styles.scss | 25 ++++++++++++----- frontend/src/theme-bukele.scss | 27 +++++++++++-------- frontend/src/theme-contrast.scss | 5 ++++ frontend/src/theme-wiz.scss | 5 ++++ 12 files changed, 56 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index 282927b4a..5f9017bbd 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -148,7 +148,7 @@ export class AcceleratorDashboardComponent implements OnInit, OnDestroy { } else { const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1; - return this.theme.theme === 'contrast' ? contrastColors[feeLevelIndex] || contrastColors[contrastColors.length - 1] : normalColors[feeLevelIndex] || normalColors[normalColors.length - 1]; + return this.theme.theme === 'contrast' || this.theme.theme === 'bukele' ? contrastColors[feeLevelIndex] || contrastColors[contrastColors.length - 1] : normalColors[feeLevelIndex] || normalColors[normalColors.length - 1]; } } diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index ceb12738d..6231ba70d 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -651,13 +651,13 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On getFilterColorFunction(flags: bigint, gradient: 'fee' | 'age'): ((tx: TxView) => Color) { return (tx: TxView) => { if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) { - if (this.themeService.theme !== 'contrast') { + if (this.themeService.theme !== 'contrast' && this.themeService.theme !== 'bukele') { return (gradient === 'age') ? ageColorFunction(tx, defaultColors.fee, defaultAuditColors, this.relativeTime || (Date.now() / 1000)) : defaultColorFunction(tx, defaultColors.fee, defaultAuditColors, this.relativeTime || (Date.now() / 1000)); } else { return (gradient === 'age') ? ageColorFunction(tx, contrastColors.fee, contrastAuditColors, this.relativeTime || (Date.now() / 1000)) : contrastColorFunction(tx, contrastColors.fee, contrastAuditColors, this.relativeTime || (Date.now() / 1000)); } } else { - if (this.themeService.theme !== 'contrast') { + if (this.themeService.theme !== 'contrast' && this.themeService.theme !== 'bukele') { return (gradient === 'age') ? { r: 1, g: 1, b: 1, a: 0.05 } : defaultColorFunction( tx, defaultColors.unmatchedfee, diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index 9dd76dec9..c59fcb7d4 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -69,7 +69,7 @@ export default class BlockScene { } setColorFunction(colorFunction: ((tx: TxView) => Color) | null): void { - this.theme.theme === 'contrast' ? this.getColor = colorFunction || contrastColorFunction : this.getColor = colorFunction || defaultColorFunction; + this.theme.theme === 'contrast' || this.theme.theme === 'bukele' ? this.getColor = colorFunction || contrastColorFunction : this.getColor = colorFunction || defaultColorFunction; this.updateAllColors(); } @@ -246,7 +246,7 @@ export default class BlockScene { this.flip = flip; this.vertexArray = vertexArray; this.highlightingEnabled = highlighting; - theme.theme === 'contrast' ? this.getColor = colorFunction || contrastColorFunction : this.getColor = colorFunction || defaultColorFunction; + theme.theme === 'contrast' || theme.theme === 'bukele' ? this.getColor = colorFunction || contrastColorFunction : this.getColor = colorFunction || defaultColorFunction; this.theme = theme; this.scene = { diff --git a/frontend/src/app/components/block-overview-graph/utils.ts b/frontend/src/app/components/block-overview-graph/utils.ts index ec6181853..9a6d9da43 100644 --- a/frontend/src/app/components/block-overview-graph/utils.ts +++ b/frontend/src/app/components/block-overview-graph/utils.ts @@ -177,7 +177,7 @@ export function ageColorFunction( return auditColors.accelerated; } - const color = theme !== 'contrast' ? defaultColorFunction(tx, colors, auditColors, relativeTime) : contrastColorFunction(tx, colors, auditColors, relativeTime); + const color = theme !== 'contrast' && theme !== 'bukele' ? defaultColorFunction(tx, colors, auditColors, relativeTime) : contrastColorFunction(tx, colors, auditColors, relativeTime); const ageLevel = (!tx.time ? 0 : (0.8 * Math.tanh((1 / 15) * Math.log2((Math.max(1, 0.6 * ((relativeTime - tx.time) - 60))))))); return { diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss index 2125c3128..28708506b 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.scss @@ -30,7 +30,7 @@ th, td { } .badge.badge-accelerated { - background-color: var(--tertiary); + background-color: #653b9c; box-shadow: #ad7de57f 0px 0px 12px -2px; color: white; animation: acceleratePulse 1s infinite; @@ -71,7 +71,7 @@ th, td { } @keyframes acceleratePulse { - 0% { background-color: var(--tertiary); box-shadow: #ad7de57f 0px 0px 12px -2px; } + 0% { background-color: #653b9c; box-shadow: #ad7de57f 0px 0px 12px -2px; } 50% { background-color: #8457bb; box-shadow: #ad7de5 0px 0px 18px -2px;} - 100% { background-color: var(--tertiary); box-shadow: #ad7de57f 0px 0px 12px -2px; } + 100% { background-color: #653b9c; box-shadow: #ad7de57f 0px 0px 12px -2px; } } \ No newline at end of file diff --git a/frontend/src/app/components/blockchain/blockchain.component.scss b/frontend/src/app/components/blockchain/blockchain.component.scss index 4da3746e4..700f57a27 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.scss +++ b/frontend/src/app/components/blockchain/blockchain.component.scss @@ -68,7 +68,7 @@ } .block-display-toggle { - color: white; + color: var(--fg); font-size: 0.8rem; position: absolute; bottom: 15.8em; diff --git a/frontend/src/app/components/theme-selector/theme-selector.component.html b/frontend/src/app/components/theme-selector/theme-selector.component.html index ecb96a99c..1610e816d 100644 --- a/frontend/src/app/components/theme-selector/theme-selector.component.html +++ b/frontend/src/app/components/theme-selector/theme-selector.component.html @@ -3,6 +3,6 @@ - +
diff --git a/frontend/src/app/services/theme.service.ts b/frontend/src/app/services/theme.service.ts index 7981f37a3..3bdb1c65b 100644 --- a/frontend/src/app/services/theme.service.ts +++ b/frontend/src/app/services/theme.service.ts @@ -24,7 +24,7 @@ export class ThemeService { apply(theme) { this.theme = theme; if (theme !== 'default') { - theme === 'contrast' ? this.mempoolFeeColors = contrastMempoolFeeColors : this.mempoolFeeColors = defaultMempoolFeeColors; + theme === 'contrast' || theme === 'bukele' ? this.mempoolFeeColors = contrastMempoolFeeColors : this.mempoolFeeColors = defaultMempoolFeeColors; try { if (!this.style) { this.style = document.createElement('link'); diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 12783a332..dd0ce8bef 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -106,6 +106,11 @@ $dropdown-link-active-bg: $active-bg; --grey: #6c757d; --tooltip-grey: #b1b1b1; --orange: #b86d12; + + --search-button: #4d2d77; + --search-button-border: #472a6e; + --search-button-focus: #533180; + --search-button-shadow: #7c58ab80; } html, body { @@ -192,21 +197,21 @@ main { .btn-purple:not(:disabled):not(.disabled):active, .btn-purple:not(:disabled):not(.disabled).active, .show > .btn-purple.dropdown-toggle { color: #fff; - background-color: #4d2d77; - border-color: #472a6e; + background-color: var(--search-button); + border-color: var(--search-button-border); } .btn-purple:focus, .btn-purple.focus { color: #fff; - background-color: #533180; - border-color: #4d2d77; - box-shadow: 0 0 0 0.2rem rgb(124 88 171 / 50%); + background-color: var(--search-button-focus); + border-color: var(--search-button); + box-shadow: 0 0 0 0.2rem var(--search-button-shadow); } .btn-purple:hover { color: #fff; - background-color: #533180; - border-color: #4d2d77; + background-color: var(--search-button-focus); + border-color: var(--search-button); } .form-control.form-control-secondary { @@ -1299,3 +1304,9 @@ a { .badge-info { background-color: var(--info); } + +.btn-primary { + color: var(--fg); + background-color: var(--primary); + border-color: var(--primary); +} \ No newline at end of file diff --git a/frontend/src/theme-bukele.scss b/frontend/src/theme-bukele.scss index e9124194e..e81058dab 100644 --- a/frontend/src/theme-bukele.scss +++ b/frontend/src/theme-bukele.scss @@ -1,9 +1,9 @@ /* Theme */ -$bg: #1c1e4d; -$active-bg: #313945; +$bg: #313945; +$active-bg: #000000; $hover-bg: #E5E5E5; -$fg: #FFFFFF; -$title-fg: #c9a892; +$fg: #ffffff; +$title-fg: #c19a81; $taproot: #eba814; $taproot-light: #d5a90a; @@ -17,11 +17,11 @@ $gray-700: $fg; $nav-tabs-link-active-bg: $active-bg; -$primary: #343992; -$secondary: #32345e; -$tertiary: #a08674; +$primary: #007cfa; +$secondary: #1c1e4d; +$tertiary: #8c7566; $success: #009900; -$info: #e4d3c8; +$info: #dac3b4; $h5-font-size: 1.15rem !default; @@ -74,14 +74,14 @@ $dropdown-link-active-bg: $active-bg; --skeleton-bg: #2e324e; --skeleton-bg-light: #5d6182; - --box-bg: #15173a; - --stat-box-bg: #0e0f28; + --box-bg: #272d37; + --stat-box-bg: #1c2027; --alert-bg: #3a1c61; --navbar-bg: #212121; --transparent-fg: #ffffffbb; --fade-out-box-bg-start: rgba(23, 28, 42, 0); --fade-out-box-bg-end: rgba(23, 28, 42, 1); - --opacity: 0.7; + --opacity: 0.57; --testnet: #1d486f; --signet: #6f1d5d; @@ -103,4 +103,9 @@ $dropdown-link-active-bg: $active-bg; --grey: #7e7e7e; --tooltip-grey: #b1b1b1; --orange: #ff9f00; + + --search-button: #645449; + --search-button-border: #605046; + --search-button-focus: #786457; + --search-button-shadow: #a0867480; } diff --git a/frontend/src/theme-contrast.scss b/frontend/src/theme-contrast.scss index a3faddaef..61188a361 100644 --- a/frontend/src/theme-contrast.scss +++ b/frontend/src/theme-contrast.scss @@ -103,4 +103,9 @@ $dropdown-link-active-bg: $active-bg; --grey: #7e7e7e; --tooltip-grey: #b1b1b1; --orange: #ff9f00; + + --search-button: #4d2d77; + --search-button-border: #472a6e; + --search-button-focus: #533180; + --search-button-shadow: #7c58ab80; } diff --git a/frontend/src/theme-wiz.scss b/frontend/src/theme-wiz.scss index f46576c11..d560a6575 100644 --- a/frontend/src/theme-wiz.scss +++ b/frontend/src/theme-wiz.scss @@ -103,4 +103,9 @@ $dropdown-link-active-bg: $active-bg; --grey: #7e7e7e; --tooltip-grey: #b1b1b1; --orange: #ff9f00; + + --search-button: #4d2d77; + --search-button-border: #472a6e; + --search-button-focus: #533180; + --search-button-shadow: #7c58ab80; } From 4c697d000802decca827b41d8f1b905aaaf7a7ea Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 7 May 2024 22:42:06 +0000 Subject: [PATCH 07/36] Update onbtc theme & branding --- frontend/custom-sv-config.json | 8 +- .../liquid-master-page.component.scss | 5 +- .../master-page-preview.component.html | 2 +- .../master-page-preview.component.scss | 3 +- .../master-page/master-page.component.html | 54 +- .../master-page/master-page.component.scss | 5 +- frontend/src/app/services/state.service.ts | 4 +- .../global-footer.component.html | 36 +- .../global-footer/global-footer.component.ts | 15 +- frontend/src/index.sv.html | 2 +- frontend/src/resources/elsalvador.svg | 27 +- frontend/src/resources/onbtc.svg | 112 +-- frontend/src/resources/onbtclogo.svg | 678 ++++++++++++++++++ frontend/src/theme-bukele.scss | 64 +- frontend/src/theme-contrast.scss | 3 +- frontend/src/theme-wiz.scss | 3 +- 16 files changed, 829 insertions(+), 192 deletions(-) create mode 100644 frontend/src/resources/onbtclogo.svg diff --git a/frontend/custom-sv-config.json b/frontend/custom-sv-config.json index f64f41be8..7a62d1ec5 100644 --- a/frontend/custom-sv-config.json +++ b/frontend/custom-sv-config.json @@ -1,12 +1,12 @@ { - "theme": "contrast", + "theme": "bukele", "enterprise": "onbtc", "branding": { "name": "onbtc", - "title": "Oficina Nacional del Bitcoin", + "title": "Bitcoin Office", "site_id": 19, - "header_img": "/resources/onbtc.svg", - "img": "/resources/elsalvador.svg", + "header_img": "/resources/onbtclogo.svg", + "footer_img": "/resources/onbtclogo.svg", "rounded_corner": true }, "dashboard": { diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.scss b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.scss index b0f3ce9c6..17bc8aeb5 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.scss +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.scss @@ -4,6 +4,7 @@ top: 0; width: 100%; z-index: 100; + background-color: var(--bg); } li.nav-item.active { @@ -17,7 +18,7 @@ fa-icon { .navbar { z-index: 100; min-height: 64px; - background-color: var(--bg); + background-color: var(--nav-bg); } li.nav-item { @@ -48,7 +49,7 @@ li.nav-item { } .navbar-nav { - background: var(--navbar-bg); + background: var(--nav-bg); bottom: 0; box-shadow: 0px 0px 15px 0px #000; flex-direction: row; diff --git a/frontend/src/app/components/master-page-preview/master-page-preview.component.html b/frontend/src/app/components/master-page-preview/master-page-preview.component.html index 49efce400..8f3204ec4 100644 --- a/frontend/src/app/components/master-page-preview/master-page-preview.component.html +++ b/frontend/src/app/components/master-page-preview/master-page-preview.component.html @@ -6,7 +6,7 @@ } @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + enterpriseInfo.title } @else { diff --git a/frontend/src/app/components/master-page-preview/master-page-preview.component.scss b/frontend/src/app/components/master-page-preview/master-page-preview.component.scss index fb0fd5c24..bb2e5b706 100644 --- a/frontend/src/app/components/master-page-preview/master-page-preview.component.scss +++ b/frontend/src/app/components/master-page-preview/master-page-preview.component.scss @@ -5,6 +5,7 @@ max-width: 1200px; max-height: 600px; padding-top: 80px; + background: var(--nav-bg); header { position: absolute; @@ -18,7 +19,7 @@ flex-direction: row; justify-content: space-between; align-items: center; - background: var(--stat-box-bg); + background: var(--nav-bg); text-align: start; font-size: 1.8em; } diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 18fbeac68..169dd24a3 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -17,16 +17,16 @@ - -
- -
-
-
@if (enterpriseInfo?.header_img) { - enterpriseInfo.title + enterpriseInfo.title } @else { + +
+ +
+
+
} @@ -38,24 +38,28 @@
- -
- -
-
-
- - @if (enterpriseInfo?.header_img) { - enterpriseInfo.title - } @else { - - - } -
-
Offline
-
Reconnecting...
-
-
+ @if (enterpriseInfo?.header_img) { + enterpriseInfo.title + } @else { + +
+ +
+
+
+ + @if (enterpriseInfo?.header_img) { + enterpriseInfo.title + } @else { + + + } +
+
Offline
+
Reconnecting...
+
+
+ }
diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts index 2847b6586..e64151323 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { combineLatest, merge, Observable, of, Subject, Subscription } from 'rxjs'; import { catchError, filter, map, scan, share, shareReplay, startWith, switchMap, tap } from 'rxjs/operators'; import { BlockExtended, OptimizedMempoolStats, TransactionStripped } from '../../interfaces/node-api.interface'; @@ -85,6 +85,7 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni private electrsApiService: ElectrsApiService, private websocketService: WebsocketService, private seoService: SeoService, + private cd: ChangeDetectorRef, @Inject(PLATFORM_ID) private platformId: Object, ) { this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); @@ -283,8 +284,8 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni startAddressSubscription(): void { if (this.stateService.env.customize && this.stateService.env.customize.dashboard.widgets.some(w => w.props?.address)) { - const address = this.stateService.env.customize.dashboard.widgets.find(w => w.props?.address).props.address; - const addressString = (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(address)) ? address.toLowerCase() : address; + let addressString = this.stateService.env.customize.dashboard.widgets.find(w => w.props?.address).props.address; + addressString = (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(addressString)) ? addressString.toLowerCase() : addressString; this.addressSubscription = ( addressString.match(/04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}/) @@ -299,6 +300,7 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni ).subscribe((address: Address) => { this.websocketService.startTrackAddress(address.address); this.address = address; + this.cd.markForCheck(); }); this.addressSummary$ = ( From c15393981dcd450eeac5d2361b12c9617e6e128c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 8 May 2024 00:22:54 +0000 Subject: [PATCH 10/36] Update onbtc fallback preview img --- frontend/src/resources/sv/onbtc-preview.jpg | Bin 81761 -> 348463 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/src/resources/sv/onbtc-preview.jpg b/frontend/src/resources/sv/onbtc-preview.jpg index 1ae838e1251d2567cb9ac9a1de1c4ca65a0b5bc4..b243fd8e054702c03620e66e814c1ec81dcdda3f 100644 GIT binary patch literal 348463 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%0DY*^0qb>1VjvjAHfBeTN}EASOo|OoQx3UeR+7 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