diff --git a/apps/namadillo/package.json b/apps/namadillo/package.json
index a95434df4..56d667981 100644
--- a/apps/namadillo/package.json
+++ b/apps/namadillo/package.json
@@ -15,6 +15,7 @@
"@tanstack/query-core": "^5.40.0",
"@tanstack/react-query": "^5.40.0",
"@tanstack/react-query-persist-client": "^5.40.0",
+ "animejs": "^3.2.2",
"bignumber.js": "^9.1.1",
"clsx": "^2.1.1",
"comlink": "^4.4.1",
@@ -91,6 +92,7 @@
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
+ "@types/animejs": "^3.1.12",
"@types/invariant": "^2.2.37",
"@types/jest": "^29.5.12",
"@types/lodash.debounce": "^4.0.9",
diff --git a/apps/namadillo/src/App/Common/Timeline.tsx b/apps/namadillo/src/App/Common/Timeline.tsx
index e76e0b7d8..256956160 100644
--- a/apps/namadillo/src/App/Common/Timeline.tsx
+++ b/apps/namadillo/src/App/Common/Timeline.tsx
@@ -1,4 +1,8 @@
+import anime from "animejs";
import clsx from "clsx";
+import { useScope } from "hooks/useScope";
+import { useRef } from "react";
+import { FaCheckCircle } from "react-icons/fa";
import { twMerge } from "tailwind-merge";
export type TransactionStep = {
bullet?: boolean;
@@ -35,17 +39,17 @@ const StepBullet = ({ disabled }: DisabledProps): JSX.Element => (
const StepContent = ({
children,
- isNextStep,
+ isCurrentStep,
hasError,
disabled,
}: React.PropsWithChildren & {
- isNextStep: boolean;
+ isCurrentStep: boolean;
hasError: boolean;
disabled: boolean;
}): JSX.Element => (
@@ -59,8 +63,109 @@ export const Timeline = ({
hasError,
complete,
}: TransactionTimelineProps): JSX.Element => {
+ const containerRef = useRef
(null);
+
+ useScope(
+ (query, container) => {
+ if (!complete) return;
+
+ const timeline = anime.timeline({
+ easing: "easeOutExpo",
+ duration: 1000,
+ });
+
+ const containerRect = container.getBoundingClientRect();
+ const items = Array.from(query("i,div"));
+ const hiding = items.slice(0, -1);
+ const lastItem = items.slice(-1)[0];
+ const lastItemText = lastItem.querySelector("p");
+ const rings = query('[data-animation="ring"]');
+ const confirmation = query('[data-animation="confirmation"]');
+
+ const lastItemTextHeight =
+ lastItemText ? lastItemText.getBoundingClientRect().height : 0;
+
+ const marginTop = 4; // ?
+
+ const centerLastItemY =
+ -containerRect.height / 2 +
+ lastItem.getBoundingClientRect().height / 2 +
+ lastItemTextHeight / 2 +
+ marginTop;
+
+ // Hide items, except last one
+ timeline.add({
+ targets: hiding,
+ opacity: 0,
+ delay: anime.stagger(30),
+ });
+
+ // Move last item to the center of the screen
+ timeline.add(
+ {
+ targets: lastItem,
+ translateY: centerLastItemY,
+ },
+ "-=700"
+ );
+
+ // Try to hide any existing text contained on the last item, as soon
+ // as the item goes up
+ timeline.add(
+ {
+ targets: lastItem.querySelector("p"),
+ opacity: 0,
+ duration: 400,
+ },
+ "-=1000"
+ );
+
+ // Display concentric rings, one by one
+ timeline.add(
+ {
+ targets: rings,
+ opacity: [0, 1],
+ duration: 800,
+ scale: [0, 1],
+ delay: anime.stagger(100),
+ },
+ "-=600"
+ );
+
+ // Sucks everything into the screen
+ timeline.add({
+ targets: [...rings, lastItem],
+ scale: 0,
+ duration: 300,
+ });
+
+ // Displays success box confirmation
+ timeline.add({
+ targets: confirmation,
+ duration: 1000,
+ scale: [0, 1],
+ opacity: [0, 1],
+ });
+ },
+ containerRef,
+ [complete]
+ );
+
+ const renderRing = (className: string): JSX.Element => (
+
+ );
+
return (
-
+
{steps.map((step, index: number) => {
return (
@@ -80,11 +185,9 @@ export const Timeline = ({
currentStepIndex} />
)}
currentStepIndex}
- hasError={Boolean(hasError)}
+ hasError={!!hasError}
>
{step.children}
@@ -92,6 +195,26 @@ export const Timeline = ({
);
})}
+
+ {renderRing("h-30")}
+ {renderRing("h-60")}
+ {renderRing("h-96")}
+
+
+
+
);
};
diff --git a/apps/namadillo/src/App/Sidebars/YourStakingDistribution.tsx b/apps/namadillo/src/App/Sidebars/YourStakingDistribution.tsx
index 6faedf790..a075b1d95 100644
--- a/apps/namadillo/src/App/Sidebars/YourStakingDistribution.tsx
+++ b/apps/namadillo/src/App/Sidebars/YourStakingDistribution.tsx
@@ -1,8 +1,10 @@
import { PieChart, PieChartData } from "@namada/components";
import { shortenAddress } from "@namada/utils";
import BigNumber from "bignumber.js";
-import { AnimatePresence, motion } from "framer-motion";
+import clsx from "clsx";
+import { AnimatePresence } from "framer-motion";
import { useState } from "react";
+import { twMerge } from "tailwind-merge";
import { MyValidator } from "types";
type YourStakingDistributionProps = {
@@ -62,29 +64,34 @@ export const YourStakingDistribution = ({
setDisplayedValidator(myValidators[index]);
}}
>
-
+
- {displayedValidator === undefined && (
-
- Your Stake Distribution
-
- )}
- {displayedValidator && (
-
- {displayedValidator.validator.alias}
-
- {getFormattedPercentage(displayedValidator)}
-
-
- )}
+
+ Your Stake Distribution
+
+
+ {displayedValidator?.validator.alias}
+
+ {displayedValidator &&
+ getFormattedPercentage(displayedValidator)}
+
+
diff --git a/apps/namadillo/src/App/Transactions/TransferTimelineErrorEntry.tsx b/apps/namadillo/src/App/Transactions/TransferTimelineErrorEntry.tsx
index cdb564392..f20f871bc 100644
--- a/apps/namadillo/src/App/Transactions/TransferTimelineErrorEntry.tsx
+++ b/apps/namadillo/src/App/Transactions/TransferTimelineErrorEntry.tsx
@@ -11,10 +11,10 @@ export const TransferTimelineErrorEntry = ({
}: TransferTimelineErrorEntryProps): JSX.Element => {
return (
<>
-
+
- {children}
+ {children}
{
const textSteps = [...allTransferStages[transaction.type]];
-
const hasError = transaction.status === "error";
const isTransparentTransfer = transparentTransferTypes.includes(
transaction.type
@@ -137,9 +136,13 @@ export const TransferTransactionTimeline = ({
initialImage.length
);
+ const filteredSteps = textSteps.filter(
+ (step) => step !== TransferStep.WaitingConfirmation
+ );
+
const stepsWithDescription = buildStepEntries(
- textSteps.filter((step) => step !== TransferStep.WaitingConfirmation),
- currentStepIndex,
+ filteredSteps,
+ currentStepIndex + filteredSteps.length - textSteps.length,
hasError,
transaction,
!isTransparentTransfer
diff --git a/apps/namadillo/src/hooks/useScope.tsx b/apps/namadillo/src/hooks/useScope.tsx
new file mode 100644
index 000000000..a189e1283
--- /dev/null
+++ b/apps/namadillo/src/hooks/useScope.tsx
@@ -0,0 +1,22 @@
+import { RefObject, useEffect } from "react";
+
+type QueryFnOutput = ReturnType;
+type CallbackFn = (query: string) => QueryFnOutput;
+type CallbackProps = (callback: CallbackFn, container: HTMLElement) => void;
+
+export const useScope = (
+ callback: CallbackProps,
+ scope: RefObject,
+ dependencies: unknown[] = []
+): void => {
+ useEffect(() => {
+ const queryFn = (query: string): QueryFnOutput => {
+ if (!scope.current)
+ throw "You must pass a valid scope for useAnimation hook";
+ return scope.current.querySelectorAll(query);
+ };
+
+ if (!scope.current) return;
+ callback(queryFn, scope.current);
+ }, [...dependencies]);
+};
diff --git a/yarn.lock b/yarn.lock
index cbe90c8c7..ecf5bc0ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3671,6 +3671,7 @@ __metadata:
"@testing-library/jest-dom": "npm:^6.5.0"
"@testing-library/react": "npm:^16.0.0"
"@testing-library/user-event": "npm:^14.5.2"
+ "@types/animejs": "npm:^3.1.12"
"@types/invariant": "npm:^2.2.37"
"@types/jest": "npm:^29.5.12"
"@types/lodash.debounce": "npm:^4.0.9"
@@ -3682,6 +3683,7 @@ __metadata:
"@types/styled-components": "npm:^5.1.22"
"@types/traverse": "npm:^0.6.36"
"@vitejs/plugin-react": "npm:^4.3.1"
+ animejs: "npm:^3.2.2"
autoprefixer: "npm:^10.4.16"
axios: "npm:^1.7.9"
bignumber.js: "npm:^9.1.1"
@@ -4875,6 +4877,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/animejs@npm:^3.1.12":
+ version: 3.1.12
+ resolution: "@types/animejs@npm:3.1.12"
+ checksum: 21d10ed4fbaa99572e252b4d6e666b62a19089f63b13b0e9f8edf5fb0b708afc2b38c3055c52f6a4771bc71793d7a7aec83754c16d0e5606a0ad92b34778ba61
+ languageName: node
+ linkType: hard
+
"@types/aria-query@npm:^5.0.1":
version: 5.0.4
resolution: "@types/aria-query@npm:5.0.4"
@@ -6360,6 +6369,13 @@ __metadata:
languageName: node
linkType: hard
+"animejs@npm:^3.2.2":
+ version: 3.2.2
+ resolution: "animejs@npm:3.2.2"
+ checksum: f43dfcc0c743a2774e76fbfcb16a22350da7104f413d9d1b85c48128b0c078090642809deb631e21dfa0a40651111be39d9d7f694c9c1b70d8637ce8b6d63116
+ languageName: node
+ linkType: hard
+
"ansi-align@npm:^3.0.1":
version: 3.0.1
resolution: "ansi-align@npm:3.0.1"