ghost-portal/packages/page-settings/src/useExtensions.ts
2020-04-01 21:31:36 +02:00

118 lines
3.0 KiB
TypeScript

// Copyright 2017-2020 @polkadot/app-settings authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { InjectedExtension, InjectedMetadataKnown, MetadataDef } from '@polkadot/extension-inject/types';
import { useEffect, useState } from 'react';
import { ApiPromise } from '@polkadot/api';
import { useApi } from '@polkadot/react-hooks';
interface ExtensionKnown {
extension: InjectedExtension;
known: InjectedMetadataKnown[];
update: (def: MetadataDef) => Promise<boolean>;
}
interface ExtensionInfo extends ExtensionKnown {
current: InjectedMetadataKnown | null;
}
interface Extensions {
count: number;
extensions?: ExtensionInfo[];
}
type TriggerFn = (counter: number) => void;
let triggerCount = 0;
const triggers = new Map<string, TriggerFn>();
function triggerAll (): void {
[...triggers.values()].forEach((trigger) => trigger(Date.now()));
}
async function getExtensionInfo (extension: InjectedExtension): Promise<ExtensionKnown | null> {
if (!extension.metadata) {
return null;
}
try {
const metadata = extension.metadata;
const known = await metadata.get();
return {
extension,
known,
update: async (def: MetadataDef): Promise<boolean> => {
let result = false;
try {
result = await metadata.provide(def);
triggerAll();
} catch (error) {
// ignore
}
return result;
}
};
} catch (error) {
return null;
}
}
async function getKnown (extensions: InjectedExtension[]): Promise<ExtensionKnown[]> {
const all = await Promise.all(extensions.map(getExtensionInfo));
return all.filter((info): info is ExtensionKnown => !!info);
}
function filterAll (api: ApiPromise, all: ExtensionKnown[]): Extensions {
const extensions = all
.map((info): ExtensionInfo | null => {
const current = info.known.find(({ genesisHash }) => api.genesisHash.eq(genesisHash)) || null;
const isUpgradable = !current || api.runtimeVersion.specVersion.gtn(current.specVersion);
return isUpgradable
? { ...info, current }
: null;
})
.filter((info): info is ExtensionInfo => !!info);
return {
count: extensions.length,
extensions
};
}
export default function useExtensions (): Extensions {
const { api, extensions, isApiReady, isDevelopment } = useApi();
const [all, setAll] = useState<ExtensionKnown[] | undefined>();
const [state, setState] = useState<Extensions>({ count: 0 });
const [trigger, setTrigger] = useState(0);
useEffect((): () => void => {
const myId = `${++triggerCount}-${Date.now()}`;
triggers.set(myId, setTrigger);
return (): void => {
triggers.delete(myId);
};
}, []);
useEffect((): void => {
extensions && getKnown(extensions).then(setAll);
}, [extensions, trigger]);
useEffect((): void => {
isApiReady && !isDevelopment && all && setState(
filterAll(api, all)
);
}, [all, api, isApiReady, isDevelopment]);
return state;
}