update eager connect hook and make app concerned with selected pubkey, refactor token to be stored as object
This commit is contained in:
parent
1e6900d147
commit
84af6177ad
@ -1,14 +1,16 @@
|
|||||||
import { Callout, Button } from '@vegaprotocol/ui-toolkit';
|
import { Callout, Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/react-helpers';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { rest } from '../lib/connectors';
|
import { rest } from '../lib/connectors';
|
||||||
|
import { useVegaWallet, VegaKeyExtended } from '@vegaprotocol/react-helpers';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { Connectors, rest } from '../lib/connectors';
|
||||||
import { LocalStorage } from '@vegaprotocol/storage';
|
import { LocalStorage } from '@vegaprotocol/storage';
|
||||||
|
|
||||||
export function Index() {
|
export function Index() {
|
||||||
// Get keys from vega wallet immediately
|
// Get keys from vega wallet immediately
|
||||||
useEagerConnect();
|
useEagerConnect();
|
||||||
|
|
||||||
const { publicKey, publicKeys, selectPublicKey } = useVegaWallet();
|
const { publicKeys } = useVegaWallet();
|
||||||
|
const { publicKey, onSelect } = useCurrentVegaKey();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="m-24 ">
|
<div className="m-24 ">
|
||||||
@ -31,7 +33,7 @@ export function Index() {
|
|||||||
<select
|
<select
|
||||||
name="change-key"
|
name="change-key"
|
||||||
value={publicKey?.pub}
|
value={publicKey?.pub}
|
||||||
onChange={(e) => selectPublicKey(e.target.value)}
|
onChange={(e) => onSelect(e.target.value)}
|
||||||
>
|
>
|
||||||
{publicKeys.map((pk) => (
|
{publicKeys.map((pk) => (
|
||||||
<option key={pk.pub} value={pk.pub}>
|
<option key={pk.pub} value={pk.pub}>
|
||||||
@ -53,9 +55,53 @@ function useEagerConnect() {
|
|||||||
const { connect } = useVegaWallet();
|
const { connect } = useVegaWallet();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Might be safer to store connector name and eager connect using that
|
const cfg = LocalStorage.getItem('vega_wallet');
|
||||||
if (LocalStorage.getItem('vega_wallet_token')) {
|
const cfgObj = JSON.parse(cfg);
|
||||||
connect(rest);
|
|
||||||
|
// No stored config, user has never connected or manually cleared storage
|
||||||
|
if (!cfgObj || !cfgObj.connector) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const connector = Connectors[cfgObj.connector];
|
||||||
|
|
||||||
|
// Developer hasn't provided this connector
|
||||||
|
if (!connector) {
|
||||||
|
throw new Error(`Connector ${cfgObj?.connector} not configured`);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(Connectors[cfgObj.connector]);
|
||||||
}, [connect]);
|
}, [connect]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useCurrentVegaKey(): {
|
||||||
|
publicKey: VegaKeyExtended | null;
|
||||||
|
onSelect: (pk: string) => void;
|
||||||
|
} {
|
||||||
|
const { publicKeys } = useVegaWallet();
|
||||||
|
const [pk, setPk] = useState<string | null>(() =>
|
||||||
|
LocalStorage.getItem('vega_selected_publickey')
|
||||||
|
);
|
||||||
|
|
||||||
|
const publicKey = useMemo(() => {
|
||||||
|
if (!publicKeys?.length) return null;
|
||||||
|
|
||||||
|
const found = publicKeys.find((x) => x.pub === pk);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, [pk, publicKeys]);
|
||||||
|
|
||||||
|
// on public key change set to localStorage
|
||||||
|
useEffect(() => {
|
||||||
|
LocalStorage.setItem('vega_selected_publickey', pk);
|
||||||
|
}, [pk]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
publicKey,
|
||||||
|
onSelect: setPk,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -17,9 +17,13 @@ export function VegaConnectDialog({
|
|||||||
setDialogOpen,
|
setDialogOpen,
|
||||||
}: VegaConnectDialogProps) {
|
}: VegaConnectDialogProps) {
|
||||||
const { connect } = useVegaWallet();
|
const { connect } = useVegaWallet();
|
||||||
|
|
||||||
|
// Selected connector, we need to show the auth form if the rest connector (which is
|
||||||
|
// currently the only way to connect) is selected.
|
||||||
const [selectedConnector, setSelectedConnector] =
|
const [selectedConnector, setSelectedConnector] =
|
||||||
useState<VegaConnector | null>(null);
|
useState<VegaConnector | null>(null);
|
||||||
|
|
||||||
|
// Connects with the provided connector instance and closes the modal
|
||||||
const connectAndClose = useCallback(
|
const connectAndClose = useCallback(
|
||||||
(connector: VegaConnector) => {
|
(connector: VegaConnector) => {
|
||||||
connect(connector);
|
connect(connector);
|
||||||
@ -28,6 +32,8 @@ export function VegaConnectDialog({
|
|||||||
[connect, setDialogOpen]
|
[connect, setDialogOpen]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Effect to immediately connect if the selected connector is NOT a
|
||||||
|
// rest connector
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
selectedConnector !== null &&
|
selectedConnector !== null &&
|
||||||
|
@ -7,37 +7,58 @@ import {
|
|||||||
import { LocalStorage } from '@vegaprotocol/storage';
|
import { LocalStorage } from '@vegaprotocol/storage';
|
||||||
|
|
||||||
export interface VegaConnector {
|
export interface VegaConnector {
|
||||||
|
/** Connect to wallet and return keys */
|
||||||
connect(): Promise<VegaKey[] | null>;
|
connect(): Promise<VegaKey[] | null>;
|
||||||
|
/** Disconnect from wallet */
|
||||||
disconnect(): Promise<void>;
|
disconnect(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors
|
||||||
|
// need to use local storage, I don't think so...
|
||||||
|
interface RestConnectorConfig {
|
||||||
|
token: string | null;
|
||||||
|
connector: 'rest';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connector for using the Vega Wallet Service rest api, requires authentication to get a session token
|
||||||
|
*/
|
||||||
export class RestConnector implements VegaConnector {
|
export class RestConnector implements VegaConnector {
|
||||||
|
static storageKey = 'vega_wallet';
|
||||||
apiConfig: Configuration;
|
apiConfig: Configuration;
|
||||||
service: DefaultApi;
|
service: DefaultApi;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.apiConfig = createConfiguration({
|
const cfg = this.getConfig();
|
||||||
|
|
||||||
|
// If theres a stored auth token create api config with bearer authMethod
|
||||||
|
this.apiConfig = cfg?.token
|
||||||
|
? createConfiguration({
|
||||||
authMethods: {
|
authMethods: {
|
||||||
bearer: `Bearer ${LocalStorage.getItem('vega_wallet_token')}`,
|
bearer: `Bearer ${cfg.token}`,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
: createConfiguration();
|
||||||
|
|
||||||
this.service = new DefaultApi(this.apiConfig);
|
this.service = new DefaultApi(this.apiConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(params: { wallet: string; passphrase: string }) {
|
async authenticate(params: { wallet: string; passphrase: string }) {
|
||||||
try {
|
try {
|
||||||
const tokenRes = await this.service.authTokenPost(params);
|
const res = await this.service.authTokenPost(params);
|
||||||
|
|
||||||
// Renew DefaultApi now we have the token
|
// Renew service instance with default bearer authMethod now that we have the token
|
||||||
this.service = new DefaultApi(
|
this.service = new DefaultApi(
|
||||||
createConfiguration({
|
createConfiguration({
|
||||||
authMethods: {
|
authMethods: {
|
||||||
bearer: `Bearer ${tokenRes.token}`,
|
bearer: `Bearer ${res.token}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
LocalStorage.setItem('vega_wallet_token', tokenRes.token);
|
// Store the token, and other things for later
|
||||||
|
this.setConfig({ connector: 'rest', token: res.token });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -52,7 +73,7 @@ export class RestConnector implements VegaConnector {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
// keysGet failed, its likely that the session has expired so remove the token from storage
|
// keysGet failed, its likely that the session has expired so remove the token from storage
|
||||||
LocalStorage.removeItem('vega_wallet_token');
|
this.clearConfig();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,12 +84,38 @@ export class RestConnector implements VegaConnector {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
// Always clear the tokens
|
// Always clear config, if authTokenDelete fails the user still tried to
|
||||||
LocalStorage.removeItem('vega_wallet_token');
|
// connect so clear the config (and containing token) from storage
|
||||||
|
this.clearConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setConfig(cfg: RestConnectorConfig) {
|
||||||
|
LocalStorage.setItem(RestConnector.storageKey, JSON.stringify(cfg));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getConfig(): RestConnectorConfig | null {
|
||||||
|
const cfg = LocalStorage.getItem(RestConnector.storageKey);
|
||||||
|
if (cfg) {
|
||||||
|
try {
|
||||||
|
const obj = JSON.parse(cfg);
|
||||||
|
return obj;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearConfig() {
|
||||||
|
LocalStorage.removeItem(RestConnector.storageKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy injected connector that we may use when browser wallet is implemented
|
||||||
|
*/
|
||||||
export class InjectedConnector implements VegaConnector {
|
export class InjectedConnector implements VegaConnector {
|
||||||
async connect() {
|
async connect() {
|
||||||
return [
|
return [
|
||||||
|
@ -7,9 +7,6 @@ export interface VegaKeyExtended extends VegaKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface VegaWalletContextShape {
|
export interface VegaWalletContextShape {
|
||||||
/** The current select public key */
|
|
||||||
publicKey: VegaKeyExtended | null;
|
|
||||||
|
|
||||||
/** Public keys stored in users wallet */
|
/** Public keys stored in users wallet */
|
||||||
publicKeys: VegaKeyExtended[] | null;
|
publicKeys: VegaKeyExtended[] | null;
|
||||||
|
|
||||||
@ -19,9 +16,6 @@ export interface VegaWalletContextShape {
|
|||||||
/** Disconnects from the connector and clears public key state */
|
/** Disconnects from the connector and clears public key state */
|
||||||
disconnect: () => Promise<void>;
|
disconnect: () => Promise<void>;
|
||||||
|
|
||||||
/** Sets the current selected public key */
|
|
||||||
selectPublicKey: (publicKey: string) => void;
|
|
||||||
|
|
||||||
/** Reference to the connector */
|
/** Reference to the connector */
|
||||||
connector: VegaConnector | null;
|
connector: VegaConnector | null;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ interface VegaWalletProviderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
||||||
const [publicKey, setPublicKey] = useState<VegaKeyExtended | null>(null);
|
|
||||||
const [publicKeys, setPublicKeys] = useState<VegaKeyExtended[] | null>(null);
|
const [publicKeys, setPublicKeys] = useState<VegaKeyExtended[] | null>(null);
|
||||||
const connector = useRef<VegaConnector | null>(null);
|
const connector = useRef<VegaConnector | null>(null);
|
||||||
|
|
||||||
@ -16,6 +15,12 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
connector.current = c;
|
connector.current = c;
|
||||||
try {
|
try {
|
||||||
const res = await connector.current.connect();
|
const res = await connector.current.connect();
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
console.log('connect failed', res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const publicKeysWithName = res.map((pk) => {
|
const publicKeysWithName = res.map((pk) => {
|
||||||
const nameMeta = pk.meta?.find((m) => m.key === 'name');
|
const nameMeta = pk.meta?.find((m) => m.key === 'name');
|
||||||
return {
|
return {
|
||||||
@ -24,7 +29,6 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
setPublicKeys(publicKeysWithName);
|
setPublicKeys(publicKeysWithName);
|
||||||
setPublicKey(publicKeysWithName[0]);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@ -34,40 +38,20 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
try {
|
try {
|
||||||
await connector.current?.disconnect();
|
await connector.current?.disconnect();
|
||||||
setPublicKeys(null);
|
setPublicKeys(null);
|
||||||
setPublicKey(null);
|
|
||||||
connector.current = null;
|
connector.current = null;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const selectPublicKey = useCallback(
|
|
||||||
(key: string) => {
|
|
||||||
if (!publicKeys || !publicKeys.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedKey = publicKeys.find((k) => k.pub === key);
|
|
||||||
|
|
||||||
if (!selectedKey) {
|
|
||||||
throw new Error('Public key doesnt exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
setPublicKey(selectedKey);
|
|
||||||
},
|
|
||||||
[publicKeys]
|
|
||||||
);
|
|
||||||
|
|
||||||
const contextValue = useMemo<VegaWalletContextShape>(() => {
|
const contextValue = useMemo<VegaWalletContextShape>(() => {
|
||||||
return {
|
return {
|
||||||
publicKey,
|
|
||||||
publicKeys,
|
publicKeys,
|
||||||
selectPublicKey,
|
|
||||||
connect,
|
connect,
|
||||||
disconnect,
|
disconnect,
|
||||||
connector: connector.current,
|
connector: connector.current,
|
||||||
};
|
};
|
||||||
}, [publicKey, publicKeys, selectPublicKey, connect, disconnect, connector]);
|
}, [publicKeys, connect, disconnect, connector]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VegaWalletContext.Provider value={contextValue}>
|
<VegaWalletContext.Provider value={contextValue}>
|
||||||
|
@ -20,6 +20,7 @@ export function RestConnectorForm({
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<FormFields>({
|
} = useForm<FormFields>({
|
||||||
|
// TODO: Remove default values
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
wallet: 'test6',
|
wallet: 'test6',
|
||||||
passphrase: '123',
|
passphrase: '123',
|
||||||
|
Loading…
Reference in New Issue
Block a user