Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/dapp ethereum connection persistence #2708

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,24 @@

<script lang="ts">
import { Component, Emit, Mixins, Prop } from 'vue-property-decorator';
import { mapState } from 'vuex';

import ActionButton from '@/components/ActionButton.vue';
import RaidenDialog from '@/components/dialogs/RaidenDialog.vue';
import Spinner from '@/components/icons/Spinner.vue';
import NavigationMixin from '@/mixins/navigation-mixin';
import type { Settings } from '@/types';

@Component({
components: {
RaidenDialog,
ActionButton,
Spinner,
},
computed: {
...mapState(['settings']),
},
})
export default class UploadStateDialog extends Mixins(NavigationMixin) {
dragCount = 0;
activeDropzone = false;
dropzoneErrorMessage = false;
uploadingStateProgress = false;
settings!: Settings;

@Prop({ required: true, type: Boolean, default: false })
visible!: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { providers } from 'ethers';
import { EthereumConnection } from './types';

export class DirectRpcProvider extends EthereumConnection {
public static readonly connection_name = 'direct_rpc_provider';
public static readonly connectionName = 'direct_rpc_provider';
public static readonly isAvailable = true;
public readonly provider: providers.JsonRpcProvider;
public readonly account: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { providers } from 'ethers';
import { EthereumConnection } from './types';

export class InjectedProvider extends EthereumConnection {
public static readonly connection_name = 'injected_provider';
public static readonly connectionName = 'injected_provider';
public readonly provider: providers.JsonRpcProvider;
public readonly account = 0; // Refers to the currently selected account in the wallet.

Expand Down
11 changes: 9 additions & 2 deletions raiden-dapp/src/services/ethereum-connection/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import type { providers } from 'ethers';

// The type evaluation for static class members works slightly different for
// the moment. Thereby it is not possible to have any better type restrictions
// here. Though having the named type here in place will allows us to adopt it
// later and being explicit about it at all places.
export type EthereumConnectionOptions = any; // eslint-disable-line @typescript-eslint/no-explicit-any

// TOOD: watch-out when `static abstract` becomes possible in TypeScript
export abstract class EthereumConnection {
static connection_name: string;
static connectionName: string;
static isAvailable = false;
static connect: (options?: any) => Promise<EthereumConnection>; // eslint-disable-line @typescript-eslint/no-explicit-any
static connect: (options: EthereumConnectionOptions) => Promise<EthereumConnection>;
abstract provider: providers.JsonRpcProvider;
abstract account: string | number;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { providers } from 'ethers';
import { EthereumConnection } from './types';

export class WalletConnect extends EthereumConnection {
public static readonly connection_name = 'wallet_connect';
public static readonly connectionName = 'wallet_connect';
public static readonly isAvailable = true;
public readonly provider: providers.JsonRpcProvider;
public readonly account = 0; // Refers to the currently selected account in the wallet.
Expand Down
24 changes: 5 additions & 19 deletions raiden-dapp/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import { emptyTokenModel, PlaceHolderNetwork } from '@/model/types';
import { notifications } from '@/store/notifications';
import type { UserDepositContractState } from '@/store/user-deposit-contract';
import { userDepositContract } from '@/store/user-deposit-contract';
import type { RootState, Settings, Tokens, Transfers } from '@/types';
import { userSettings, userSettingsLocalStorage } from '@/store/user-settings';
import type { RootState, Tokens, Transfers } from '@/types';

import type { NotificationsState } from './notifications/types';

Expand All @@ -38,9 +39,6 @@ const _defaultState: RootState = {
presences: {},
network: PlaceHolderNetwork,
stateBackup: '',
settings: {
useRaidenAccount: true,
},
config: {},
disclaimerAccepted: false,
stateBackupReminderDateMs: 0,
Expand Down Expand Up @@ -68,12 +66,6 @@ const hasNonZeroBalance = (a: Token, b: Token) =>
b.balance &&
(!(a.balance as BigNumber).isZero() || !(b.balance as BigNumber).isZero());

const settingsLocalStorage = new VuexPersistence<RootState>({
reducer: (state) => ({ settings: state.settings }),
filter: (mutation) => mutation.type == 'updateSettings',
key: 'raiden_dapp_settings',
});

const disclaimerLocalStorage = new VuexPersistence<RootState>({
reducer: (state) => ({
disclaimerAccepted: state.persistDisclaimerAcceptance ? state.disclaimerAccepted : false,
Expand Down Expand Up @@ -133,11 +125,10 @@ const store: StoreOptions<CombinedStoreState> = {
},
reset(state: RootState) {
// Preserve settings and backup when resetting state
const { settings, disclaimerAccepted, stateBackup, stateBackupReminderDateMs } = state;
const { disclaimerAccepted, stateBackup, stateBackupReminderDateMs } = state;

Object.assign(state, {
...defaultState(),
settings,
disclaimerAccepted,
stateBackup,
stateBackupReminderDateMs,
Expand All @@ -152,9 +143,6 @@ const store: StoreOptions<CombinedStoreState> = {
clearBackupState(state: RootState) {
state.stateBackup = '';
},
updateSettings(state: RootState, settings: Settings) {
state.settings = settings;
},
updateConfig(state: RootState, config: Partial<RaidenConfig>) {
state.config = config;
},
Expand Down Expand Up @@ -265,9 +253,6 @@ const store: StoreOptions<CombinedStoreState> = {
transfer: (state: RootState) => (paymentId: BigNumber) => {
return Object.values(state.transfers).find((transfer) => transfer.paymentId.eq(paymentId));
},
usingRaidenAccount: (state: RootState): boolean => {
return !!state.settings.useRaidenAccount;
},
versionUpdateAvailable: (state: RootState): boolean => {
const { activeVersion, availableVersion } = state.versionInfo;
return (
Expand All @@ -278,13 +263,14 @@ const store: StoreOptions<CombinedStoreState> = {
},
},
plugins: [
settingsLocalStorage.plugin,
userSettingsLocalStorage.plugin,
disclaimerLocalStorage.plugin,
backupReminderLocalStorage.plugin,
],
modules: {
notifications,
userDepositContract,
userSettings,
},
};

Expand Down
20 changes: 20 additions & 0 deletions raiden-dapp/src/store/user-settings/getters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { GetterTree } from 'vuex';

import type { EthereumConnectionOptions } from '@/services/ethereum-connection';
import type { RootState } from '@/types';

import type { UserSettingsState } from './types';

type Getters = {
getEthereumConnectionOptions(
state: UserSettingsState,
): (connectionName: string) => EthereumConnectionOptions;
};

export const getters: GetterTree<UserSettingsState, RootState> & Getters = {
// This getter is relevant to have a standardized default value that works for
// all component which interact with such connection options.
getEthereumConnectionOptions: (state) => (connectionName) => {
return state.ethereumConnectionOptions[connectionName] ?? {};
},
};
21 changes: 21 additions & 0 deletions raiden-dapp/src/store/user-settings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Module } from 'vuex';
import { VuexPersistence } from 'vuex-persist';

import type { RootState } from '@/types';

import { mutations } from './mutations';
import state from './state';
import type { UserSettingsState } from './types';

export const userSettings: Module<UserSettingsState, RootState> = {
namespaced: true,
state,
mutations,
};

export const userSettingsLocalStorage = new VuexPersistence<RootState>({
key: 'raiden_dapp_settings',
modules: ['userSettings'],
});

export * from './types';
23 changes: 23 additions & 0 deletions raiden-dapp/src/store/user-settings/mutations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { MutationTree } from 'vuex';

import type { EthereumConnectionOptions } from '@/services/ethereum-connection';

import type { UserSettingsState } from './types';

export const mutations: MutationTree<UserSettingsState> = {
enableRaidenAccount(state) {
state.useRaidenAccount = true;
},
disableRaidenAccount(state) {
state.useRaidenAccount = false;
},
saveEthereumConnectionOptions(
state,
payload: {
connectionName: string;
connectionOptions: EthereumConnectionOptions;
},
) {
state.ethereumConnectionOptions[payload.connectionName] = payload.connectionOptions;
},
};
10 changes: 10 additions & 0 deletions raiden-dapp/src/store/user-settings/state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { UserSettingsState } from './types';

export const defaultState = (): UserSettingsState => ({
useRaidenAccount: true,
ethereumConnectionOptions: {},
});

const state: UserSettingsState = defaultState();

export default state;
6 changes: 6 additions & 0 deletions raiden-dapp/src/store/user-settings/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { EthereumConnectionOptions } from '@/services/ethereum-connection';

export interface UserSettingsState {
useRaidenAccount: boolean;
ethereumConnectionOptions: { [connectionName: string]: EthereumConnectionOptions };
}
2 changes: 0 additions & 2 deletions raiden-dapp/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type RaidenService from '@/services/raiden-service';
export type Tokens = { [token: string]: Token };
export type Transfers = { [key: string]: RaidenTransfer };
export type ChannelAction = 'close' | 'deposit' | 'withdraw' | 'settle';
export type Settings = { [setting: string]: boolean | number | string };

export interface VersionInfo {
activeVersion: string;
Expand All @@ -31,7 +30,6 @@ export interface RootState {
presences: Presences;
transfers: Transfers;
stateBackup: string;
settings: Settings;
config: Partial<RaidenConfig>;
disclaimerAccepted: boolean;
stateBackupReminderDateMs: number;
Expand Down
10 changes: 6 additions & 4 deletions raiden-dapp/src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import type { Location } from 'vue-router';
import { mapGetters, mapState } from 'vuex';
import { createNamespacedHelpers, mapGetters, mapState } from 'vuex';

import ActionButton from '@/components/ActionButton.vue';
import ConnectionPendingDialog from '@/components/dialogs/ConnectionPendingDialog.vue';
Expand All @@ -75,7 +75,8 @@ import {
InjectedProvider,
WalletConnect,
} from '@/services/ethereum-connection';
import type { Settings } from '@/types';

const { mapState: mapStateUserSettings } = createNamespacedHelpers('userSettings');

function mapRaidenServiceErrorToErrorCode(error: Error): ErrorCode {
if (error.message && error.message.includes('No deploy info provided')) {
Expand All @@ -90,6 +91,7 @@ function mapRaidenServiceErrorToErrorCode(error: Error): ErrorCode {
@Component({
computed: {
...mapState(['isConnected', 'stateBackup', 'settings']),
...mapStateUserSettings(['useRaidenAccount']),
...mapGetters(['tokens']),
},
components: {
Expand All @@ -103,7 +105,7 @@ export default class Home extends Vue {
connecting = false;
connectionError: ErrorCode | null = null;
stateBackup!: string;
settings!: Settings;
useRaidenAccount!: boolean;

get navigationTarget(): Location {
const redirectTo = this.$route.query.redirectTo as string;
Expand Down Expand Up @@ -131,7 +133,7 @@ export default class Home extends Vue {

const stateBackup = this.stateBackup;
const configuration = await ConfigProvider.configuration();
const useRaidenAccount = this.settings.useRaidenAccount ? true : undefined;
const useRaidenAccount = this.useRaidenAccount ? true : undefined;
const ethereumConnection = await this.getEthereumConnection(configuration);

// TODO: This will become removed when we have the connection manager.
Expand Down
20 changes: 13 additions & 7 deletions raiden-dapp/src/views/account/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-switch v-model="settings.useRaidenAccount" @change="toggleMainAccount" />
<v-switch v-model="useRaidenAccount" @change="toggleRaidenAccountUsage" />
</v-list-item-action>
</v-list-item>
</v-list>
Expand All @@ -20,18 +20,24 @@

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { mapState } from 'vuex';
import { createNamespacedHelpers } from 'vuex';

import type { Settings } from '@/types';
const { mapState, mapMutations } = createNamespacedHelpers('userSettings');

@Component({
computed: { ...mapState(['settings']) },
computed: {
...mapState(['useRaidenAccount']),
...mapMutations(['enableRaidenAccount', 'disableRaidenAccount']),
},
})
export default class RaidenSettings extends Vue {
settings!: Settings;
useRaidenAccount!: boolean;

toggleMainAccount() {
this.$store.commit('updateSettings', this.settings);
enableRaidenAccount!: () => void;
disableRaidenAccount!: () => void;

toggleRaidenAccountUsage(value: boolean): void {
value ? this.enableRaidenAccount() : this.disableRaidenAccount();
}
}
</script>
Expand Down
7 changes: 0 additions & 7 deletions raiden-dapp/tests/unit/store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,6 @@ describe('store', () => {
});
});

test('usingRaidenAccount getter reflect settings state property', () => {
[true, false].forEach((useRaidenAccount) => {
store.commit('updateSettings', { useRaidenAccount });
expect(store.getters.usingRaidenAccount).toEqual(useRaidenAccount);
});
});

test('version update is available if available version is higher than active one', () => {
expect(store.getters.versionUpdateAvailable).toBe(false);

Expand Down
19 changes: 19 additions & 0 deletions raiden-dapp/tests/unit/store/user-settings/getters.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getters } from '@/store/user-settings/getters';
import { defaultState } from '@/store/user-settings/state';

describe('user settings store getters', () => {
test('get empty options for etherum connections per default', () => {
const state = defaultState();

expect(getters.getEthereumConnectionOptions(state)('test_connection')).toMatchObject({});
});

test('get saved options for etherum connections', () => {
const state = defaultState();
state.ethereumConnectionOptions['test_connection'] = { option: 'test' };

expect(getters.getEthereumConnectionOptions(state)('test_connection')).toMatchObject({
option: 'test',
});
});
});
Loading