Skip to content

Commit

Permalink
fix: extract data persistence from authorization fetcher in React Nat…
Browse files Browse the repository at this point in the history
…ive example app
  • Loading branch information
steveluscher committed Jul 28, 2022
1 parent 1bbc743 commit 8169e18
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 64 deletions.
57 changes: 54 additions & 3 deletions examples/example-react-native-app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import {ConnectionProvider} from '@solana/wallet-adapter-react';
import {clusterApiUrl} from '@solana/web3.js';
import {clusterApiUrl, PublicKey, PublicKeyInitData} from '@solana/web3.js';
import React, {Suspense} from 'react';
import {ActivityIndicator, SafeAreaView, StyleSheet, View} from 'react-native';
import {
ActivityIndicator,
AppState,
SafeAreaView,
StyleSheet,
View,
} from 'react-native';
import {Provider as PaperProvider} from 'react-native-paper';
import {Cache, SWRConfig} from 'swr';

import SnackbarProvider from './components/SnackbarProvider';
import MainScreen from './screens/MainScreen';

const DEVNET_ENDPOINT = /*#__PURE__*/ clusterApiUrl('devnet');

function cacheReviver(key: string, value: any) {
if (key === 'publicKey') {
return new PublicKey(value as PublicKeyInitData);
} else {
return value;
}
}

const STORAGE_KEY = 'app-cache';
let initialCacheFetchPromise: Promise<void>;
let initialCacheFetchResult: any;
function asyncStorageProvider() {
if (initialCacheFetchPromise == null) {
initialCacheFetchPromise = AsyncStorage.getItem(STORAGE_KEY).then(
result => {
initialCacheFetchResult = result;
},
);
throw initialCacheFetchPromise;
}
let storedAppCache;
try {
storedAppCache = JSON.parse(initialCacheFetchResult, cacheReviver);
} catch {}
const map = new Map(storedAppCache || []);
initialCacheFetchResult = undefined;
function persistCache() {
const appCache = JSON.stringify(Array.from(map.entries()));
AsyncStorage.setItem(STORAGE_KEY, appCache);
}
AppState.addEventListener('change', state => {
if (state !== 'active') {
persistCache();
}
});
AppState.addEventListener('memoryWarning', () => {
persistCache();
});
return map as Cache<any>;
}

export default function App() {
return (
<ConnectionProvider endpoint={DEVNET_ENDPOINT}>
Expand All @@ -24,7 +73,9 @@ export default function App() {
/>
</View>
}>
<MainScreen />
<SWRConfig value={{provider: asyncStorageProvider}}>
<MainScreen />
</SWRConfig>
</Suspense>
</SnackbarProvider>
</PaperProvider>
Expand Down
77 changes: 16 additions & 61 deletions examples/example-react-native-app/utils/useAuthorization.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import {PublicKey} from '@solana/web3.js';
import {
AuthorizationResult,
AuthorizeAPI,
AuthToken,
Base64EncodedAddress,
DeauthorizeAPI,
ReauthorizeAPI,
Expand All @@ -13,45 +13,23 @@ import useSWR from 'swr';

type Account = Readonly<{
address: Base64EncodedAddress;
authToken: AuthToken;
publicKey: PublicKey;
}>;

const STORAGE_KEY = 'cachedAuthorization';

function getDataFromAuthorizationResult(
authorizationResult: AuthorizationResult,
) {
return {
account: getAccountFromAuthorizationResult(authorizationResult),
authorization: authorizationResult,
};
}

function getAccountFromAuthorizationResult(
authorizationResult: AuthorizationResult,
): Account {
const address = authorizationResult.addresses[0]; // TODO(#44): support multiple addresses
return {
address,
authToken: authorizationResult.auth_token,
publicKey: getPublicKeyFromAddress(address),
};
}

async function authorizationFetcher(storageKey: string) {
try {
const serializedValue = await AsyncStorage.getItem(storageKey);
if (!serializedValue) {
return null;
}
const authorization = JSON.parse(serializedValue) as AuthorizationResult;
return getDataFromAuthorizationResult(authorization);
} catch {
// Presume the data in storage is corrupt and erase it.
await AsyncStorage.removeItem(STORAGE_KEY);
return null;
}
}

function getPublicKeyFromAddress(address: Base64EncodedAddress): PublicKey {
const publicKeyByteArray = toUint8Array(address);
return new PublicKey(publicKeyByteArray);
Expand All @@ -62,60 +40,37 @@ export const APP_IDENTITY = {
};

export default function useAuthorization() {
const {data, mutate} = useSWR(STORAGE_KEY, authorizationFetcher, {
suspense: true,
});
const setAuthorization = useCallback(
(authorizationResult: AuthorizationResult | null) => {
mutate(
async () => {
if (authorizationResult) {
await AsyncStorage.setItem(
STORAGE_KEY,
JSON.stringify(authorizationResult),
);
return getDataFromAuthorizationResult(authorizationResult);
} else {
await AsyncStorage.removeItem(STORAGE_KEY);
return null;
}
},
{
optimisticData: authorizationResult
? getDataFromAuthorizationResult(authorizationResult)
: null,
},
);
},
[mutate],
const {data: account, mutate} = useSWR<Account | null | undefined>(
STORAGE_KEY,
);
const authorizeSession = useCallback(
async (wallet: AuthorizeAPI & ReauthorizeAPI) => {
const authorizationResult = await (data
const authorizationResult = await (account
? wallet.reauthorize({
auth_token: data.authorization.auth_token,
auth_token: account.authToken,
})
: wallet.authorize({
cluster: 'devnet',
identity: APP_IDENTITY,
}));
setAuthorization(authorizationResult);
return getAccountFromAuthorizationResult(authorizationResult);
return await mutate(
getAccountFromAuthorizationResult(authorizationResult),
);
},
[data, setAuthorization],
[account, mutate],
);
const deauthorizeSession = useCallback(
async (wallet: DeauthorizeAPI) => {
if (data?.authorization.auth_token == null) {
if (account?.authToken == null) {
return;
}
await wallet.deauthorize({auth_token: data.authorization.auth_token});
setAuthorization(null);
await wallet.deauthorize({auth_token: account?.authToken});
mutate(null);
},
[data?.authorization.auth_token, setAuthorization],
[account, mutate],
);
return {
account: data?.account ?? null,
account: account ?? null,
authorizeSession,
deauthorizeSession,
};
Expand Down

0 comments on commit 8169e18

Please sign in to comment.