Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions apps/api/src/dynamic-bridge-discovery/bridge.loader.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import * as fs from 'fs';
import * as path from 'path';
<<<<<<< HEAD:src/dynamic-bridge-discovery/bridge.loader.ts
import {
BridgeAdapter,
BridgeAdapterConstructor,
} from '../interfaces/bridge-adapter.interface';
import { BridgeModuleConfig } from '../interfaces/bridge-config.interface';
import {
BridgeInitializationException,
BridgeLoadException,
} from '../exceptions/bridge.exceptions';
import { BridgeRegistry } from '../registry/bridge.registry';
import { BRIDGE_ADAPTER_METADATA } from '../decorators/bridge.decorators';
=======
import { BridgeRegistry } from './bridge.registry';
import { BridgeModuleConfig } from './bridge-config.interface';
import {
Expand All @@ -25,7 +12,6 @@ import {
BridgeLoadException,
} from './bridge.exceptions';
import { BRIDGE_ADAPTER_METADATA } from './bridge.decorators';
>>>>>>> 902330b94c4294029cf45eb84c6121443fbb0427:apps/api/src/dynamic-bridge-discovery/bridge.loader.ts

@Injectable()
export class BridgeLoader implements OnModuleInit {
Expand Down Expand Up @@ -141,25 +127,12 @@ export class BridgeLoader implements OnModuleInit {
}

<<<<<<< HEAD:src/dynamic-bridge-discovery/bridge.loader.ts
const mergedConfig = {
...(this.config.globalConfig ?? {}),
...(adapterConfig.options ?? {}),
};
const instance: BridgeAdapter = new AdapterClass(mergedConfig);

await this.initializeAdapter(instance);
this.registry.register(instance, {
source: resolvedPath,
configKey: name,
});
=======
await this.createAndRegisterAdapter(
AdapterClass,
resolvedPath,
adapterConfig.options ?? {},
name,
);
>>>>>>> 902330b94c4294029cf45eb84c6121443fbb0427:apps/api/src/dynamic-bridge-discovery/bridge.loader.ts
} catch (err) {
throw new BridgeLoadException(resolvedPath, err as Error);
}
Expand Down
7 changes: 0 additions & 7 deletions apps/api/src/dynamic-bridge-discovery/bridge.registry.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { Injectable, Logger } from '@nestjs/common';
<<<<<<< HEAD:src/dynamic-bridge-discovery/bridge.registry.ts
import {
BridgeAdapter,
BridgeCapability,
} from '../interfaces/bridge-adapter.interface';
=======
import { BridgeAdapter, BridgeCapability } from './bridge-adapter.interface';
>>>>>>> 902330b94c4294029cf45eb84c6121443fbb0427:apps/api/src/dynamic-bridge-discovery/bridge.registry.ts
import {
BridgeCapabilityNotFoundException,
BridgeDuplicateException,
Expand Down
37 changes: 37 additions & 0 deletions apps/api/src/dynamic-bridge-discovery/demo-bridge.adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { BridgeAdapter, BridgeCapability } from './bridge-adapter.interface';
import { BridgePlugin } from './bridge.decorators';

@BridgePlugin({ name: 'demo-bridge', version: '0.1.0' })
export class DemoBridgeAdapter implements BridgeAdapter {
readonly name = 'demo-bridge';
readonly version = '0.1.0';
readonly capabilities: BridgeCapability[] = [
{ name: 'demo', version: '1.0.0', description: 'Demo bridge for plugin architecture' },
];

private initialized = false;

async initialize(config?: Record<string, unknown>): Promise<void> {
this.initialized = true;
}

async isHealthy(): Promise<boolean> {
return this.initialized;
}

async shutdown(): Promise<void> {
this.initialized = false;
}

async execute<T = unknown, R = unknown>(operation: string, payload: T): Promise<R> {
if (!this.initialized) {
throw new Error('DemoBridgeAdapter not initialized');
}

return {
operation,
payload,
handledBy: this.name,
} as unknown as R;
}
}
8 changes: 0 additions & 8 deletions apps/api/src/dynamic-bridge-discovery/http-bridge.adapter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
<<<<<<< HEAD:src/dynamic-bridge-discovery/http-bridge.adapter.ts
import {
BridgeAdapter,
BridgeCapability,
} from '../interfaces/bridge-adapter.interface';
import { BridgePlugin } from '../decorators/bridge.decorators';
=======
import { BridgeAdapter, BridgeCapability } from './bridge-adapter.interface';
import { BridgePlugin } from './bridge.decorators';
>>>>>>> 902330b94c4294029cf45eb84c6121443fbb0427:apps/api/src/dynamic-bridge-discovery/http-bridge.adapter.ts

@BridgePlugin({ name: 'http-bridge', version: '1.0.0' })
export class HttpBridgeAdapter implements BridgeAdapter {
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/dynamic-bridge-discovery/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export {
// Example adapters (not for production use — illustrative only)
export { HttpBridgeAdapter } from './http-bridge.adapter';
export { WebSocketBridgeAdapter } from './websocket-bridge.adapter';
export { DemoBridgeAdapter } from './demo-bridge.adapter';
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
<<<<<<< HEAD:src/dynamic-bridge-discovery/websocket-bridge.adapter.ts
import {
BridgeAdapter,
BridgeCapability,
} from '../interfaces/bridge-adapter.interface';
import { BridgePlugin } from '../decorators/bridge.decorators';
=======
import { BridgeAdapter, BridgeCapability } from './bridge-adapter.interface';
import { BridgePlugin } from './bridge.decorators';
>>>>>>> 902330b94c4294029cf45eb84c6121443fbb0427:apps/api/src/dynamic-bridge-discovery/websocket-bridge.adapter.ts

@BridgePlugin({ name: 'ws-bridge', version: '1.0.0' })
export class WebSocketBridgeAdapter implements BridgeAdapter {
Expand Down
15 changes: 9 additions & 6 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TransactionProvider,
} from '@bridgewise/ui-components';
import { OfflineBanner } from '../components/OfflineBanner';
import { I18nProvider } from '../components/I18nProvider';
import './globals.css';

const customTheme = {
Expand Down Expand Up @@ -47,12 +48,14 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<BridgeWiseProvider theme={customTheme} defaultMode="system">
<TransactionProvider>
<OfflineBanner />
{children}
</TransactionProvider>
</BridgeWiseProvider>
<I18nProvider>
<BridgeWiseProvider theme={customTheme} defaultMode="system">
<TransactionProvider>
<OfflineBanner />
{children}
</TransactionProvider>
</BridgeWiseProvider>
</I18nProvider>
</body>
</html>
);
Expand Down
104 changes: 86 additions & 18 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@

'use client';

import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
BridgeWiseProvider,
TransactionHeartbeat,
useTransaction,
BridgeStatus,
} from '@bridgewise/ui-components';
import VersionDisplay from '../components/VersionDisplay';
import { LanguageSwitcher } from '../components/LanguageSwitcher';

const customTheme = {
primaryColor: '#22c55e',
Expand All @@ -21,7 +23,9 @@ const customTheme = {
};

function TransactionDemo() {
const { state, updateState, startTransaction, clearState } = useTransaction();
const { t } = useTranslation();
const { state, updateState, startTransaction, clearState, executeBatch } = useTransaction();
const [batchResult, setBatchResult] = useState<{ id: string; txHash: string; success: boolean; error?: string; }[] | null>(null);

// Simulate progress
useEffect(() => {
Expand All @@ -37,52 +41,117 @@ function TransactionDemo() {
let nextProgress = state.progress + 5;
let nextStep = state.step;

if (nextProgress > 20 && nextProgress < 40) nextStep = 'Confirming on source chain...';
if (nextProgress > 50 && nextProgress < 70) nextStep = 'Bridging assets...';
if (nextProgress > 80) nextStep = 'Finalizing on destination...';
if (nextProgress > 20 && nextProgress < 40) nextStep = t('app.statusConfirming');
if (nextProgress > 50 && nextProgress < 70) nextStep = t('app.statusBridging');
if (nextProgress > 80) nextStep = t('app.statusFinalizing');

updateState({ progress: Math.min(nextProgress, 100), step: nextStep });
}, 800);

return () => clearInterval(interval);
}, [state, updateState]);
}, [state, updateState, t]);

return (
<BridgeWiseProvider theme={customTheme} defaultMode="dark">
<div className="flex min-h-screen flex-col items-center justify-center gap-12 p-10 bg-zinc-50 dark:bg-black">
<main className="flex flex-col items-center gap-8 max-w-3xl">
<LanguageSwitcher />
<h1 className="text-4xl font-bold text-center text-zinc-900 dark:text-zinc-100">
BridgeWise Theming Demo
{t('app.title')}
</h1>

<p className="max-w-xl text-center text-zinc-600 dark:text-zinc-400">
This page demonstrates the BridgeWise theme system. The heartbeat and status components
are styled via CSS variables injected by <code>BridgeWiseProvider</code>, with a custom
primary color and dark background.
{t('app.description')}
</p>

<div className="flex flex-wrap items-center justify-center gap-4">
<button
onClick={() => startTransaction('tx-' + Date.now())}
className="px-6 py-3 rounded-lg text-sm font-medium text-white bg-emerald-500 hover:bg-emerald-600 active:scale-95 transition"
>
Start Transaction
{t('app.startTransaction')}
</button>
<button
onClick={clearState}
className="px-6 py-3 rounded-lg text-sm font-medium border border-zinc-300 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 active:scale-95 transition"
>
Clear State
{t('app.clearState')}
</button>
<button
onClick={async () => {
const result = await executeBatch([
{
id: `batch-${Date.now()}-1`,
bridgeName: 'demo',
sourceChain: 'ethereum',
destinationChain: 'polygon',
sourceToken: 'ETH',
destinationToken: 'USDC',
amount: 1.23,
fee: 0.02,
slippagePercent: 0.5,
account: '0x000',
txHash: `0x${Math.random().toString(16).slice(2)}1`,
},
{
id: `batch-${Date.now()}-2`,
bridgeName: 'demo',
sourceChain: 'polygon',
destinationChain: 'ethereum',
sourceToken: 'USDC',
destinationToken: 'ETH',
amount: 10.5,
fee: 0.1,
slippagePercent: 0.5,
account: '0x000',
txHash: `0x${Math.random().toString(16).slice(2)}2`,
},
{
id: `batch-${Date.now()}-3`,
bridgeName: 'demo',
sourceChain: 'avax',
destinationChain: 'optimism',
sourceToken: 'USDC',
destinationToken: 'DAI',
amount: 50,
fee: 0.25,
slippagePercent: 0.5,
account: '0x000',
txHash: `0x${Math.random().toString(16).slice(2)}3`,
},
]);
setBatchResult(result.results);
}}
className="px-6 py-3 rounded-lg text-sm font-medium border border-blue-300 text-blue-700 dark:border-blue-500 dark:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-900 active:scale-95 transition"
>
Run Batch (3)
</button>
</div>

{batchResult && (
<div className="w-full rounded-lg border border-slate-300 bg-slate-50 p-4 text-sm text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100">
<h3 className="font-semibold mb-2">Batch result</h3>
<ul className="space-y-1">
{batchResult.map((item) => (
<li key={item.id} className="flex justify-between">
<span>{item.id}</span>
<span>
{item.success ? '✅ success' : '❌ failed'}
{item.error ? `: ${item.error}` : ''}
</span>
</li>
))}
</ul>
</div>
)}

<section className="grid w-full gap-6 md:grid-cols-2">
<div className="rounded-2xl border border-zinc-200/60 dark:border-zinc-800/80 bg-white/60 dark:bg-zinc-900/60 p-4 shadow-sm">
<h2 className="text-lg font-semibold mb-2 text-zinc-900 dark:text-zinc-50">
Inline BridgeStatus
{t('app.inlineStatusTitle')}
</h2>
<p className="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
An inline status card using the same theme variables as the floating heartbeat.
{t('app.inlineStatusText')}
</p>
<BridgeStatus
txHash={state.txHash || '0x0000000000000000000000000000000000000000'}
Expand All @@ -99,14 +168,13 @@ function TransactionDemo() {

<div className="rounded-2xl border border-zinc-200/60 dark:border-zinc-800/80 bg-white/60 dark:bg-zinc-900/60 p-4 shadow-sm">
<h2 className="text-lg font-semibold mb-2 text-zinc-900 dark:text-zinc-50">
Component-level Overrides
{t('app.componentOverridesTitle')}
</h2>
<p className="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
The floating heartbeat below uses a custom <code>className</code> to adjust its
position while still inheriting all theme variables.
{t('app.componentOverridesText')}
</p>
<p className="text-xs text-zinc-500 dark:text-zinc-500">
Trigger a transaction and you&apos;ll see the heartbeat appear in the bottom-left corner.
{t('app.componentOverridesHint')}
</p>
</div>
</section>
Expand Down
16 changes: 16 additions & 0 deletions apps/web/components/I18nProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { ReactNode, useEffect } from 'react';
import { I18nextProvider } from 'react-i18next';
import i18n from '../i18n';

export function I18nProvider({ children }: { children: ReactNode }) {
useEffect(() => {
const storedLanguage = window.localStorage.getItem('bridgewise-language');
if (storedLanguage && storedLanguage !== i18n.language) {
i18n.changeLanguage(storedLanguage);
}
}, []);

return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
}
Loading
Loading