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

Add links to bridge transfer modal #1362

Merged
merged 13 commits into from
Mar 28, 2024
Merged
18 changes: 10 additions & 8 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -573,14 +573,16 @@ i.icon-divider {
}

@include desktop {
.app-main {
.app-menu {
&:not(.collapsed) {
position: relative;
}
&.collapsed {
& + .app-body {
margin-left: 74px;
.app-main--swap {
&.app-main {
.app-menu {
&:not(.collapsed) {
position: relative;
}
&.collapsed {
& + .app-body {
margin-left: 74px;
}
}
}
}
Expand Down
19 changes: 10 additions & 9 deletions src/components/App/Settings/Node/NodeInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,15 @@ const stripEndingSlash = (str: string): string => (str.charAt(str.length - 1) ==
},
})
export default class NodeInfo extends Mixins(TranslationMixin) {
@Prop({ default: () => {}, type: Function }) handleBack!: FnWithoutArgs;
@Prop({ default: () => {}, type: Function }) handleNode!: (node: any, isNewNode: boolean) => void;
@Prop({ default: () => {}, type: Function }) removeNode!: (node: any) => void;
@Prop({ default: () => ({}), type: Object }) node!: Node;
@Prop({ default: false, type: Boolean }) existing!: boolean;
@Prop({ default: false, type: Boolean }) removable!: boolean;
@Prop({ default: false, type: Boolean }) connected!: boolean;
@Prop({ default: false, type: Boolean }) showTutorial!: boolean;
@Prop({ default: () => {}, type: Function }) readonly handleBack!: FnWithoutArgs;
@Prop({ default: () => {}, type: Function }) readonly handleNode!: (node: any, isNewNode: boolean) => void;
@Prop({ default: () => {}, type: Function }) readonly removeNode!: (node: any) => void;
@Prop({ default: () => ({}), type: Object }) readonly node!: Node;
@Prop({ default: false, type: Boolean }) readonly existing!: boolean;
@Prop({ default: false, type: Boolean }) readonly removable!: boolean;
@Prop({ default: false, type: Boolean }) readonly connected!: boolean;
@Prop({ default: false, type: Boolean }) readonly showTutorial!: boolean;
@Prop({ default: false, type: Boolean }) readonly disabled!: boolean;
@Prop({ default: '', type: String }) readonly nodeAddressConnecting!: string;

@Ref('nodeNameInput') private readonly nodeNameInput!: HTMLInputElement;
Expand Down Expand Up @@ -172,7 +173,7 @@ export default class NodeInfo extends Mixins(TranslationMixin) {
}

get buttonDisabled(): boolean {
return this.connected && !this.nodeDataChanged;
return this.disabled || (this.connected && !this.nodeDataChanged);
}

get buttonType(): string {
Expand Down
9 changes: 7 additions & 2 deletions src/components/App/Settings/Node/SelectNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
:key="node.address"
:label="node.address"
:value="node.address"
:disabled="node.address === nodeAddressConnecting"
:disabled="disabled || isConnecting(node.address)"
size="medium"
class="select-node-list__item s-flex"
>
Expand All @@ -30,7 +30,7 @@
icon="arrows-swap-90-24"
@click.stop="handleNode(node)"
/>
<s-icon v-else-if="node.address === nodeAddressConnecting" name="el-icon-loading" />
<s-icon v-else-if="isConnecting(node.address)" name="el-icon-loading" />
</div>
<s-button
class="select-node-details"
Expand Down Expand Up @@ -63,6 +63,7 @@ export default class SelectNode extends Mixins(TranslationMixin) {
@Prop({ default: () => {}, type: Function }) readonly handleNode!: (node?: Node) => void;
@Prop({ default: () => {}, type: Function }) readonly viewNode!: (node?: Node) => void;
@Prop({ default: '', type: String }) readonly nodeAddressConnecting!: string;
@Prop({ default: false, type: Boolean }) readonly disabled!: boolean;

@ModelSync('value', 'input', { type: String })
readonly currentAddressValue!: string;
Expand All @@ -75,6 +76,10 @@ export default class SelectNode extends Mixins(TranslationMixin) {
return `${location.name} ${flag}`;
}

isConnecting(address: string) {
return address === this.nodeAddressConnecting;
}

getTitle(node: Node) {
const { name, chain } = node;

Expand Down
6 changes: 6 additions & 0 deletions src/components/App/Settings/Node/SelectNodeDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
:nodes="connection.nodeList"
:handle-node="handleNode"
:view-node="navigateToNodeInfo"
:disabled="!connectionAllowance"
/>
<node-info
v-else
Expand All @@ -23,6 +24,7 @@
:handle-node="handleNode"
:remove-node="removeNode"
:show-tutorial="isSoraNetwork"
:disabled="!connectionAllowance"
/>
</dialog-base>
</template>
Expand Down Expand Up @@ -81,6 +83,10 @@ export default class SelectNodeDialog extends Mixins(NodeErrorMixin, mixins.Load
return this.connection.nodeAddressConnecting;
}

get connectionAllowance(): boolean {
return this.connection.connectionAllowance;
}

get connectedNodeAddress(): string {
if (this.nodeAddressConnecting) return '';

Expand Down
6 changes: 1 addition & 5 deletions src/components/mixins/BridgeHistoryMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ export default class BridgeHistoryMixin<T extends IBridgeTransaction> extends Mi
@action.bridge.updateInternalHistory updateInternalHistory!: FnWithoutArgs;
@action.bridge.updateExternalHistory updateExternalHistory!: (clearHistory?: boolean) => Promise<void>;

isOutgoingType(type: Operation): boolean {
return isOutgoingTransaction({ type } as IBridgeTransaction);
}

async showHistory(id?: string): Promise<void> {
if (!id) {
this.handleBack();
Expand All @@ -38,7 +34,7 @@ export default class BridgeHistoryMixin<T extends IBridgeTransaction> extends Mi
const tx = this.history[id as string];

// to display actual fees in BridgeTransaction
this.setSoraToEvm(this.isOutgoingType(tx.type));
this.setSoraToEvm(isOutgoingTransaction(tx));
await this.setAssetAddress(tx.assetAddress);

this.setHistoryId(tx.id);
Expand Down
143 changes: 99 additions & 44 deletions src/components/mixins/BridgeTransactionMixin.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,108 @@
import { BridgeTxStatus } from '@sora-substrate/util/build/bridgeProxy/consts';
import { BridgeNetworkType } from '@sora-substrate/util/build/bridgeProxy/consts';
import { WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web';
import { Component, Mixins } from 'vue-property-decorator';

import TranslationMixin from '@/components/mixins/TranslationMixin';
import { isOutgoingTransaction } from '@/utils/bridge/common/utils';
import { isUnsignedToPart } from '@/utils/bridge/eth/utils';
import NetworkFormatterMixin from '@/components/mixins/NetworkFormatterMixin';
import { soraExplorerLinks } from '@/utils';

import type { IBridgeTransaction } from '@sora-substrate/util';

const { ETH_BRIDGE_STATES } = WALLET_CONSTS;
import type { BridgeNetworkId } from '@sora-substrate/util/build/bridgeProxy/types';

@Component
export default class BridgeTransactionMixin extends Mixins(TranslationMixin) {
isFailedState(item: Nullable<IBridgeTransaction>): boolean {
if (!(item && item.transactionState)) return false;
// ETH
if (
[ETH_BRIDGE_STATES.EVM_REJECTED as string, ETH_BRIDGE_STATES.SORA_REJECTED as string].includes(
item.transactionState
)
)
return true;
// EVM
if (item.transactionState === BridgeTxStatus.Failed) return true;
// OTHER
return false;
}

isSuccessState(item: Nullable<IBridgeTransaction>): boolean {
if (!item) return false;
// ETH
if (
item.transactionState ===
(isOutgoingTransaction(item) ? ETH_BRIDGE_STATES.EVM_COMMITED : ETH_BRIDGE_STATES.SORA_COMMITED)
)
return true;
// EVM
if (item.transactionState === BridgeTxStatus.Done) return true;
// OTHER
return false;
}

isWaitingForAction(item: Nullable<IBridgeTransaction>): boolean {
if (!item) return false;
// ETH
return item.transactionState === ETH_BRIDGE_STATES.EVM_REJECTED && isUnsignedToPart(item);
}

formatDatetime(item: Nullable<IBridgeTransaction>): string {
return this.formatDate(item?.startTime ?? Date.now());
export default class BridgeTransactionMixin extends Mixins(NetworkFormatterMixin) {
get tx(): Nullable<IBridgeTransaction> {
console.warn('[BridgeTransactionMixin] "tx" computed property is not implemented');
return null;
}

get isOutgoing(): boolean {
return this.isOutgoingTx(this.tx);
}

get isEvmTxType(): boolean {
return (
!!this.externalNetworkType && [BridgeNetworkType.Eth, BridgeNetworkType.Evm].includes(this.externalNetworkType)
);
}

get txSoraAccount(): string {
return this.tx?.from ?? '';
}

get txExternalAccount(): string {
return this.tx?.to ?? '';
}

get txSoraId(): string {
return this.tx?.txId ?? '';
}

get txSoraBlockId(): string {
return this.tx?.blockId ?? '';
}

get txSoraHash(): string {
return this.tx?.hash ?? '';
}

get txInternalHash(): string {
if (!this.isOutgoing) return this.txSoraHash;

return this.txSoraHash || this.txSoraBlockId || this.txSoraId;
}

get txExternalHash(): string {
return this.tx?.externalHash ?? this.txExternalBlockId;
}

get txExternalBlockId(): string {
return this.tx?.externalBlockId ?? '';
}

get externalNetworkType(): Nullable<BridgeNetworkType> {
return this.tx?.externalNetworkType;
}

get externalNetworkId(): Nullable<BridgeNetworkId> {
return this.tx?.externalNetwork;
}

get soraExplorerLinks(): Array<WALLET_CONSTS.ExplorerLink> {
return soraExplorerLinks(this.soraNetwork, this.txSoraId, this.txSoraBlockId);
}

get externalExplorerLinks(): Array<WALLET_CONSTS.ExplorerLink> {
if (!(this.externalNetworkType && this.externalNetworkId)) return [];

return this.getNetworkExplorerLinks(
this.externalNetworkType,
this.externalNetworkId,
this.txExternalHash,
this.txExternalBlockId,
this.EvmLinkType.Transaction
);
}

get internalAccountLinks(): Array<WALLET_CONSTS.ExplorerLink> {
return soraExplorerLinks(this.soraNetwork, this.txSoraAccount, this.txSoraBlockId, true);
}

get externalAccountLinks(): Array<WALLET_CONSTS.ExplorerLink> {
if (!(this.externalNetworkType && this.externalNetworkId)) return [];

return this.getNetworkExplorerLinks(
this.externalNetworkType,
this.externalNetworkId,
this.txExternalAccount,
this.txExternalBlockId,
this.EvmLinkType.Account
);
}

getNetworkText(text: string, networkId?: Nullable<BridgeNetworkId>, approximate = false): string {
const network = networkId ? this.getNetworkName(this.externalNetworkType, networkId) : this.TranslationConsts.Sora;
const approx = approximate ? this.TranslationConsts.Max : '';

return [approx, network, text].filter((item) => !!item).join(' ');
}
}
Loading