Skip to content

Commit

Permalink
Unify web, xNFT & mobile (#1)
Browse files Browse the repository at this point in the history
* unify xnft and web

* better handle non connected cases

* use `URL` variable for RPC

* fix bundle command

* fix xnft useWallet

* fix `isOwner`

* remove unused pyth feeds

* add referrer logic

* use map for Referrer name -> referrer index

* Add web & xnft explanation to README

* remove console.table
  • Loading branch information
dr497 authored May 30, 2023
1 parent 5e5946c commit 83b1768
Show file tree
Hide file tree
Showing 29 changed files with 409 additions and 169 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ This open-source mobile app is designed to provide a seamless and user-friendly
| Korean translation | Translate the app in Korean ||||
| Turkish translation | Translate the app in Turkish ||||

<br />
<h2 align="center">Development and Build Commands 💻</h2>
<br />

This section introduces commands to expedite the development and building processes of our React Native project for web and xNFT platforms, enhancing the wallet connection experience.

### Development

For development, you can initiate the server with `yarn dev:xnft` for the xNFT target and `yarn dev:web` for the web target.

### Building

To transition to a production-ready build, use `yarn build:xnft` for the xNFT target and `yarn build:web` for the web target.

### Wallet Connection

Both development and build configurations accommodate seamless wallet connection for web and xNFT platforms. Thus, these commands facilitate a unified and efficient experience of developing and building applications for different targets. Please report issues or suggestions through an issue or a pull request.

<br />
<h2 align="center">Features 🛠️</h2>
<br />
Expand Down
5 changes: 3 additions & 2 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = function(api) {
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
presets: ["babel-preset-expo"],
plugins: ["transform-inline-environment-variables"],
};
};
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
"private": true,
"homepage": ".",
"scripts": {
"build": "expo export:web",
"build:xnft": "XNFT=true expo export:web",
"build:web": "WEB=true expo export:web",
"start": "yarn build && npx xnft web",
"bundle": "yarn build && npx xnft bundle",
"dev": "GENERATE_SOURCEMAP=false expo start --web & npx xnft --iframe http://localhost:19006"
"bundle": "yarn build:xnft && npx xnft bundle",
"dev": "GENERATE_SOURCEMAP=false expo start --clear --web & npx xnft --iframe http://localhost:19006",
"dev:xnft": "XNFT=true yarn dev",
"dev:web": "WEB=true yarn dev"
},
"dependencies": {
"@adraffy/ens-normalize": "^1.9.0",
Expand All @@ -25,6 +28,8 @@
"@react-navigation/stack": "6.2.1",
"@solana/buffer-layout": "^4.0.1",
"@solana/spl-token": "^0.3.7",
"@solana/wallet-adapter-react": "^0.15.32",
"@solana/wallet-adapter-react-ui": "^0.9.31",
"@solana/web3.js": "^1.75.0",
"axios": "^1.4.0",
"buffer": "^6.0.3",
Expand Down
74 changes: 64 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ import { DomainView } from "./screens/DomainView";
import { SuccessCheckoutModal } from "./components/SuccessCheckoutModal";
import { EditRecordModal } from "./components/EditRecordModal";
import { ErrorModal } from "./components/ErrorModal";
import { usePublicKeys } from "./hooks/xnft-hooks";
import { SuccessModal } from "./components/SuccessModal";
import { TransferModal } from "./components/TransferModal";
import { WormholeExplainerModal } from "./components/WormholeExplainerModal";
import { EditPicture } from "./components/EditPicture";
import { ProgressExplainerModal } from "./components/ProgressExplainerModal";
import { SearchModal } from "./components/SearchModal";
import { DiscountExplainerModal } from "./components/DiscountExplainerModal";
import { isXnft, isMobile, isWeb } from "./utils/platform";
import { ReactNode, useEffect, useMemo } from "react";
import {
ConnectionProvider,
WalletProvider,
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import { useWallet } from "./hooks/useWallet";
import { URL } from "./utils/rpc";
import { useReferrer } from "./hooks/useReferrer";

const Stack = createStackNavigator<RootBottomTabParamList>();

Expand Down Expand Up @@ -79,8 +88,18 @@ const SearchNavigator = () => {
};

function TabNavigator() {
useReferrer();
const [cart] = useRecoilState(cartState);
const publicKey = usePublicKeys().get("solana");
const { publicKey, setVisible, connected } = useWallet();

console.log("Connected: ", connected);

useEffect(() => {
if (!connected) {
setVisible(true);
}
}, []);

return (
<Tab.Navigator
initialRouteName="Home"
Expand All @@ -91,8 +110,17 @@ function TabNavigator() {
>
<Tab.Screen
name="Profile"
initialParams={{ owner: publicKey }}
initialParams={{ owner: publicKey?.toBase58() }}
children={({ route }) => <ProfileScreen owner={route.params.owner} />}
listeners={({ navigation }) => ({
tabPress: (e) => {
e.preventDefault();
if (!connected) {
return setVisible(true);
}
navigation.navigate("Profile", { owner: publicKey?.toBase58() });
},
})}
options={{
tabBarLabel: "Profile",
tabBarIcon: ({ color, size }) => (
Expand Down Expand Up @@ -166,14 +194,40 @@ function App() {
}

return (
<RecoilRoot>
<NavigationContainer>
<ModalProvider stack={stackModal}>
<TabNavigator />
</ModalProvider>
</NavigationContainer>
</RecoilRoot>
<Wrap>
<RecoilRoot>
<NavigationContainer>
<ModalProvider stack={stackModal}>
<TabNavigator />
</ModalProvider>
</NavigationContainer>
</RecoilRoot>
</Wrap>
);
}

const Wrap = ({ children }: { children: ReactNode }) => {
const wallets = useMemo(() => [], []);
if (isXnft) {
return <>{children}</>;
}
if (isWeb) {
return (
<ConnectionProvider endpoint={URL}>
<WalletProvider autoConnect wallets={wallets}>
<WalletModalProvider>{children}</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
}
if (isMobile) {
<ConnectionProvider endpoint={URL}>
<WalletProvider autoConnect wallets={wallets}>
<WalletModalProvider>{children}</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>;
}
return <>{children}</>;
};

export default registerRootComponent(App);
6 changes: 6 additions & 0 deletions src/atoms/referrer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from "recoil";

export const referrerState = atom<undefined | number>({
key: "referrer",
default: undefined,
});
16 changes: 11 additions & 5 deletions src/components/EditPicture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import {
Numberu32,
NAME_OFFERS_ID,
} from "@bonfida/spl-name-service";
import { usePublicKeys, useSolanaConnection } from "../hooks/xnft-hooks";
import { useSolanaConnection } from "../hooks/xnft-hooks";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import { removeZeroRight } from "../utils/record/zero";
import { sendTx } from "../utils/send-tx";
import { WrapModal } from "./WrapModal";
import { isTokenized } from "@bonfida/name-tokenizer";
import { unwrap } from "../utils/unwrap";
import { registerFavourite } from "@bonfida/name-offers";
import { useWallet } from "../hooks/useWallet";

export const EditPicture = ({
modal: { closeModal, getParam },
Expand All @@ -42,11 +43,11 @@ export const EditPicture = ({
const [loading, setLoading] = useState(false);
const [pic, setPic] = useState<string | undefined>("");
const connection = useSolanaConnection();
const publicKey = usePublicKeys().get("solana");
const { publicKey, signTransaction, setVisible, connected } = useWallet();

const handle = async () => {
if (!pic) return;
if (!connection || !publicKey) return;
if (!connection || !publicKey || !signTransaction) return;
try {
setLoading(true);
const ixs: TransactionInstruction[] = [];
Expand Down Expand Up @@ -141,7 +142,12 @@ export const EditPicture = ({
);
ixs.push(ix);

const sig = await sendTx(connection, new PublicKey(publicKey), ixs);
const sig = await sendTx(
connection,
new PublicKey(publicKey),
ixs,
signTransaction
);
console.log(sig);

setLoading(false);
Expand Down Expand Up @@ -177,7 +183,7 @@ export const EditPicture = ({
<View style={tw`flex flex-col items-center`}>
<TouchableOpacity
disabled={loading}
onPress={handle}
onPress={connected ? handle : () => setVisible(true)}
style={tw`bg-blue-900 w-full h-[40px] my-1 flex flex-row items-center justify-center rounded-lg`}
>
<Text style={tw`font-bold text-white`}>Confirm</Text>
Expand Down
48 changes: 20 additions & 28 deletions src/components/EditRecordModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@ import {
ActivityIndicator,
} from "react-native";
import tw from "../utils/tailwind";
import {
PublicKey,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { usePublicKeys, useSolanaConnection } from "../hooks/xnft-hooks";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import { useSolanaConnection } from "../hooks/xnft-hooks";
import { ChainId, Network, post } from "@bonfida/sns-emitter";
import { Buffer } from "buffer";
import { useModal } from "react-native-modalfy";
Expand All @@ -32,6 +28,7 @@ import { removeZeroRight } from "../utils/record/zero";
import { WrapModal } from "./WrapModal";
import { getPlaceholder } from "../utils/record/place-holder";
import { sendTx } from "../utils/send-tx";
import { useWallet } from "../hooks/useWallet";

export const EditRecordModal = ({
modal: { closeModal, getParam },
Expand All @@ -44,13 +41,13 @@ export const EditRecordModal = ({
const currentContent = getParam<string | undefined>("currentValue");
const refresh = getParam<() => Promise<void>>("refresh");
const connection = useSolanaConnection();
const publicKey = usePublicKeys().get("solana");
const { publicKey, signTransaction, setVisible, connected } = useWallet();
const { openModal } = useModal();

const [value, setValue] = useState(currentContent ? currentContent : "");

const handleUpdate = async () => {
if (!connection || !publicKey) return;
if (!connection || !publicKey || !signTransaction) return;
try {
setLoading(true);
const ixs: TransactionInstruction[] = [];
Expand Down Expand Up @@ -94,8 +91,8 @@ export const EditRecordModal = ({
connection,
Buffer.from([1]).toString() + record,
space, // Hardcode space to 2kB
new PublicKey(publicKey),
new PublicKey(publicKey),
publicKey,
publicKey,
lamports,
undefined,
parent
Expand All @@ -108,16 +105,16 @@ export const EditRecordModal = ({
pubkey
);

if (!registry.owner.equals(new PublicKey(publicKey))) {
if (!registry.owner.equals(publicKey)) {
// Record was created before domain was transfered
const ix = transferInstruction(
NAME_PROGRAM_ID,
pubkey,
new PublicKey(publicKey),
publicKey,
registry.owner,
undefined,
parent,
new PublicKey(publicKey)
publicKey
);
ixs.push(ix);
}
Expand All @@ -130,7 +127,7 @@ export const EditRecordModal = ({
pubkey,
new Numberu32(0),
zero,
new PublicKey(publicKey)
publicKey
);
ixs.push(ix);
}
Expand Down Expand Up @@ -162,7 +159,7 @@ export const EditRecordModal = ({
pubkey,
new Numberu32(0),
data,
new PublicKey(publicKey)
publicKey
);
ixs.push(ix);

Expand All @@ -172,19 +169,14 @@ export const EditRecordModal = ({
ChainId.BSC,
Network.Mainnet,
domain,
new PublicKey(publicKey),
publicKey,
1_000,
pubkey
);
ixs.push(...ix);
}

let tx = new Transaction().add(...ixs);
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.feePayer = new PublicKey(publicKey);
tx = await window.xnft.solana.signTransaction(tx);

const sig = await connection.sendRawTransaction(tx.serialize());
const sig = await sendTx(connection, publicKey, ixs, signTransaction);
console.log(sig);

await sleep(400);
Expand All @@ -201,17 +193,17 @@ export const EditRecordModal = ({
};

const handleDelete = async () => {
if (!connection || !publicKey) return;
if (!connection || !publicKey || !signTransaction) return;
try {
setLoading(true);
const { pubkey } = getDomainKeySync(record + "." + domain, true);
const ix = deleteInstruction(
NAME_PROGRAM_ID,
pubkey,
new PublicKey(publicKey),
new PublicKey(publicKey)
publicKey,
publicKey
);
const sig = await sendTx(connection, new PublicKey(publicKey), [ix]);
const sig = await sendTx(connection, publicKey, [ix], signTransaction);
console.log(sig);

await sleep(400);
Expand Down Expand Up @@ -239,15 +231,15 @@ export const EditRecordModal = ({
<View style={tw`flex flex-col items-center`}>
<TouchableOpacity
disabled={loading}
onPress={handleUpdate}
onPress={connected ? handleUpdate : () => setVisible(true)}
style={tw`bg-blue-900 w-full h-[40px] my-1 flex flex-row items-center justify-center rounded-lg`}
>
<Text style={tw`font-bold text-white`}>Confirm</Text>
{loading && <ActivityIndicator style={tw`ml-3`} size={16} />}
</TouchableOpacity>
<TouchableOpacity
disabled={loading}
onPress={handleDelete}
onPress={connected ? handleDelete : () => setVisible(true)}
style={tw`bg-red-400 w-full h-[40px] my-1 flex flex-row items-center justify-center rounded-lg`}
>
<Text style={tw`font-bold text-white`}>Delete</Text>
Expand Down
Loading

0 comments on commit 83b1768

Please sign in to comment.