Allow for unsubscribe on account subscriptions (#1067)

* Allow for unsubscribe on account subscriptions

* CHANGELOG

* Adjust

* Adjust
This commit is contained in:
Jaco 2022-07-14 20:15:20 +02:00 committed by GitHub
parent 2d09932ca1
commit a7f27ac4b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 19 deletions

View File

@ -1,5 +1,18 @@
# CHANGELOG
## master
Contributed:
- Account selection/management for authorized sites (Thanks to https://github.com/Tbaut)
- UI icon & cursor adjustments for authorization (Thanks to https://github.com/roiLeo)
Changes:
- Allow for unsubscribe handling on account subscriptions
- Add error handling for user-supplied account callbacks
## 0.44.1 Jun 6, 2022
**Important** CHANGELOG entries are a rollup of details since last publish to the stores

View File

@ -1,12 +1,13 @@
// Copyright 2019-2022 @polkadot/extension authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Subscription } from 'rxjs';
import type { InjectedAccount, InjectedMetadataKnown, MetadataDef, ProviderMeta } from '@polkadot/extension-inject/types';
import type { KeyringPair } from '@polkadot/keyring/types';
import type { JsonRpcResponse } from '@polkadot/rpc-provider/types';
import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
import type { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
import type { MessageTypes, RequestAccountList, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestTypes, ResponseRpcListProviders, ResponseSigning, ResponseTypes, SubscriptionMessageTypes } from '../types';
import type { MessageTypes, RequestAccountList, RequestAccountUnsubscribe, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestTypes, ResponseRpcListProviders, ResponseSigning, ResponseTypes, SubscriptionMessageTypes } from '../types';
import { PHISHING_PAGE_REDIRECT } from '@polkadot/extension-base/defaults';
import { canDerive } from '@polkadot/extension-base/utils';
@ -21,6 +22,11 @@ import { withErrorLog } from './helpers';
import State, { AuthResponse } from './State';
import { createSubscription, unsubscribe } from './subscriptions';
interface AccountSub {
subscription: Subscription;
url: string;
}
function transformAccounts (accounts: SubjectInfo, anyType = false): InjectedAccount[] {
return Object
.values(accounts)
@ -36,6 +42,8 @@ function transformAccounts (accounts: SubjectInfo, anyType = false): InjectedAcc
}
export default class Tabs {
readonly #accountSubs: Record<string, AccountSub> = {};
readonly #state: State;
constructor (state: State) {
@ -65,21 +73,37 @@ export default class Tabs {
return this.filterForAuthorizedAccounts(transformedAccounts, url);
}
private accountsSubscribeAuthorized (url: string, id: string, port: chrome.runtime.Port): boolean {
const cb = createSubscription<'pub(accounts.subscribeAuthorized)'>(id, port);
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo): void => {
const transformedAccounts = transformAccounts(accounts);
private accountsSubscribeAuthorized (url: string, id: string, port: chrome.runtime.Port): string {
const cb = createSubscription<'pub(accounts.subscribe)'>(id, port);
return cb(
this.filterForAuthorizedAccounts(transformedAccounts, url)
);
});
this.#accountSubs[id] = {
subscription: accountsObservable.subject.subscribe((accounts: SubjectInfo): void => {
const transformedAccounts = transformAccounts(accounts);
cb(this.filterForAuthorizedAccounts(transformedAccounts, url));
}),
url
};
port.onDisconnect.addListener((): void => {
unsubscribe(id);
subscription.unsubscribe();
this.accountsUnsubscribe(url, { id });
});
return id;
}
private accountsUnsubscribe (url: string, { id }: RequestAccountUnsubscribe): boolean {
const sub = this.#accountSubs[id];
if (!sub || sub.url !== url) {
return false;
}
delete this.#accountSubs[id];
unsubscribe(id);
sub.subscription.unsubscribe();
return true;
}
@ -199,12 +223,15 @@ export default class Tabs {
case 'pub(authorize.tab)':
return this.authorize(url, request as RequestAuthorizeTab);
case 'pub(accounts.listAuthorized)':
case 'pub(accounts.list)':
return this.accountsListAuthorized(url, request as RequestAccountList);
case 'pub(accounts.subscribeAuthorized)':
case 'pub(accounts.subscribe)':
return this.accountsSubscribeAuthorized(url, id, port);
case 'pub(accounts.unsubscribe)':
return this.accountsUnsubscribe(url, request as RequestAccountUnsubscribe);
case 'pub(bytes.sign)':
return this.bytesSign(url, request as SignerPayloadRaw);

View File

@ -116,8 +116,9 @@ export interface RequestSignatures {
'pri(signing.requests)': [RequestSigningSubscribe, boolean, SigningRequest[]];
'pri(window.open)': [AllowedPath, boolean];
// public/external requests, i.e. from a page
'pub(accounts.listAuthorized)': [RequestAccountList, InjectedAccount[]];
'pub(accounts.subscribeAuthorized)': [RequestAccountSubscribe, boolean, InjectedAccount[]];
'pub(accounts.list)': [RequestAccountList, InjectedAccount[]];
'pub(accounts.subscribe)': [RequestAccountSubscribe, string, InjectedAccount[]];
'pub(accounts.unsubscribe)': [RequestAccountUnsubscribe, boolean];
'pub(authorize.tab)': [RequestAuthorizeTab, Promise<AuthResponse>];
'pub(bytes.sign)': [SignerPayloadRaw, ResponseSigning];
'pub(extrinsic.sign)': [SignerPayloadJSON, ResponseSigning];
@ -260,6 +261,10 @@ export interface RequestAccountList {
export type RequestAccountSubscribe = null;
export interface RequestAccountUnsubscribe {
id: string;
}
export interface RequestRpcSend {
method: string;
params: unknown[];

View File

@ -13,15 +13,21 @@ export default class Accounts implements InjectedAccounts {
}
public get (anyType?: boolean): Promise<InjectedAccount[]> {
return sendRequest('pub(accounts.listAuthorized)', { anyType });
return sendRequest('pub(accounts.list)', { anyType });
}
public subscribe (cb: (accounts: InjectedAccount[]) => unknown): Unsubcall {
sendRequest('pub(accounts.subscribeAuthorized)', null, cb)
.catch((error: Error) => console.error(error));
let id: string | null = null;
sendRequest('pub(accounts.subscribe)', null, cb)
.then((subId): void => {
id = subId;
})
.catch(console.error);
return (): void => {
// FIXME we need the ability to unsubscribe
id && sendRequest('pub(accounts.unsubscribe)', { id })
.catch(console.error);
};
}
}