Skip to content

Commit

Permalink
feat(expo): added expo with server api
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony23991 committed Mar 25, 2024
1 parent 4e4a673 commit 0d6fd93
Show file tree
Hide file tree
Showing 19 changed files with 15,551 additions and 15 deletions.
6 changes: 6 additions & 0 deletions examples/fullstack/expo/.babelrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
36 changes: 36 additions & 0 deletions examples/fullstack/expo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/

# Native
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env
.env*.local

# typescript
*.tsbuildinfo
48 changes: 48 additions & 0 deletions examples/fullstack/expo/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"expo": {
"name": "jan-expo-demo",
"slug": "jan-expo-demo",
"scheme": "jan-expo-demo",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true,
"infoPlist": {
"LSApplicationQueriesSchemes": [
"metamask",
"trust",
"safe",
"rainbow",
"uniswap"
]
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/favicon.png",
"output": "server",
"bundler": "metro"
},
"plugins": [
[
"expo-router",
{
"origin": "https://jan-expo-demo.dev/"
}
]
]
}
}
54 changes: 54 additions & 0 deletions examples/fullstack/expo/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import '@walletconnect/react-native-compat';
import { Web3Modal, createWeb3Modal, defaultWagmiConfig } from '@web3modal/wagmi-react-native';
import * as Linking from 'expo-linking';
import 'react-native-url-polyfill/auto';
import { arbitrum, mainnet, polygon } from 'viem/chains';
import { WagmiConfig } from 'wagmi';

import { JustaNameProvider } from '@justaname.id/react/src';
import React from 'react';
import HomeScreen from './home';

const projectId = process.env.EXPO_PUBLIC_PROJECT_ID ?? ""

const metadata = {
name: 'JAN Expo Demo',
description: 'JAN Expo Demo',
url: 'https://web3modal.com',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
redirect: {
native: Linking.createURL('/'),
universal: 'YOUR_APP_UNIVERSAL_LINK.com'
}
}

const chains = [mainnet, polygon, arbitrum]

const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata });

createWeb3Modal({
projectId,
chains,
wagmiConfig
})

export default function App() {
const queryClient = new QueryClient();

const chainId = process.env.EXPO_PUBLIC_CHAIN_ID ? parseInt(process.env.EXPO_PUBLIC_CHAIN_ID) as 1 | 11155111 : undefined;

return (
<WagmiConfig config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<JustaNameProvider
backendUrl={"http://192.168.1.5:8081"}
chainId={chainId}>
<HomeScreen />
<Web3Modal />
</JustaNameProvider>
</QueryClientProvider>
</WagmiConfig>
);
}

32 changes: 32 additions & 0 deletions examples/fullstack/expo/app/api/request-challenge+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ChainId } from '@justaname.id/sdk';
import { ExpoRequest, ExpoResponse } from 'expo-router/server';
import { getJustaNameInstance } from '../../justaname';

export async function GET(req: ExpoRequest): Promise<ExpoResponse> {
const { searchParams } = req.expoUrl;

const address = searchParams.get('address');

if (!address) {
return new ExpoResponse('Address is required', { status: 400 });
}

const justaname = await getJustaNameInstance();
const chainId = parseInt(
process.env.EXPO_PUBLIC_CHAIN_ID as string
) as ChainId;
const origin = 'exp://192.168.1.5:8081';
const domain = process.env.EXPO_PUBLIC_ENS_DOMAIN as string;

try {
const challenge = await justaname.siwe.requestChallenge({
chainId,
origin,
address,
domain,
});
return ExpoResponse.json(challenge);
} catch (e: any) {
return new ExpoResponse(e.message, { status: 500 });
}
}
47 changes: 47 additions & 0 deletions examples/fullstack/expo/app/api/subnames/claim+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ChainId } from '@justaname.id/sdk';
import { ExpoRequest, ExpoResponse } from 'expo-router/server';
import { getJustaNameInstance } from '../../../justaname';

export async function POST(req: ExpoRequest): Promise<ExpoResponse> {
const { username, message, signature, address } = await req.json();

if (!username) {
return new ExpoResponse('Username is required', { status: 400 });
}

if (!address) {
return new ExpoResponse('Address is required', { status: 400 });
}

if (!signature) {
return new ExpoResponse('Signature is required', { status: 400 });
}

if (!message) {
return new ExpoResponse('Message is required', { status: 400 });
}
const justaname = await getJustaNameInstance();

const chainId = parseInt(
process.env.EXPO_PUBLIC_CHAIN_ID as string
) as ChainId;
const ensDomain = process.env.EXPO_PUBLIC_ENS_DOMAIN as string;
try {
const subname = await justaname.subnames.addSubname(
{
username: username.split('.')[0],
ensDomain,
chainId,
},
{
xSignature: signature,
xAddress: address,
xMessage: message,
}
);
return ExpoResponse.json(subname);
} catch (e: any) {
console.log('claim error', e);
return new ExpoResponse(e.message, { status: 500 });
}
}
123 changes: 123 additions & 0 deletions examples/fullstack/expo/app/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import "@ethersproject/shims";
import { useAccountSubnames, useClaimSubname, useIsSubnameAvailable } from '@justaname.id/react/src';
import '@walletconnect/react-native-compat';
import { W3mButton } from '@web3modal/wagmi-react-native';
import React, { useState } from 'react';
import { ActivityIndicator, Button, Keyboard, KeyboardAvoidingView, Platform, StyleSheet, Text, TextInput, TouchableWithoutFeedback, View } from 'react-native';
import 'react-native-url-polyfill/auto';
import { useDebounced } from "../hooks/useDebounced";


export default function HomeScreen() {
const [inputValue, setInputValue] = useState('');
// Wagmi

const {
value: debouncedSubdomain,
} = useDebounced(
inputValue,
200,
);

const { subnames } = useAccountSubnames();
const { isAvailable, isLoading } = useIsSubnameAvailable({
username: debouncedSubdomain,
ensDomain: process.env.EXPO_PUBLIC_ENS_DOMAIN as string,
})
const { claimSubname } = useClaimSubname();

const handleAddSubdomain = async () => {
return await claimSubname({
username: inputValue,
});
}

return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{ flex: 1 }}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<Text style={styles.text}>JAN WALLET TEST</Text>
<Text style={styles.subText}>Connect ur wallet</Text>
<W3mButton />
<View style={styles.inputContainer}>
<TextInput
style={[styles.input,
{
borderColor:
isAvailable
? 'lightgreen'
: 'red',
}
]}
placeholder="Enter your string"
value={inputValue}
onChangeText={setInputValue}
/>
<Text style={styles.domainText}>.{process.env.EXPO_PUBLIC_ENS_DOMAIN}</Text>
</View>
<Button
title="Claim"
onPress={handleAddSubdomain}
disabled={!isAvailable}
/>
{debouncedSubdomain.length > 2 && isLoading &&
<ActivityIndicator
size="large"
color="black"
animating={true}
style={{ marginTop: 20 }}
/>
}
<Text style={styles.subText}>Current Subdomains</Text>
<View>
{subnames.map((subdomain) => (
<Text key={subdomain.id}>{subdomain.subname}</Text>
))}
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
subText: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 5,
marginTop: 20,
},
input: {
height: 40,
width: '90%',
marginVertical: 12,
borderWidth: 2,
borderRadius: 20,
padding: 10,
marginHorizontal: 20,
},
inputContainer: {
position: 'relative',
width: '100%',
},
domainText: {
position: 'absolute',
right: 28,
top: 22,
fontSize: 15,
color: 'black'
}
});
Binary file added examples/fullstack/expo/assets/adaptive-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fullstack/expo/assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fullstack/expo/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fullstack/expo/assets/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions examples/fullstack/expo/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
21 changes: 21 additions & 0 deletions examples/fullstack/expo/hooks/useDebounced/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from "react";

export const useDebounced = (
value: string,
delay: number
): { value: string } => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(handler);
};
}, [value, delay]);

return {
value: debouncedValue,
};
};
12 changes: 12 additions & 0 deletions examples/fullstack/expo/justaname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { JustaName } from '@justaname.id/sdk';

let justanameInstance: JustaName | null = null;

export const getJustaNameInstance = async () => {
if (!justanameInstance) {
justanameInstance = await JustaName.init({
apiKey: process.env.JUSTANAME_API_KEY,
});
}
return justanameInstance;
};
Loading

0 comments on commit 0d6fd93

Please sign in to comment.