- Introduced a new button to sign transactions, allowing users to generate a signature for a predefined raw transaction. - Updated state management to handle the transaction signature and display it in the UI. - Enhanced connection logic to ensure the user's address is retrieved before signing. - Modified the WalletStore to improve transaction status handling during updates.
152 lines
5.4 KiB
TypeScript
152 lines
5.4 KiB
TypeScript
import type { InjectedMetadataKnown } from "@polkadot/extension-inject/types";
|
|
import { VARAN_WALLET_LS_KEY } from "./constants";
|
|
import { generateRequestId } from "./helpers";
|
|
import { WalletStorage } from "./types";
|
|
|
|
type WalletStoreType = {
|
|
accounts: string[];
|
|
injectedMetadataKnown?: InjectedMetadataKnown;
|
|
transactions: {
|
|
id: number;
|
|
signedTx: string;
|
|
status: 'signing' | 'success' | 'failed';
|
|
}[];
|
|
}
|
|
|
|
const defaultStore: WalletStoreType = {
|
|
accounts: [],
|
|
injectedMetadataKnown: undefined,
|
|
transactions: [],
|
|
}
|
|
|
|
export class WalletStore {
|
|
private readonly store: WalletStorage;
|
|
private readonly storeSubscribe: (cb: (ev: StorageEvent) => void) => void;
|
|
private _store: WalletStoreType;
|
|
|
|
constructor() {
|
|
this.store = window.localStorage as WalletStorage;
|
|
this._store = Object.assign(defaultStore, JSON.parse(this.store.getItem(VARAN_WALLET_LS_KEY) || '{}'));
|
|
this.storeSubscribe = (cb): void => {
|
|
window.addEventListener('storage', cb);
|
|
};
|
|
this._setupListeners();
|
|
}
|
|
|
|
private callbacks: {
|
|
[K in keyof WalletStoreType]: ({
|
|
cb: (value: WalletStoreType[K]) => void,
|
|
id: number;
|
|
})[];
|
|
} = {
|
|
accounts: [],
|
|
injectedMetadataKnown: [],
|
|
transactions: [],
|
|
};
|
|
|
|
private _setupListeners(): void {
|
|
let currentState = this._store;
|
|
this.storeSubscribe(ev => {
|
|
ev.stopImmediatePropagation();
|
|
if (ev.key !== VARAN_WALLET_LS_KEY) return;
|
|
|
|
const data = JSON.parse(ev.newValue || '{}') as WalletStoreType;
|
|
const keys = Object.keys(data) as (keyof WalletStoreType)[];
|
|
for (const key of keys) {
|
|
if (key === 'injectedMetadataKnown') continue;
|
|
if (key === 'accounts') {
|
|
const currentAccounts = currentState[key];
|
|
const dataAccounts = data[key];
|
|
if (currentAccounts.length !== dataAccounts.length) {
|
|
currentState = data;
|
|
this._store = data;
|
|
this.dispatch(key);
|
|
continue;
|
|
}
|
|
|
|
let shouldDispatch = false;
|
|
dataAccounts.forEach(account => {
|
|
if (!currentAccounts.includes(account)) {
|
|
shouldDispatch = true;
|
|
}
|
|
});
|
|
|
|
if (shouldDispatch) {
|
|
currentState = data;
|
|
this._store = data;
|
|
this.dispatch(key);
|
|
}
|
|
continue;
|
|
}
|
|
if (key === 'transactions') {
|
|
const currentTransactions = currentState[key];
|
|
const dataTransactions = data[key];
|
|
if (currentTransactions.length !== dataTransactions.length) {
|
|
currentState = data;
|
|
this._store = data;
|
|
this.dispatch(key);
|
|
continue;
|
|
}
|
|
|
|
let shouldDispatch = false;
|
|
dataTransactions.forEach(transaction => {
|
|
const currentTransaction = currentTransactions.find(t => t.id === transaction.id);
|
|
if (currentTransaction && currentTransaction.status !== transaction.status) {
|
|
shouldDispatch = true;
|
|
}
|
|
});
|
|
|
|
if (shouldDispatch) {
|
|
currentState = data;
|
|
this._store = data;
|
|
this.dispatch(key);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private dispatch<Key extends keyof WalletStoreType>(key: Key): void {
|
|
const callbacks = this.callbacks[key];
|
|
if (!callbacks || callbacks.length === 0) return;
|
|
callbacks.forEach(c => c.cb(this._store[key]));
|
|
}
|
|
|
|
get<Key extends keyof WalletStoreType>(key: Key): WalletStoreType[Key] {
|
|
return this._store[key] || defaultStore[key];
|
|
}
|
|
|
|
set<Key extends keyof WalletStoreType>(key: Key, value: WalletStoreType[Key]): void {
|
|
this._store[key] = value;
|
|
this.dispatch(key);
|
|
this.store.setItem(VARAN_WALLET_LS_KEY, JSON.stringify(this._store));
|
|
}
|
|
|
|
remove<Key extends keyof WalletStoreType>(key: Key): void {
|
|
const value = this._store[key];
|
|
if (value) {
|
|
this._store[key] = defaultStore[key];
|
|
}
|
|
this.store.setItem(VARAN_WALLET_LS_KEY, JSON.stringify(this._store));
|
|
}
|
|
|
|
clear(): void {
|
|
this.store.removeItem(VARAN_WALLET_LS_KEY);
|
|
}
|
|
|
|
subscribe<Key extends keyof WalletStoreType>(key: Key, cb: (value: WalletStoreType[Key]) => void): number {
|
|
if (this.callbacks[key] == undefined) {
|
|
this.callbacks[key] = [];
|
|
}
|
|
const id = generateRequestId();
|
|
this.callbacks[key].push({cb, id});
|
|
return id;
|
|
}
|
|
|
|
unsubscribe<Key extends keyof WalletStoreType>(key: Key, id: number): void {
|
|
if (this.callbacks[key] == undefined || this.callbacks[key].length === 0) return;
|
|
const filtered = this.callbacks[key].filter(c => c.id !== id);
|
|
this.callbacks[key] = filtered as typeof this.callbacks[Key];
|
|
}
|
|
} |