From 5103cf4e16da95a4fd12ff1ca14a749bfbb927a7 Mon Sep 17 00:00:00 2001 From: Stefan Popov Date: Tue, 10 Sep 2024 15:13:00 +0200 Subject: [PATCH 1/9] Switch to public wallet (#1519) --- .npmrc | 1 - .yarnrc | 1 - yarn.lock | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 .npmrc delete mode 100644 .yarnrc diff --git a/.npmrc b/.npmrc deleted file mode 100644 index e7bc5dfee..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -@soramitsu:registry=https://nexus.iroha.tech/repository/npm-group/ \ No newline at end of file diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index 8b1562ac6..000000000 --- a/.yarnrc +++ /dev/null @@ -1 +0,0 @@ -"@soramitsu:registry" "https://nexus.iroha.tech/repository/npm-group/" \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 6a8648b3a..3c00e6882 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2984,8 +2984,8 @@ "@soramitsu/soraneo-wallet-web@1.39.12": version "1.39.12" - resolved "https://nexus.iroha.tech/repository/npm-group/@soramitsu/soraneo-wallet-web/-/soraneo-wallet-web-1.39.12.tgz#fbb42e6430f55481bae352ef24175345670cd971" - integrity sha512-mb2FeQhdliPv3/1pF0bXqLoTLYR/4FkJ+iBctZ1OBIwqLdTAirgNjlf0U+SJR2uv8Ri2EDrHO7lWvYUkqsfwMw== + resolved "https://registry.yarnpkg.com/@soramitsu/soraneo-wallet-web/-/soraneo-wallet-web-1.39.12.tgz#2aa17d77fbb6eb227ad4db112d72fa64404ccec8" + integrity sha512-2fV/gcgv26kdyKhhGtmwgeUjKVE/ttwaY4u70j5nQcOuLNMsZWyGEt0srBUge4dU57q78S8i51k9oMOhCTgmWA== dependencies: "@polkadot/vue-identicon" "2.12.1" "@sora-substrate/util" "1.39.12" From 895ce5434790c6c23638bf8aa0b3c5aae0a78c3f Mon Sep 17 00:00:00 2001 From: Nikita Polyakov <53777036+Nikita-Polyakov@users.noreply.github.com> Date: Wed, 11 Sep 2024 02:40:47 +0300 Subject: [PATCH 2/9] update calculateMaxLimit on asset zero price (#1514) Co-authored-by: Stefan Popov --- src/store/bridge/actions.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/store/bridge/actions.ts b/src/store/bridge/actions.ts index cdcae7d6e..f20601b7c 100644 --- a/src/store/bridge/actions.ts +++ b/src/store/bridge/actions.ts @@ -338,30 +338,29 @@ function calculateMaxLimit( referenceAsset: string, usdLimit: CodecString, quote: SwapQuote -): FPNumber { +): FPNumber | null { const outgoingLimitUSD = FPNumber.fromCodecValue(usdLimit); if (outgoingLimitUSD.isZero() || limitAsset === referenceAsset) return outgoingLimitUSD; try { - // We should calculate the asset price in reference asset - // For this purpose is used the reduced asset amount (amount should be greater than any order book tick size) - const assetAmount = FPNumber.ONE; - const multiplier = new FPNumber(1000); - const quoteAmount = assetAmount.div(multiplier); + const quoteAmount = FPNumber.ONE; const { result: { amount }, } = quote(limitAsset, referenceAsset, quoteAmount.toString(), false, [], false); // result amount multiplied by a multiplier to get asset price - const assetPriceUSD = FPNumber.fromCodecValue(amount).mul(multiplier); - if (!assetPriceUSD.isFinity() || assetPriceUSD.isZero()) return FPNumber.ZERO; + const assetPriceUSD = FPNumber.fromCodecValue(amount); + + // zero price means liquidity problem - disable limit + if (!assetPriceUSD.isFinity() || assetPriceUSD.isZero()) return null; return outgoingLimitUSD.div(assetPriceUSD); } catch (error) { console.error(error); - return FPNumber.ZERO; + // disable limit on calculation error + return null; } } @@ -563,7 +562,7 @@ const actions = defineActions({ if (!hasOutgoingLimit) return; const referenceAsset = DAI.address; - const sources = [LiquiditySourceTypes.XYKPool, LiquiditySourceTypes.XSTPool]; + const sources = [LiquiditySourceTypes.XYKPool, LiquiditySourceTypes.XSTPool, LiquiditySourceTypes.OrderBook]; const limitObservable = api.bridgeProxy.getCurrentTransferLimitObservable(); const quoteObservable = api.swap.getSwapQuoteObservable(referenceAsset, limitAsset, sources, DexId.XOR); From b2aa7ffb9029295ff479d056ed15e19023714958 Mon Sep 17 00:00:00 2001 From: Kron1749 <70746258+Kron1749@users.noreply.github.com> Date: Wed, 11 Sep 2024 02:58:01 +0300 Subject: [PATCH 3/9] Feature/pip for widget (#1484) * updated_with_proper_close * updated_for_of * fixed_pip_open * updated_request_window * pip style updates (#1508) * pip style updates * update prop * pip_off_when_before_destroy radius_to_zero_when_pip * naming_updated * updated_with_proper_close * updated_border_radius_class --------- Co-authored-by: Nikita Polyakov <53777036+Nikita-Polyakov@users.noreply.github.com> Co-authored-by: NaghmeMohammadifar <45916098+Naghme98@users.noreply.github.com> Co-authored-by: Stefan Popov --- src/App.vue | 15 ----- src/components/shared/Widget/Base.vue | 94 ++++++++++++++++++++++++++- src/styles/common.scss | 15 +++++ src/views/Swap.vue | 3 +- 4 files changed, 109 insertions(+), 18 deletions(-) diff --git a/src/App.vue b/src/App.vue index bb44981a8..4ba4507a6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -441,21 +441,6 @@ ul ul { } .app { - .el-loading-mask { - background-color: var(--s-color-utility-body); - z-index: $app-loader-layer; - - .el-loading-spinner { - background-image: url('~@/assets/img/pswap-loader.svg'); - height: var(--s-size-medium); - width: var(--s-size-medium); - margin-left: calc(50% - (var(--s-size-medium) / 2)); - > svg { - display: none; - } - } - } - &-main.app-main { &--rewards, &--referral { diff --git a/src/components/shared/Widget/Base.vue b/src/components/shared/Widget/Base.vue index 92fc2ebb8..ea7e7b9df 100644 --- a/src/components/shared/Widget/Base.vue +++ b/src/components/shared/Widget/Base.vue @@ -4,7 +4,7 @@ size="big" primary :shadow="shadow" - :class="['base-widget', { delimeter, full, flat }]" + :class="['base-widget', { delimeter, full, flat, pip: pipOpened }]" v-loading="loading" > -
@@ -79,6 +84,10 @@ export default class BaseWidget extends Vue { * Widget has a loading state */ @Prop({ default: false, type: Boolean }) readonly loading!: boolean; + /** + * Widget "Picture in picture" mode is disabled + */ + @Prop({ default: false, type: Boolean }) readonly pipDisabled!: boolean; @Prop({ default: () => {}, type: Function }) readonly onResize!: (id: string, size: Size) => void; @@ -93,6 +102,9 @@ export default class BaseWidget extends Vue { height: 0, }; + private pipWindow: Window | null = null; + private pipOpened = false; + public capitalize = capitalize; get hasHeader(): boolean { @@ -107,6 +119,67 @@ export default class BaseWidget extends Vue { return this.flat ? 'never' : 'always'; } + get isPipAvailable() { + if (this.pipDisabled) return false; + + return 'documentPictureInPicture' in window && !this.pipOpened; + } + + async openPip() { + if (!this.isPipAvailable) return; + + try { + const pipWindow = await (window as any).documentPictureInPicture.requestWindow({ + width: this.$el.clientWidth, + height: this.$el.clientHeight, + }); + + this.pipOpened = true; + this.pipWindow = pipWindow; + + // Access the root element of the Vue component + const widgetElement = this.$el as HTMLElement; + const originalParent = widgetElement.parentNode as HTMLElement; + + // STYLES + const allStyles = Array.from(document.styleSheets) + .map((styleSheet) => + Array.from(styleSheet.cssRules) + .map((rule) => rule.cssText) + .join('\n') + ) + .join('\n'); + // Create a new style element in the Picture-in-Picture window + const style = pipWindow.document.createElement('style'); + style.innerHTML = allStyles; + // Append style element to the Picture-in-Picture window's head + pipWindow.document.head.appendChild(style); + + // THEME + // Get the element from the Picture-in-Picture window's document + const htmlElement = pipWindow.document.documentElement; + // Get the element from the original document + const originalHtmlElement = document.documentElement; + // Copy attributes from the original element to the element in the Picture-in-Picture window's document + for (const attribute of originalHtmlElement.attributes) { + htmlElement.setAttribute(attribute.nodeName, attribute.nodeValue); + } + // Move the Vue component to the Picture-in-Picture window + pipWindow.document.body.appendChild(widgetElement); + + // Event listener when the PiP window is closed + pipWindow.addEventListener('pagehide', () => { + // Move the element back to the original document when PiP is closed + this.$nextTick(() => { + this.pipOpened = false; + originalParent.appendChild(widgetElement); + }); + }); + } catch (error) { + console.error('Error during PiP handling:', error); + } + } + mounted(): void { this.createContentObserver(); this.updateSize(this.getWidgetSize()); // initial @@ -114,6 +187,13 @@ export default class BaseWidget extends Vue { beforeDestroy(): void { this.destroyContentObserver(); + if (this.pipOpened && this.pipWindow) { + this.pipWindow.close(); + this.pipOpened = false; + this.pipWindow = null; + } else { + console.info('PiP was never opened or already closed.'); + } } private createContentObserver(): void { @@ -208,6 +288,12 @@ $left: $inner-spacing-medium; } } + &.pip { + &.s-border-radius-small { + border-radius: unset; + } + } + &-block { display: flex; align-items: center; @@ -251,6 +337,10 @@ $left: $inner-spacing-medium; } } + &-pip { + order: 2; + } + &-content { display: flex; flex-flow: column nowrap; diff --git a/src/styles/common.scss b/src/styles/common.scss index 380a49e41..f1b6d4d50 100644 --- a/src/styles/common.scss +++ b/src/styles/common.scss @@ -15,6 +15,21 @@ $country-emoji-font: 'Twemoji Country Flags'; @include generic-input-lines; +.el-loading-mask { + background-color: var(--s-color-utility-body); + z-index: $app-loader-layer; + + .el-loading-spinner { + background-image: url('~@/assets/img/pswap-loader.svg'); + height: var(--s-size-medium); + width: var(--s-size-medium); + margin-left: calc(50% - (var(--s-size-medium) / 2)); + > svg { + display: none; + } + } +} + .flag-emodji { &, &-input input.el-input__inner { diff --git a/src/views/Swap.vue b/src/views/Swap.vue index da666fb87..50d62685c 100644 --- a/src/views/Swap.vue +++ b/src/views/Swap.vue @@ -10,7 +10,7 @@ v-model="widgets" >