diff --git a/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.html b/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.html
new file mode 100644
index 000000000..bf0080344
--- /dev/null
+++ b/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.scss b/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.scss
new file mode 100644
index 000000000..35f6e32d5
--- /dev/null
+++ b/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.scss
@@ -0,0 +1,45 @@
+.sparkles {
+ position: absolute;
+ top: var(--block-size);
+ height: 50px;
+ right: 0;
+}
+
+.sparkle {
+ position: absolute;
+ color: rgba(152, 88, 255, 0.75);
+ opacity: 0;
+ transform: scale(0.8) rotate(0deg);
+ animation: pop ease 2000ms forwards, sparkle ease 500ms infinite;
+}
+
+.inner-sparkle {
+ display: block;
+}
+
+@keyframes pop {
+ 0% {
+ transform: scale(0.8) rotate(0deg);
+ opacity: 0;
+ }
+ 20% {
+ transform: scale(1) rotate(72deg);
+ opacity: 1;
+ }
+ 100% {
+ transform: scale(0) rotate(360deg);
+ opacity: 0;
+ }
+}
+
+@keyframes sparkle {
+ 0% {
+ color: rgba(152, 88, 255, 0.75);
+ }
+ 50% {
+ color: rgba(198, 162, 255, 0.75);
+ }
+ 100% {
+ color: rgba(152, 88, 255, 0.75);
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.ts b/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.ts
new file mode 100644
index 000000000..2316c996d
--- /dev/null
+++ b/frontend/src/app/components/acceleration/sparkles/acceleration-sparkles.component.ts
@@ -0,0 +1,73 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
+
+@Component({
+ selector: 'app-acceleration-sparkles',
+ templateUrl: './acceleration-sparkles.component.html',
+ styleUrls: ['./acceleration-sparkles.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AccelerationSparklesComponent implements OnChanges {
+ @Input() arrow: ElementRef;
+ @Input() run: boolean = false;
+
+ @ViewChild('sparkleAnchor')
+ sparkleAnchor: ElementRef;
+
+ constructor(
+ private cd: ChangeDetectorRef,
+ ) {}
+
+ endTimeout: any;
+ lastSparkle: number = 0;
+ sparkleWidth: number = 0;
+ sparkles: any[] = [];
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.run) {
+ if (this.endTimeout) {
+ clearTimeout(this.endTimeout);
+ this.endTimeout = null;
+ }
+ if (this.run) {
+ this.doSparkle();
+ } else {
+ this.endTimeout = setTimeout(() => {
+ this.sparkles = [];
+ }, 2000);
+ }
+ }
+ }
+
+ doSparkle(): void {
+ if (this.run) {
+ const now = performance.now();
+ if (now - this.lastSparkle > 20) {
+ this.lastSparkle = now;
+ if (this.arrow?.nativeElement && this.sparkleAnchor?.nativeElement) {
+ const anchor = this.sparkleAnchor.nativeElement.getBoundingClientRect().right;
+ const right = this.arrow.nativeElement.getBoundingClientRect().right;
+ const dx = (anchor - right) + 30;
+ const numSparkles = Math.ceil(Math.random() * 3);
+ for (let i = 0; i < numSparkles; i++) {
+ this.sparkles.push({
+ style: {
+ right: (dx + (Math.random() * 10)) + 'px',
+ top: (15 + (Math.random() * 30)) + 'px',
+ },
+ rotation: {
+ transform: `rotate(${Math.random() * 360}deg)`,
+ }
+ });
+ }
+ while (this.sparkles.length > 200) {
+ this.sparkles.shift();
+ }
+ this.cd.markForCheck();
+ }
+ }
+ requestAnimationFrame(() => {
+ this.doSparkle();
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html
index 24f229598..b979e032b 100644
--- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html
+++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html
@@ -51,7 +51,8 @@
-
+
+
diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
index 13608bb73..a0958ec40 100644
--- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
+++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core';
+import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
import { Subscription, Observable, of, combineLatest } from 'rxjs';
import { MempoolBlock } from '../../interfaces/websocket.interface';
import { StateService } from '../../services/state.service';
@@ -77,6 +77,9 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
maxArrowPosition = 0;
rightPosition = 0;
transition = 'background 2s, right 2s, transform 1s';
+ @ViewChild('arrowUp')
+ arrowElement: ElementRef;
+ acceleratingArrow: boolean = false;
markIndex: number;
txPosition: MempoolPosition;
@@ -201,6 +204,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
this.markBlocksSubscription = this.stateService.markBlock$
.subscribe((state) => {
+ const oldTxPosition = this.txPosition;
this.markIndex = undefined;
this.txPosition = undefined;
this.txFeePerVSize = undefined;
@@ -209,6 +213,12 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
}
if (state.mempoolPosition) {
this.txPosition = state.mempoolPosition;
+ if (this.txPosition.accelerated && !oldTxPosition.accelerated) {
+ this.acceleratingArrow = true;
+ setTimeout(() => {
+ this.acceleratingArrow = false;
+ }, 2000);
+ }
}
if (state.txFeePerVSize) {
this.txFeePerVSize = state.txFeePerVSize;
diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts
index 89bcfafbb..2d5b4d0f9 100644
--- a/frontend/src/app/shared/shared.module.ts
+++ b/frontend/src/app/shared/shared.module.ts
@@ -100,6 +100,7 @@ import { MempoolErrorComponent } from './components/mempool-error/mempool-error.
import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component';
import { PendingStatsComponent } from '../components/acceleration/pending-stats/pending-stats.component';
import { AccelerationStatsComponent } from '../components/acceleration/acceleration-stats/acceleration-stats.component';
+import { AccelerationSparklesComponent } from '../components/acceleration/sparkles/acceleration-sparkles.component';
import { BlockViewComponent } from '../components/block-view/block-view.component';
import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component';
@@ -225,6 +226,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
AccelerationsListComponent,
AccelerationStatsComponent,
PendingStatsComponent,
+ AccelerationSparklesComponent,
HttpErrorComponent,
TwitterWidgetComponent,
FaucetComponent,
@@ -355,6 +357,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
AccelerationsListComponent,
AccelerationStatsComponent,
PendingStatsComponent,
+ AccelerationSparklesComponent,
HttpErrorComponent,
TwitterWidgetComponent,
TwitterLogin,