Skip to content

Commit

Permalink
feat!: make components optional
Browse files Browse the repository at this point in the history
  • Loading branch information
gagdiez committed May 14, 2024
1 parent d1865a2 commit 319fe29
Show file tree
Hide file tree
Showing 45 changed files with 643 additions and 1,386 deletions.
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as show from './messages';
projectName,
contract,
frontend,
components,
install,
},
projectPath,
Expand All @@ -24,6 +25,7 @@ import * as show from './messages';
createSuccess = await createProject({
contract,
frontend,
components,
templatesDir: path.resolve(__dirname, '../templates'),
projectPath,
});
Expand Down
14 changes: 9 additions & 5 deletions src/make.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import fs from 'fs';
import { ncp } from 'ncp';
import path from 'path';

export async function createProject({ contract, frontend, projectPath, templatesDir }: CreateContractParams & CreateGatewayParams): Promise<boolean> {

export async function createProject({ contract, frontend, components, projectPath, templatesDir }: CreateContractParams & CreateGatewayParams): Promise<boolean> {
if (contract !== 'none') {
await createContract({ contract, projectPath, templatesDir });
} else {
await createGateway({ frontend, projectPath, templatesDir });
await createGateway({ frontend, components, projectPath, templatesDir });
}

return true;
Expand All @@ -21,13 +20,18 @@ async function createContract({ contract, projectPath, templatesDir }: CreateCon
const sourceContractDir = path.resolve(templatesDir, 'contracts', contract);
fs.mkdirSync(projectPath, { recursive: true });
await copyDir(sourceContractDir, projectPath);

}

async function createGateway({ frontend, projectPath, templatesDir }: CreateGatewayParams) {
async function createGateway({ frontend, components, projectPath, templatesDir }: CreateGatewayParams) {
const sourceFrontendDir = path.resolve(`${templatesDir}/frontend/${frontend}`);
fs.mkdirSync(projectPath, { recursive: true });
await copyDir(sourceFrontendDir, projectPath);

if (components) {
const sourceComponentsDir = path.resolve(`${templatesDir}/frontend/components/${frontend}`);
await copyDir(sourceComponentsDir, projectPath);
}
}

// Wrap `ncp` tool to wait for the copy to finish when using `await`
Expand Down
2 changes: 1 addition & 1 deletion src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const frontendTemplates: FrontendMessage = {
export const successFrontendToText = (frontend: Frontend) =>
frontend === 'none'
? ''
: chalk`a gateway using ${frontendTemplates[frontend]}`;
: chalk`a web-app using ${frontendTemplates[frontend]}`;

export const setupSuccess = (
projectName: ProjectName,
Expand Down
3 changes: 2 additions & 1 deletion src/tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const tracker = mixpanel.init(MIXPANEL_TOKEN);
export const trackingMessage = chalk`Near collects anonymous information on the commands used. No personal information that could identify you is shared`;

// TODO: track different failures & install usage
export const trackUsage = async (frontend: Frontend, contract: Contract) => {
export const trackUsage = async (frontend: Frontend, components: boolean, contract: Contract) => {
// prevents logging from CI
if (process.env.NEAR_ENV === 'ci' || process.env.NODE_ENV === 'ci') {
console.log('Mixpanel logging is skipped in CI env');
Expand All @@ -18,6 +18,7 @@ export const trackUsage = async (frontend: Frontend, contract: Contract) => {
try {
const mixPanelProperties = {
frontend,
components,
contract,
os: process.platform,
nodeVersion: process.versions.node,
Expand Down
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
export type Contract = 'ts' | 'rs' | 'none';
export const CONTRACTS: Contract[] = ['ts', 'rs', 'none'];

export type Frontend = 'next-app' | 'next-page' | 'none';
export const FRONTENDS: Frontend[] = ['next-app' , 'next-page', 'none'];

export type App = 'contract' | 'gateway';
export const APPS: App[] = ['contract', 'gateway'];

export type ProjectName = string;

export interface UserConfig {
contract: Contract;
frontend: Frontend;
components: boolean;
projectName: ProjectName;
install: boolean;
}
Expand All @@ -20,6 +25,7 @@ export type CreateContractParams = {

export type CreateGatewayParams = {
frontend: Frontend,
components: boolean,
projectPath: string,
templatesDir: string,
}
Expand Down
35 changes: 23 additions & 12 deletions src/user-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,28 @@ export async function getUserArgs(): Promise<UserConfig> {
const options = program.opts();
const [projectName] = program.args;
const { contract, frontend, install } = options;
return { contract, frontend, projectName, install };
return { contract, frontend, components: false, projectName, install };
}

type Choices<T> = { title: string, description?: string, value: T }[];

const appChoices: Choices<App> = [
{ title: 'A Near Gateway (Web App)', description: 'A multi-chain App that talks with Near contracts and Near components', value: 'gateway' },
{ title: 'A Near Smart Contract', description: 'A smart contract to be deployed in the Near Blockchain', value: 'contract' },
{ title: 'A Web App', description: 'A Web App that talks with Near contracts', value: 'gateway' },
{ title: 'A Smart Contract', description: 'A smart contract to be deployed in the Near Blockchain', value: 'contract' },
];
const contractChoices: Choices<Contract> = [
{ title: 'JS/TS Contract', description: 'A Near contract written in javascript/typescript', value: 'ts' },
{ title: 'Rust Contract', description: 'A Near contract written in Rust', value: 'rs' },
];

const frontendChoices: Choices<Frontend> = [
{ title: 'NextJs (Classic)', description: 'A composable app built using Next.js, React and Near components', value: 'next-page' },
{ title: 'NextJS (App Router)', description: 'A composable app built using Next.js, React and Near components', value: 'next-app' },
{ title: 'NextJs (Classic)', description: 'A web-app built using Next.js Page Router', value: 'next-page' },
{ title: 'NextJS (App Router)', description: 'A web-app built using Next.js new App Router', value: 'next-app' },
];

const componentChoices: Choices<Boolean> = [
{ title: 'No', value: false },
{ title: 'Yes', value: true },
];

const appPrompt: PromptObject = {
Expand All @@ -57,10 +62,17 @@ const appPrompt: PromptObject = {
const frontendPrompt: PromptObject = {
type: 'select',
name: 'frontend',
message: 'Select a framework for your frontend (Gateway)',
message: 'Select a framework for your frontend',
choices: frontendChoices,
};

const componentsPrompt: PromptObject = {
type: 'select',
name: 'components',
message: 'Are you planning in using on-chain NEAR Components (aka BOS Components)?',
choices: componentChoices,
};

const contractPrompt: PromptObject[] = [
{
type: 'select',
Expand Down Expand Up @@ -95,15 +107,15 @@ export async function getUserAnswers(): Promise<UserConfig> {

if (app === 'gateway') {
// If gateway, ask for the framework to use
const { frontend, projectName, install } = await promptUser([frontendPrompt, namePrompts, npmPrompt]);
return { frontend, contract: 'none', projectName, install };
const { frontend, components, projectName, install } = await promptUser([frontendPrompt, componentsPrompt, namePrompts, npmPrompt]);
return { frontend, components, contract: 'none', projectName, install };
} else {
// If contract, ask for the language for the contract
let { contract } = await promptUser(contractPrompt);

const { projectName } = await promptUser(namePrompts);
const install = contract === 'ts' ? (await promptUser(npmPrompt)).install as boolean : false;
return { frontend: 'none', contract, projectName, install };
return { frontend: 'none', components: false, contract, projectName, install };
}
}

Expand Down Expand Up @@ -133,8 +145,8 @@ export async function promptAndGetConfig(): Promise<{ config: UserConfig, projec
if (!validateUserArgs(args)) return;

// track user input
const { frontend, contract } = args;
trackUsage(frontend, contract);
const { frontend, components, contract } = args;
trackUsage(frontend, components, contract);

let path = projectPath(args.projectName);

Expand Down Expand Up @@ -165,7 +177,6 @@ const validateUserArgs = (args: UserConfig) => {
}

if ((args.contract === 'none') === (args.frontend === 'none')) {
console.log(args.contract, args.frontend);
show.argsError('Please create a contract OR a frontend');
return false;
}
Expand Down
41 changes: 41 additions & 0 deletions templates/frontend/components/next-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "hello-near",
"version": "1.0.0",
"private": true,
"engines": {
"node": ">=18"
},
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@near-wallet-selector/core": "^8.9.7",
"@near-wallet-selector/here-wallet": "^8.9.7",
"@near-wallet-selector/modal-ui": "^8.9.7",
"@near-wallet-selector/my-near-wallet": "^8.9.7",
"@web3-onboard/core": "^2.21.5",
"@web3-onboard/injected-wallets": "^2.10.15",
"@web3-onboard/ledger": "^2.6.0",
"@web3-onboard/react": "^2.8.16",
"@web3-onboard/walletconnect": "^2.5.4",
"base64-js": "^1.5.1",
"bootstrap": "^5.3.3",
"ieee754": "^1.2.1",
"near-api-js": "^3.0.4",
"near-social-vm": "github:gagdiez/VM",
"next": "14.2.0",
"pino-pretty": "^11.0.0",
"react": "^18",
"react-dom": "^18"
},
"overrides": {
"near-api-js": "^3.0.4"
},
"devDependencies": {
"eslint": "^8.57.0",
"eslint-config-next": "14.2.2"
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
'use client';
import dynamic from 'next/dynamic';

import styles from '@/app/app.module.css';
import { DocsCard, HelloNearCard } from '@/components/cards';
import { Cards } from '@/components/cards';
import { Components } from '@/config';

const Component = dynamic(() => import('@/components/vm-component'), { ssr: false });
const Component = dynamic(() => import('@/components/vm'), {
ssr: false,
loading: () => <p>Loading Component...</p>,
});

export default function HelloComponents() {

export default function HelloComponents() {
return (
<>
<main className={styles.main}>
Expand All @@ -19,9 +21,11 @@ export default function HelloComponents() {
</p>
</div>
<div className={styles.center}>
<h1> <code>Multi-chain</code> Components Made Simple </h1>
<h1>
<code>Multi-chain</code> Components Made Simple
</h1>
</div>
<div className='row'>
<div className="row">
<div className="col-6">
<Component src={Components.HelloNear} />
<p className="my-4">&nbsp;</p>
Expand All @@ -34,10 +38,9 @@ export default function HelloComponents() {
<hr />

<div className={styles.grid}>
<DocsCard />
<HelloNearCard />
<Cards />
</div>
</main>
</>
);
}
}
43 changes: 43 additions & 0 deletions templates/frontend/components/next-app/src/components/cards.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Link from 'next/link';

import styles from '@/app/app.module.css';

export const Cards = () => {
return (
<div className={styles.grid}>
<Link
href="https://docs.near.org/build/web3-apps/quickstart"
className={styles.card}
target='_blank'
rel="noopener noreferrer"
>
<h2>
Near Docs <span>-&gt;</span>
</h2>
<p>Learn how this application works, and what you can build on Near.</p>
</Link>

<Link
href="/hello-near"
className={styles.card}
rel="noopener noreferrer"
>
<h2>
Near Integration <span>-&gt;</span>
</h2>
<p>Discover how simple it is to interact with a Near smart contract.</p>
</Link>

<Link
href="/hello-components"
className={styles.card}
rel="noopener noreferrer"
>
<h2>
Web3 Components <span>-&gt;</span>
</h2>
<p>See how Web3 components can help you to create multi-chain apps.</p>
</Link>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Widget, EthersProviderContext } from 'near-social-vm';
import { useEthersProviderContext } from '@/wallets/web3-wallet';
import { useInitNear } from 'near-social-vm';
import { useEffect } from 'react';
import { useStore } from '@/app/layout';
'use client';

import { useEffect, useContext } from 'react';
import { useInitNear, Widget, EthersProviderContext } from 'near-social-vm';

import { NearContext } from '@/context';
import { NetworkId } from '@/config';
import { useEthersProviderContext } from '@/wallets/eth';

export default function Component({ src }) {
const ethersContext = useEthersProviderContext();
const { wallet } = useStore();
const { wallet } = useContext(NearContext);
const { initNear } = useInitNear();

useEffect(() => {
Expand Down
23 changes: 23 additions & 0 deletions templates/frontend/components/next-app/src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const contractPerNetwork = {
mainnet: 'hello.near-examples.near',
testnet: 'hello.near-examples.testnet',
};

const componentsPerNetwork = {
mainnet: {
socialDB: 'social.near',
Lido: 'zavodil.near/widget/Lido',
HelloNear: 'gagdiez.near/widget/HelloNear',
LoveNear: 'gagdiez.near/widget/LoveNear',
},
testnet: {
socialDB: 'v1.social08.testnet',
Lido: 'influencer.testnet/widget/Lido',
HelloNear: 'influencer.testnet/widget/HelloNear',
LoveNear: 'influencer.testnet/widget/LoveNear',
}
};

export const NetworkId = 'testnet';
export const HelloNearContract = contractPerNetwork[NetworkId];
export const Components = componentsPerNetwork[NetworkId];
Loading

0 comments on commit 319fe29

Please sign in to comment.