Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
2 changes: 1 addition & 1 deletion apps/quick-dapp-v2/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"main": "apps/quick-dapp-v2/src/main.tsx",
"polyfills": "apps/quick-dapp-v2/src/polyfills.ts",
"tsConfig": "apps/quick-dapp-v2/tsconfig.app.json",
"assets": ["apps/quick-dapp-v2/src/profile.json", "apps/quick-dapp-v2/src/assets/sparkling.png"],
"assets": ["apps/quick-dapp-v2/src/profile.json", "apps/quick-dapp-v2/src/assets/sparkling.png", "apps/quick-dapp-v2/src/assets/start-now-guide.png"],
"styles": ["apps/quick-dapp-v2/src/index.css"],
"scripts": [],
"webpackConfig": "apps/quick-dapp-v2/webpack.config.js"
Expand Down
82 changes: 75 additions & 7 deletions apps/quick-dapp-v2/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,70 @@ function App(): JSX.Element {

try {
await connectRemix();

// @ts-ignore
remixClient.call('locale', 'currentLocale').then((l: any) => setLocale(l));
// @ts-ignore
remixClient.on('locale', 'localeChanged', (l: any) => setLocale(l));

// @ts-ignore
remixClient.on('filePanel', 'workspaceDeleted', (workspaceName: string) => {
dispatch({
type: 'SET_DAPPS',
payload: dappsRef.current.filter((d: any) => d.workspaceName !== workspaceName)
});
});

// Wait for filePanel to be ready (important for sidepanel)
for (let i = 0; i < 10; i++) {
try {
// @ts-ignore
const currentWs = await remixClient.call('filePanel', 'getCurrentWorkspace');
if (currentWs && currentWs.name) {
break;
}
} catch (e) {}
await new Promise(resolve => setTimeout(resolve, 500));
}


const dapps = (await dappManager.getDapps()) || [];
dispatch({ type: 'SET_DAPPS', payload: dapps });

const validDapps = dapps.filter((d: any) => d.config?.status !== 'draft');
const FIVE_MINUTES = 5 * 60 * 1000;
const now = Date.now();

for (const dapp of dapps) {
const status = dapp.status;
const processingStartedAt = dapp.processingStartedAt || 0;
const elapsed = now - processingStartedAt;

if (status === 'creating' || status === 'updating') {
if (elapsed < FIVE_MINUTES) {
dispatch({
type: 'SET_DAPP_PROCESSING',
payload: { slug: dapp.slug, isProcessing: true }
});
} else {
console.warn(`[QuickDapp] Timeout: ${dapp.slug} stuck in '${status}' for ${Math.round(elapsed / 1000)}s. Resetting status.`);
await dappManager.updateDappConfig(dapp.slug, {
status: 'created',
processingStartedAt: null
});
}
}
}

const refreshedDapps = (await dappManager.getDapps()) || [];
dispatch({ type: 'SET_DAPPS', payload: refreshedDapps });

if (validDapps.length > 0) {
if (refreshedDapps.length > 0) {
dispatch({ type: 'SET_VIEW', payload: 'dashboard' });
} else {
dispatch({ type: 'SET_VIEW', payload: 'create' });
}

} catch (e) {
console.error("[DEBUG-APP] Failed to load app", e);
dispatch({ type: 'SET_DAPPS', payload: [] });
dispatch({ type: 'SET_VIEW', payload: 'create' });
} finally {
Expand Down Expand Up @@ -92,8 +138,13 @@ function App(): JSX.Element {
}
};

const onDappUpdateStart = (data: any) => {
const onDappUpdateStart = async (data: any) => {
if (data && data.slug) {
await dappManager.updateDappConfig(data.slug, {
status: 'updating',
processingStartedAt: Date.now()
});

dispatch({
type: 'SET_DAPP_PROCESSING',
payload: { slug: data.slug, isProcessing: true }
Expand Down Expand Up @@ -121,13 +172,18 @@ function App(): JSX.Element {
dispatch({ type: 'SET_AI_LOADING', payload: false });
};

const onCreatingError = (errorData?: any) => {
const onCreatingError = async (errorData?: any) => {
console.error('[DEBUG-APP] Event: creatingDappError', errorData);
dispatch({ type: 'SET_AI_LOADING', payload: false });

const targetSlug = errorData?.slug || activeDappRef.current?.slug;

if (targetSlug) {
await dappManager.updateDappConfig(targetSlug, {
status: 'created',
processingStartedAt: null
});

dispatch({
type: 'SET_DAPP_PROCESSING',
payload: { slug: targetSlug, isProcessing: false }
Expand All @@ -137,10 +193,15 @@ function App(): JSX.Element {
}
};

const onDappUpdated = (data: any) => {
const onDappUpdated = async (data: any) => {
dispatch({ type: 'SET_AI_LOADING', payload: false });

if (data.slug) {
await dappManager.updateDappConfig(data.slug, {
status: 'created',
processingStartedAt: null
});

dispatch({
type: 'SET_DAPP_PROCESSING',
payload: { slug: data.slug, isProcessing: false }
Expand All @@ -163,7 +224,14 @@ function App(): JSX.Element {
}, [dappManager, dispatch]);

const handleDeleteOne = async (slug: string) => {
await dappManager.deleteDapp(slug);
const dappToDelete = dappsRef.current.find((d: any) => d.slug === slug);

if (!dappToDelete) {
console.error('[App] Could not find dapp with slug:', slug);
return;
}

await dappManager.deleteDapp(dappToDelete.workspaceName);
const updatedDapps = (await dappManager.getDapps()) || [];
dispatch({ type: 'SET_DAPPS', payload: updatedDapps });

Expand Down
19 changes: 19 additions & 0 deletions apps/quick-dapp-v2/src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,23 @@ export const setAiLoading = async (isLoading: boolean) => {
type: 'SET_AI_LOADING',
payload: isLoading,
});
};

export const openDapp = async (slug: string) => {
if (!dispatch || !state) {
console.error('[QuickDapp] dispatch or state not initialized');
return false;
}

const dapps = state.dapps || [];
const targetDapp = dapps.find((d: any) => d.slug === slug || d.workspaceName === slug);

if (targetDapp) {
await dispatch({ type: 'SET_ACTIVE_DAPP', payload: targetDapp });
await dispatch({ type: 'SET_VIEW', payload: 'editor' });
return true;
} else {
await dispatch({ type: 'SET_VIEW', payload: 'dashboard' });
return false;
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 72 additions & 31 deletions apps/quick-dapp-v2/src/components/CreateInstance/index.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,86 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
import { Card } from 'react-bootstrap';

interface CreateInstanceProps {
isAiLoading: boolean;
}

const CreateInstance: React.FC<CreateInstanceProps> = ({ isAiLoading }) => {

if (isAiLoading) {
return (
<div className="d-flex flex-column align-items-center justify-content-center py-5">
<div className="spinner-border text-primary mb-3" role="status" style={{ width: '3rem', height: '3rem' }}></div>
<h5 className="text-primary">Creating Your DApp...</h5>
<p className="text-muted">RemixAI Assistant is generating your DApp code.</p>
</div>
);
}

return (
<div className="text-center">
{isAiLoading ? (
<div className="mt-4 mb-3 p-4">
<i className="fas fa-spinner fa-spin me-2"></i>
Your dapp is being created by RemixAI Assistant.
</div>
) : (
<>
<Alert
className="mt-4 d-flex align-items-center justify-content-center"
variant="info"
data-id="quickDappTooltips"
>
<div className="flex-shrink-0 me-3">
<img
src='./assets/sparkling.png'
style={{ width: '300px' }}
alt="Sparkling star icon"
/>
<div className="py-4">
<div className="text-center mb-4">
<h3 className="mb-2">Welcome to Quick DApp</h3>
<p className="text-muted mb-0">
Transform your smart contracts into interactive DApps with AI.
</p>
</div>

<Card className="border-info">
<Card.Header className="bg-info bg-opacity-10 border-info">
<h5 className="mb-0 text-info">
<i className="fas fa-rocket me-2"></i>
Getting Started
</h5>
</Card.Header>
<Card.Body>
<p className="mb-4">After deploying your contract, create a DApp using one of these options:</p>

<div className="row g-4">
<div className="col-12 col-md-6">
<div className="border rounded p-3 h-100">
<h6 className="text-primary mb-3">
<i className="fas fa-flag me-2"></i>
Option 1: Start Now Banner
</h6>
<p className="small text-muted mb-3">
Click the <span className="badge bg-primary">Start now</span> button in the banner above the editor.
</p>
<img
src='./assets/start-now-guide.png'
alt="Start now guide"
className="img-fluid rounded shadow-sm w-100"
style={{
border: '1px solid var(--secondary)',
objectFit: 'contain'
}}
/>
</div>
</div>
<div className="text-start">
<FormattedMessage id="quickDapp.text1" />
<br />
<FormattedMessage id="quickDapp.text2" />

<div className="col-12 col-md-6">
<div className="border rounded p-3 h-100">
<h6 className="text-primary mb-3">
<i className="fas fa-magic me-2"></i>
Option 2: Sparkle Button
</h6>
<p className="small text-muted mb-3">
Click the Sparkle button on your deployed contract instance.
</p>
<img
src='./assets/sparkling.png'
alt="Sparkle button guide"
className="img-fluid rounded shadow-sm w-100"
style={{
border: '1px solid var(--secondary)',
objectFit: 'contain'
}}
/>
</div>
</div>
</Alert>
{/* <div className="mt-4 mb-3">
<FormattedMessage id="quickDapp.text7" />
</div> */}
</>
)}
</div>
</Card.Body>
</Card>
</div>
);
};
Expand Down
4 changes: 2 additions & 2 deletions apps/quick-dapp-v2/src/components/DappCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const DappCard: React.FC<DappCardProps> = ({ dapp, isProcessing, onClick, onDele
const statusColor = dapp.status === 'deployed' ? 'text-success' : 'text-warning';
const statusIcon = dapp.status === 'deployed' ? 'fa-check-circle' : 'fa-pen-square';

const loadingText = dapp.status === 'draft' ? 'AI Creating...' : 'AI Updating...';
const loadingText = dapp.status === 'creating' ? 'AI Creating...' : 'AI Updating...';

return (
<div className="col-12 col-md-6 col-xl-4 mb-4">
Expand Down Expand Up @@ -78,7 +78,7 @@ const DappCard: React.FC<DappCardProps> = ({ dapp, isProcessing, onClick, onDele
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
title="Delete Dapp"
title="Delete DApp and workspace"
>
<div
className={`rounded-circle d-flex align-items-center justify-content-center shadow-sm ${isHovered ? 'bg-danger' : 'bg-dark bg-opacity-75'
Expand Down
Loading