Skip to content
This repository was archived by the owner on Aug 16, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2 from PBillingsby/cli-updates
Browse files Browse the repository at this point in the history
Cli updates - 1
  • Loading branch information
PBillingsby authored Jul 18, 2024
2 parents ca44f92 + 79df703 commit 43a92e7
Show file tree
Hide file tree
Showing 147 changed files with 384 additions and 3,700 deletions.
1 change: 0 additions & 1 deletion core/create-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ export async function createApp(config: CreateMagicAppConfig) {
...(await buildTemplate({
...config,
chain: undefined,
product: undefined,
configuration: undefined,
isChosenTemplateValid: false,
isQuickstart: false,
Expand Down
107 changes: 26 additions & 81 deletions core/utils/templateMappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
AuthTypePrompt,
BlockchainNetworkPrompt,
ConfigurationPrompt,
ProductPrompt,
ProjectNamePrompt,
PublishableApiKeyPrompt,
} from 'scaffolds/prompts';
Expand All @@ -14,27 +13,19 @@ import DedicatedScaffold, { flags as dedicatedFlags } from '../../scaffolds/next
import FlowDedicatedScaffold, {
flags as flowDedicatedFlags,
} from '../../scaffolds/nextjs-flow-dedicated-wallet/scaffold';
import FlowUniversalScaffold, {
flags as flowUniversalFlags,
} from '../../scaffolds/nextjs-flow-universal-wallet/scaffold';
import SolanaDedicatedScaffold, {
flags as solanaDedicatedFlags,
} from '../../scaffolds/nextjs-solana-dedicated-wallet/scaffold';
import UniversalScaffold, { flags as universalFlags } from '../../scaffolds/nextjs-universal-wallet/scaffold';
import { Timer } from './timer';

export type Chain = 'evm' | 'solana' | 'flow';
export type Template =
| 'nextjs-dedicated-wallet'
| 'nextjs-universal-wallet'
| 'nextjs-solana-dedicated-wallet'
| 'nextjs-flow-universal-wallet'
| 'nextjs-flow-dedicated-wallet';

export type Product = 'universal' | 'dedicated';
type ConfigType = CreateMagicAppConfig & {
chain: Chain | undefined;
product: Product | undefined;
configuration: string | undefined;
isChosenTemplateValid: boolean;
isQuickstart: boolean;
Expand All @@ -43,32 +34,16 @@ type ConfigType = CreateMagicAppConfig & {
function mapTemplateToChain(template: string): Chain | undefined {
switch (template) {
case 'nextjs-dedicated-wallet':
case 'nextjs-universal-wallet':
return 'evm';
case 'nextjs-solana-dedicated-wallet':
return 'solana';
case 'nextjs-flow-universal-wallet':
case 'nextjs-flow-dedicated-wallet':
return 'flow';
default:
return undefined;
}
}

function mapTemplateToProduct(template: string): Product | undefined {
switch (template) {
case 'nextjs-dedicated-wallet':
case 'nextjs-solana-dedicated-wallet':
case 'nextjs-flow-dedicated-wallet':
return 'dedicated';
case 'nextjs-universal-wallet':
case 'nextjs-flow-universal-wallet':
return 'universal';
default:
return undefined;
}
}

export async function mapTemplateToScaffold(
template: string,
appData: any,
Expand All @@ -93,11 +68,6 @@ export async function mapTemplateToScaffold(
data.loginMethods = await AuthTypePrompt.loginMethodsPrompt();
}
return new DedicatedScaffold(data);
case 'nextjs-universal-wallet':
if (!data.network) {
data.network = await BlockchainNetworkPrompt.evmNetworkPrompt();
}
return new UniversalScaffold(data);
case 'nextjs-solana-dedicated-wallet':
if (!data.network) {
data.network = await BlockchainNetworkPrompt.solanaNetworkPrompt();
Expand All @@ -106,11 +76,6 @@ export async function mapTemplateToScaffold(
data.loginMethods = await AuthTypePrompt.loginMethodsPrompt();
}
return new SolanaDedicatedScaffold(data);
case 'nextjs-flow-universal-wallet':
if (!data.network) {
data.network = await BlockchainNetworkPrompt.flowNetworkPrompt();
}
return new FlowUniversalScaffold(data);
case 'nextjs-flow-dedicated-wallet':
if (!data.network) {
data.network = await BlockchainNetworkPrompt.flowNetworkPrompt();
Expand All @@ -128,12 +93,8 @@ export function mapTemplateToFlags(template: string): any {
switch (template) {
case 'nextjs-dedicated-wallet':
return dedicatedFlags;
case 'nextjs-universal-wallet':
return universalFlags;
case 'nextjs-solana-dedicated-wallet':
return solanaDedicatedFlags;
case 'nextjs-flow-universal-wallet':
return flowUniversalFlags;
case 'nextjs-flow-dedicated-wallet':
return flowDedicatedFlags;
default:
Expand All @@ -145,7 +106,6 @@ const quickstartConfig = (config: ConfigType): ConfigType => ({
...config,
template: 'nextjs-dedicated-wallet',
network: 'polygon-amoy',
product: 'dedicated',
chain: 'evm',
isChosenTemplateValid: true,
isQuickstart: true,
Expand All @@ -155,14 +115,14 @@ const solanaConfig = async (config: ConfigType): Promise<ConfigType> => ({
...config,
template: 'nextjs-solana-dedicated-wallet',
network: await BlockchainNetworkPrompt.solanaNetworkPrompt(),
product: 'dedicated',
chain: 'solana',
isChosenTemplateValid: true,
isQuickstart: false,
});

export const buildTemplate = async (appConfig: ConfigType): Promise<ConfigType> => {
let config = appConfig;
let config = { ...appConfig };

if (!config.projectName) {
config.projectName = await ProjectNamePrompt.askProjectName();
}
Expand All @@ -171,59 +131,44 @@ export const buildTemplate = async (appConfig: ConfigType): Promise<ConfigType>
config.configuration = await ConfigurationPrompt.askConfiguration();

if (config.configuration === 'quickstart') {
config = quickstartConfig(config);
return config;
return quickstartConfig(config);
}
} else {
config = {
...config,
product: mapTemplateToProduct(config.template),
chain: mapTemplateToChain(config.template),
};
config.chain = mapTemplateToChain(config.template);
}

if (!config.chain && !config.network) {
if (!config.chain) {
config.chain = await BlockchainNetworkPrompt.chainPrompt();
}

if (!config.network) {
if (config.chain === 'solana') {
config = await solanaConfig(config);
} else if (config.chain === 'flow') {
config.network = await BlockchainNetworkPrompt.flowNetworkPrompt();
} else if (config.chain === 'evm') {
config.network = await BlockchainNetworkPrompt.evmNetworkPrompt();
switch (config.chain) {
case 'solana':
config = await solanaConfig(config);
break;
case 'flow':
config.network = await BlockchainNetworkPrompt.flowNetworkPrompt();
break;
case 'evm':
config.network = await BlockchainNetworkPrompt.evmNetworkPrompt();
break;
}
} else if (
config.network === 'ethereum' ||
config.network === 'ethereum-sepolia' ||
config.network === 'polygon' ||
config.network === 'polygon-amoy' ||
config.network === 'etherlink-testnet'
) {
config.chain = 'evm';
} else if (config.network === 'solana-devnet' || config.network === 'solana-mainnet') {
config.chain = 'solana';
} else {
config.chain = 'flow';
}

if (!config.product) {
config.product = await ProductPrompt.askProduct();
const evmNetworks = ['ethereum', 'ethereum-sepolia', 'polygon', 'polygon-amoy', 'etherlink-testnet', 'zksync', 'zksync-sepolia'];
const solanaNetworks = ['solana-devnet', 'solana-mainnet'];

if (config.product === 'universal') {
if (config.chain === 'flow') {
config.template = 'nextjs-flow-universal-wallet';
} else {
config.template = 'nextjs-universal-wallet';
}
} else if (config.chain === 'flow') {
config.template = 'nextjs-flow-dedicated-wallet';
if (evmNetworks.includes(config.network)) {
config.chain = 'evm';
} else if (solanaNetworks.includes(config.network)) {
config.chain = 'solana';
} else {
config.template = 'nextjs-dedicated-wallet';
config.chain = 'flow';
}
config.isChosenTemplateValid = true;
}

config.template = config.chain === 'flow' ?
'nextjs-flow-dedicated-wallet' : 'nextjs-dedicated-wallet';
config.isChosenTemplateValid = true;

return config;
};
10 changes: 0 additions & 10 deletions scaffolds/dev-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,10 @@ export const templateDevData = {
loginMethods: ['EmailOTP', 'SMSOTP'],
projectName: 'My Flow Dedicated Wallet',
},
'nextjs-flow-universal-wallet': {
network: 'flow-testnet',
publishableApiKey: 'pk_live_8D6C562ABCA3140A',
projectName: 'My Flow Universal Wallet',
},
'nextjs-solana-dedicated-wallet': {
network: 'solana-devnet',
publishableApiKey: 'pk_live_FD2D70B32ABE11BD',
loginMethods: ['EmailOTP', 'SMSOTP'],
projectName: 'My Solana Dedicated Wallet',
},
'nextjs-universal-wallet': {
network: 'ethereum-sepolia',
publishableApiKey: 'pk_live_8D6C562ABCA3140A',
projectName: 'My Universal Wallet',
},
};
11 changes: 6 additions & 5 deletions scaffolds/nextjs-dedicated-wallet/scaffold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ export default class DedicatedScaffold extends BaseScaffold {
'./src/components/magic/wallet-methods/Disconnect.tsx',
'./src/components/magic/wallet-methods/GetIdToken.tsx',
'./src/components/magic/wallet-methods/GetMetadata.tsx',
'./src/components/magic/Dashboard.tsx',
'./src/components/magic/DevLinks.tsx',
'./src/components/magic/Header.tsx',
'./src/components/ui/Dashboard.tsx',
'./src/components/ui/DevLinks.tsx',
'./src/components/ui/Header.tsx',
'./src/components/magic/Login.tsx',
'./src/components/magic/MagicProvider.tsx',
'./src/components/magic/MagicDashboardRedirect.tsx',
'./src/hooks/MagicProvider.tsx',
'./src/hooks/Web3.tsx',
'./src/components/ui/MagicDashboardRedirect.tsx',
'./src/pages',
'./src/styles',
'./src/utils',
Expand Down
45 changes: 45 additions & 0 deletions scaffolds/nextjs-dedicated-wallet/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,51 @@ This scaffold is meant to help you bootstrap your own projects with Magic's [Ded

The folder structure of this scaffold is designed to encapsulate all things Magic into one place so you can easily add or remove components and functionality. For example, all Magic-specific components are in the `src/components/magic` directory while generic UI components are in the `src/components/ui` directory.

## Usage

This project uses Next.js but relies on fairly standard React components and hooks. Magic-related components are in the `/src/components/magic` directory, all other UI components are in the `/src/components/ui` directory, utility functions are in `/src/utils` and hooks are in the `/src/hooks` directory.

Use this project as a reference for how to use this template or implement Magic in your own project. Key areas to look at include:

### Custom Hooks
In the `/src/hooks` directory, the `MagicProvider` hook sets up and provides a Magic instance for using the Magic SDK and OAuth extension. Additionally, the `Web3.tsx` hook initializes and provides a Web3 instance using the Magic provider.

### Login Functionality
The `Login.tsx` component, located in `/src/components/magic`, manages the display and functionality of various login methods on the login page. It is a central piece for handling user authentication.

Here is a list of the available authentication methods:
- `Discord.tsx` - Handles authentication using Discord OAuth.
- `Facebook.tsx` - Handles authentication using Facebook OAuth.
- `Google.tsx` - Handles authentication using Google OAuth.
- `Twitch.tsx` - Handles authentication using Twitch OAuth.
- `EmailOTP.tsx` - Handles authentication using email one-time password (OTP).
- `Github.tsx` - Handles authentication using GitHub OAuth.
- `SMSOTP.tsx` - Handles authentication using SMS one-time password (OTP).
- `Twitter.tsx` - Handles authentication using Twitter OAuth.

### User Interaction Components

- `/src/components/magic/auth`: This contains all of the authentication methods.

- `/src/components/magic/cards`: The `SendTransactionCard.tsx` component facilitate transaction processes, `UserInfoCard.tsx` displays user information, `WalletMethodsCard.tsx` manages authentication tokens and user metadata and `SmartContract.tsx` interacts with a basic storage contract.

- `/src/components/magic/wallet-methods`: This directory includes several components that provide specific functionalities:
- `Disconnect.tsx` handles the disconnection of the user's session from the application.
- `GetIdToken.tsx` retrieves the ID token for the authenticated user.
- `GetMetadata.tsx` retrieves metadata information about the authenticated user.
- `UpdateEmail.tsx` allows the user to update their email address.

### Utility Functions
The `/src/utils` directory includes utility files that support various aspects of the application:
- `common.ts` manages user authentication processes. The `logout` function handle the process of logging out a user and clearing their authentication data, while `saveUserInfo` saves the user's token, login method, and address to local storage.
- `network.ts` defines network configurations and utilities, such as URLs, chain IDs, tokens, and block explorer links.
- `showToast.ts` handles customizable toast notifications.
- `smartContract.ts` contains functions and configurations for interacting with smart contracts, such as retrieving contract IDs, determining testnet status, generating hash links, and defining contract ABIs.

These utilities are essential for supporting various aspects of the application.

### UI Components
The `/src/components/ui` directory contains reusable UI components for building the user interface. This includes components for creating and styling cards (`Card`, `CardHeader`, `CardLabel`), layout elements for the dashboard (`Dashboard`), separators (`Divider`), error messages (`ErrorText`), form elements (`FormButton`, `FormInput`), redirection handling within the Magic dashboard (`MagicDashboardRedirect`), spacing elements (`Spacer`), loading indicators (`Spinner`), and displaying transaction history (`TransactionHistory`).
## Next.js

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
Expand Down
6 changes: 3 additions & 3 deletions scaffolds/nextjs-dedicated-wallet/template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"lint": "next lint --fix"
},
"dependencies": {
"@magic-ext/oauth": "^15.0.0",
"@magic-ext/oauth": "^22.0.7",
"@types/node": "20.3.3",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
"autoprefixer": "10.4.14",
"classnames": "^2.3.2",
"magic-sdk": "^21.5.0",
"magic-sdk": "^28.0.7",
"next": "13.4.7",
"postcss": "8.4.24",
"react": "18.2.0",
Expand All @@ -29,4 +29,4 @@
"eslint": "^8.45.0",
"eslint-config-next": "13.4.10"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { LoginProps } from '@/utils/types'
import Header from './Header'
import Header from '@/components/ui/Header'
<% loginMethods.forEach(authType => { %>
<%-`import ${authType.replaceAll(' ', '')} from './auth/${authType.replaceAll(' ', '')}';`-%>
<% }) %>
<%-`import ${authType.replaceAll(' ', '')} from './auth/${authType.replaceAll(' ', '')}';`-%>
<% }) %>

const Login = ({ token, setToken }: LoginProps) => {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LoginProps } from '@/utils/types';
import { useMagic } from '../MagicProvider';
import { useMagic } from '@/hooks/MagicProvider';
import { useEffect, useState } from 'react';
import { saveUserInfo } from '@/utils/common';
import Spinner from '@/components/ui/Spinner';
Expand All @@ -21,7 +21,7 @@ const Discord = ({ token, setToken }: LoginProps) => {
try {
if (magic) {
const result = await magic?.oauth.getRedirectResult();
const metadata = await magic?.user.getMetadata();
const metadata = await magic?.user.getInfo();
if (!metadata?.publicAddress) return;
setToken(result.magic.idToken);
saveUserInfo(result.magic.idToken, 'SOCIAL', metadata?.publicAddress);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMagic } from '../MagicProvider';
import { useMagic } from '@/hooks/MagicProvider';
import showToast from '@/utils/showToast';
import Spinner from '../../ui/Spinner';
import { RPCError, RPCErrorCode } from 'magic-sdk';
Expand All @@ -24,7 +24,7 @@ const EmailOTP = ({ token, setToken }: LoginProps) => {
setEmailError(false);
const token = await magic?.auth.loginWithEmailOTP({ email });

const metadata = await magic?.user.getMetadata();
const metadata = await magic?.user.getInfo();

if (!token || !metadata?.publicAddress) {
throw new Error('Magic login failed');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LoginProps } from '@/utils/types';
import { useMagic } from '../MagicProvider';
import { useMagic } from '@/hooks/MagicProvider';
import { useEffect, useState } from 'react';
import { saveUserInfo } from '@/utils/common';
import Spinner from '../../ui/Spinner';
Expand All @@ -21,7 +21,7 @@ const Facebook = ({ token, setToken }: LoginProps) => {
try {
if (magic) {
const result = await magic?.oauth.getRedirectResult();
const metadata = await magic?.user.getMetadata();
const metadata = await magic?.user.getInfo();
if (!metadata?.publicAddress) return;
setToken(result.magic.idToken);
saveUserInfo(result.magic.idToken, 'SOCIAL', metadata?.publicAddress);
Expand Down
Loading

0 comments on commit 43a92e7

Please sign in to comment.